Merge pull request #73113 from aanderse/httpd-vhost
nixos/httpd: support overridable virtual hosts
This commit is contained in:
commit
4d2dd15546
@ -11,50 +11,46 @@
|
||||
<programlisting>
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
[ { hostName = "example.org";
|
||||
documentRoot = "/webroot";
|
||||
{ "blog.example.org" = {
|
||||
documentRoot = "/webroot/blog.example.org";
|
||||
adminAddr = "alice@example.org";
|
||||
enableUserDir = true;
|
||||
}
|
||||
{ hostName = "example.org";
|
||||
documentRoot = "/webroot";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enablePHP = true;
|
||||
};
|
||||
"wiki.example.org" = {
|
||||
documentRoot = "/webroot/wiki.example.org";
|
||||
adminAddr = "alice@example.org";
|
||||
enableUserDir = true;
|
||||
enableSSL = true;
|
||||
sslServerCert = "/root/ssl-example-org.crt";
|
||||
sslServerKey = "/root/ssl-example-org.key";
|
||||
}
|
||||
];
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
enablePHP = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
</programlisting>
|
||||
It defines two virtual hosts with nearly identical configuration; the only
|
||||
difference is that the second one has SSL enabled. To prevent this
|
||||
difference is the document root directories. To prevent this
|
||||
duplication, we can use a <literal>let</literal>:
|
||||
<programlisting>
|
||||
let
|
||||
exampleOrgCommon =
|
||||
{ hostName = "example.org";
|
||||
documentRoot = "/webroot";
|
||||
adminAddr = "alice@example.org";
|
||||
enableUserDir = true;
|
||||
commonConfig =
|
||||
{ adminAddr = "alice@example.org";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
[ exampleOrgCommon
|
||||
(exampleOrgCommon // {
|
||||
enableSSL = true;
|
||||
sslServerCert = "/root/ssl-example-org.crt";
|
||||
sslServerKey = "/root/ssl-example-org.key";
|
||||
})
|
||||
];
|
||||
{ "blog.example.org" = (commonConfig // { documentRoot = "/webroot/blog.example.org"; });
|
||||
"wiki.example.org" = (commonConfig // { documentRoot = "/webroot/wiki.example.com"; });
|
||||
};
|
||||
}
|
||||
</programlisting>
|
||||
The <literal>let exampleOrgCommon = <replaceable>...</replaceable></literal>
|
||||
defines a variable named <literal>exampleOrgCommon</literal>. The
|
||||
The <literal>let commonConfig = <replaceable>...</replaceable></literal>
|
||||
defines a variable named <literal>commonConfig</literal>. The
|
||||
<literal>//</literal> operator merges two attribute sets, so the
|
||||
configuration of the second virtual host is the set
|
||||
<literal>exampleOrgCommon</literal> extended with the SSL options.
|
||||
<literal>commonConfig</literal> extended with the document root option.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -63,13 +59,13 @@ in
|
||||
<programlisting>
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
let exampleOrgCommon = <replaceable>...</replaceable>; in
|
||||
[ exampleOrgCommon
|
||||
(exampleOrgCommon // { <replaceable>...</replaceable> })
|
||||
];
|
||||
let commonConfig = <replaceable>...</replaceable>; in
|
||||
{ "blog.example.org" = (commonConfig // { <replaceable>...</replaceable> })
|
||||
"wiki.example.org" = (commonConfig // { <replaceable>...</replaceable> })
|
||||
};
|
||||
}
|
||||
</programlisting>
|
||||
but not <literal>{ let exampleOrgCommon = <replaceable>...</replaceable>; in
|
||||
but not <literal>{ let commonConfig = <replaceable>...</replaceable>; in
|
||||
<replaceable>...</replaceable>; }</literal> since attributes (as opposed to
|
||||
attribute values) are not expressions.
|
||||
</para>
|
||||
@ -77,80 +73,29 @@ in
|
||||
<para>
|
||||
<emphasis>Functions</emphasis> provide another method of abstraction. For
|
||||
instance, suppose that we want to generate lots of different virtual hosts,
|
||||
all with identical configuration except for the host name. This can be done
|
||||
all with identical configuration except for the document root. This can be done
|
||||
as follows:
|
||||
<programlisting>
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
let
|
||||
makeVirtualHost = name:
|
||||
{ hostName = name;
|
||||
documentRoot = "/webroot";
|
||||
makeVirtualHost = webroot:
|
||||
{ documentRoot = webroot;
|
||||
adminAddr = "alice@example.org";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
};
|
||||
in
|
||||
[ (makeVirtualHost "example.org")
|
||||
(makeVirtualHost "example.com")
|
||||
(makeVirtualHost "example.gov")
|
||||
(makeVirtualHost "example.nl")
|
||||
];
|
||||
{ "example.org" = (makeVirtualHost "/webroot/example.org");
|
||||
"example.com" = (makeVirtualHost "/webroot/example.com");
|
||||
"example.gov" = (makeVirtualHost "/webroot/example.gov");
|
||||
"example.nl" = (makeVirtualHost "/webroot/example.nl");
|
||||
};
|
||||
}
|
||||
</programlisting>
|
||||
Here, <varname>makeVirtualHost</varname> is a function that takes a single
|
||||
argument <literal>name</literal> and returns the configuration for a virtual
|
||||
argument <literal>webroot</literal> and returns the configuration for a virtual
|
||||
host. That function is then called for several names to produce the list of
|
||||
virtual host configurations.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
We can further improve on this by using the function <varname>map</varname>,
|
||||
which applies another function to every element in a list:
|
||||
<programlisting>
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
let
|
||||
makeVirtualHost = <replaceable>...</replaceable>;
|
||||
in map makeVirtualHost
|
||||
[ "example.org" "example.com" "example.gov" "example.nl" ];
|
||||
}
|
||||
</programlisting>
|
||||
(The function <literal>map</literal> is called a <emphasis>higher-order
|
||||
function</emphasis> because it takes another function as an argument.)
|
||||
</para>
|
||||
|
||||
<para>
|
||||
What if you need more than one argument, for instance, if we want to use a
|
||||
different <literal>documentRoot</literal> for each virtual host? Then we can
|
||||
make <varname>makeVirtualHost</varname> a function that takes a
|
||||
<emphasis>set</emphasis> as its argument, like this:
|
||||
<programlisting>
|
||||
{
|
||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||
let
|
||||
makeVirtualHost = { name, root }:
|
||||
{ hostName = name;
|
||||
documentRoot = root;
|
||||
adminAddr = "alice@example.org";
|
||||
};
|
||||
in map makeVirtualHost
|
||||
[ { name = "example.org"; root = "/sites/example.org"; }
|
||||
{ name = "example.com"; root = "/sites/example.com"; }
|
||||
{ name = "example.gov"; root = "/sites/example.gov"; }
|
||||
{ name = "example.nl"; root = "/sites/example.nl"; }
|
||||
];
|
||||
}
|
||||
</programlisting>
|
||||
But in this case (where every root is a subdirectory of
|
||||
<filename>/sites</filename> named after the virtual host), it would have been
|
||||
shorter to define <varname>makeVirtualHost</varname> as
|
||||
<programlisting>
|
||||
makeVirtualHost = name:
|
||||
{ hostName = name;
|
||||
documentRoot = "/sites/${name}";
|
||||
adminAddr = "alice@example.org";
|
||||
};
|
||||
</programlisting>
|
||||
Here, the construct <literal>${<replaceable>...</replaceable>}</literal>
|
||||
allows the result of an expression to be spliced into a string.
|
||||
</para>
|
||||
</section>
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
{ <xref linkend="opt-services.httpd.enable"/> = true;
|
||||
<xref linkend="opt-services.httpd.adminAddr"/> = "alice@example.org";
|
||||
<xref linkend="opt-services.httpd.documentRoot"/> = "/webroot";
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.localhost.documentRoot</link> = "/webroot";
|
||||
}
|
||||
</programlisting>
|
||||
defines a configuration with three option definitions that together enable
|
||||
@ -50,7 +50,11 @@
|
||||
httpd = {
|
||||
enable = true;
|
||||
adminAddr = "alice@example.org";
|
||||
documentRoot = "/webroot";
|
||||
virtualHosts = {
|
||||
localhost = {
|
||||
documentRoot = "/webroot";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -327,6 +327,28 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
|
||||
module.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
The httpd module no longer provides options to support serving web content without defining a virtual host. As a
|
||||
result of this the <link linkend="opt-services.httpd.logPerVirtualHost">services.httpd.logPerVirtualHost</link>
|
||||
option now defaults to <literal>true</literal> instead of <literal>false</literal>. Please update your
|
||||
configuration to make use of <link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts</link>.
|
||||
</para>
|
||||
<para>
|
||||
The <link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name></link>
|
||||
option has changed type from a list of submodules to an attribute set of submodules, better matching
|
||||
<link linkend="opt-services.nginx.virtualHosts">services.nginx.virtualHosts.<name></link>.
|
||||
</para>
|
||||
<para>
|
||||
This change comes with the addition of the following options which mimic the functionality of their <literal>nginx</literal> counterparts:
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.addSSL</link>,
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.forceSSL</link>,
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.onlySSL</link>,
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.enableACME</link>,
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.acmeRoot</link>, and
|
||||
<link linkend="opt-services.httpd.virtualHosts">services.httpd.virtualHosts.<name>.useACMEHost</link>.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</section>
|
||||
|
||||
|
@ -8,6 +8,7 @@ let
|
||||
|
||||
nagiosState = "/var/lib/nagios";
|
||||
nagiosLogDir = "/var/log/nagios";
|
||||
urlPath = "/nagios";
|
||||
|
||||
nagiosObjectDefs = cfg.objectDefs;
|
||||
|
||||
@ -49,12 +50,12 @@ let
|
||||
''
|
||||
main_config_file=${cfg.mainConfigFile}
|
||||
use_authentication=0
|
||||
url_html_path=${cfg.urlPath}
|
||||
url_html_path=${urlPath}
|
||||
'';
|
||||
|
||||
extraHttpdConfig =
|
||||
''
|
||||
ScriptAlias ${cfg.urlPath}/cgi-bin ${pkgs.nagios}/sbin
|
||||
ScriptAlias ${urlPath}/cgi-bin ${pkgs.nagios}/sbin
|
||||
|
||||
<Directory "${pkgs.nagios}/sbin">
|
||||
Options ExecCGI
|
||||
@ -62,7 +63,7 @@ let
|
||||
SetEnv NAGIOS_CGI_CONFIG ${cfg.cgiConfigFile}
|
||||
</Directory>
|
||||
|
||||
Alias ${cfg.urlPath} ${pkgs.nagios}/share
|
||||
Alias ${urlPath} ${pkgs.nagios}/share
|
||||
|
||||
<Directory "${pkgs.nagios}/share">
|
||||
Options None
|
||||
@ -72,6 +73,10 @@ let
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "nagios" "urlPath" ] "The urlPath option has been removed as it is hard coded to /nagios in the nagios package.")
|
||||
];
|
||||
|
||||
options = {
|
||||
services.nagios = {
|
||||
enable = mkOption {
|
||||
@ -128,13 +133,20 @@ in
|
||||
";
|
||||
};
|
||||
|
||||
urlPath = mkOption {
|
||||
default = "/nagios";
|
||||
description = "
|
||||
The URL path under which the Nagios web interface appears.
|
||||
That is, you can access the Nagios web interface through
|
||||
<literal>http://<replaceable>server</replaceable>/<replaceable>urlPath</replaceable></literal>.
|
||||
";
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{ hostName = "example.org";
|
||||
adminAddr = "webmaster@example.org";
|
||||
enableSSL = true;
|
||||
sslServerCert = "/var/lib/acme/example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/example.org/key.pem";
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
|
||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -182,6 +194,8 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
services.httpd.extraConfig = optionalString cfg.enableWebInterface extraHttpdConfig;
|
||||
services.httpd.virtualHosts = optionalAttrs cfg.enableWebInterface {
|
||||
${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost { extraConfig = extraHttpdConfig; } ];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
let
|
||||
|
||||
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
|
||||
inherit (lib) mapAttrs optional optionalString types;
|
||||
inherit (lib) literalExample mapAttrs optional optionalString types;
|
||||
|
||||
cfg = config.services.limesurvey;
|
||||
fpm = config.services.phpfpm.pools.limesurvey;
|
||||
@ -100,19 +100,15 @@ in
|
||||
};
|
||||
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule ({
|
||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
};
|
||||
});
|
||||
example = {
|
||||
hostName = "survey.example.org";
|
||||
enableSSL = true;
|
||||
adminAddr = "webmaster@example.org";
|
||||
sslServerCert = "/var/lib/acme/survey.example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/survey.example.org/key.pem";
|
||||
};
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{
|
||||
hostName = "survey.example.org";
|
||||
adminAddr = "webmaster@example.org";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||
@ -184,7 +180,7 @@ in
|
||||
config = {
|
||||
tempdir = "${stateDir}/tmp";
|
||||
uploaddir = "${stateDir}/upload";
|
||||
force_ssl = mkIf cfg.virtualHost.enableSSL "on";
|
||||
force_ssl = mkIf (cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL) "on";
|
||||
config.defaultlang = "en";
|
||||
};
|
||||
};
|
||||
@ -215,38 +211,36 @@ in
|
||||
enable = true;
|
||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||
extraModules = [ "proxy_fcgi" ];
|
||||
virtualHosts = [ (mkMerge [
|
||||
cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg}/share/limesurvey";
|
||||
extraConfig = ''
|
||||
Alias "/tmp" "${stateDir}/tmp"
|
||||
<Directory "${stateDir}">
|
||||
AllowOverride all
|
||||
Require all granted
|
||||
Options -Indexes +FollowSymlinks
|
||||
</Directory>
|
||||
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg}/share/limesurvey";
|
||||
extraConfig = ''
|
||||
Alias "/tmp" "${stateDir}/tmp"
|
||||
<Directory "${stateDir}">
|
||||
AllowOverride all
|
||||
Require all granted
|
||||
Options -Indexes +FollowSymlinks
|
||||
</Directory>
|
||||
|
||||
Alias "/upload" "${stateDir}/upload"
|
||||
<Directory "${stateDir}/upload">
|
||||
AllowOverride all
|
||||
Require all granted
|
||||
Options -Indexes
|
||||
</Directory>
|
||||
Alias "/upload" "${stateDir}/upload"
|
||||
<Directory "${stateDir}/upload">
|
||||
AllowOverride all
|
||||
Require all granted
|
||||
Options -Indexes
|
||||
</Directory>
|
||||
|
||||
<Directory "${pkg}/share/limesurvey">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
<Directory "${pkg}/share/limesurvey">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
|
||||
AllowOverride all
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
}
|
||||
]) ];
|
||||
AllowOverride all
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
} ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
|
@ -64,7 +64,7 @@ let
|
||||
$wgScriptPath = "";
|
||||
|
||||
## The protocol and server name to use in fully-qualified URLs
|
||||
$wgServer = "${if cfg.virtualHost.enableSSL then "https" else "http"}://${cfg.virtualHost.hostName}";
|
||||
$wgServer = "${if cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL then "https" else "http"}://${cfg.virtualHost.hostName}";
|
||||
|
||||
## The URL path to static resources (images, scripts, etc.)
|
||||
$wgResourceBasePath = $wgScriptPath;
|
||||
@ -290,19 +290,13 @@ in
|
||||
};
|
||||
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule ({
|
||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
};
|
||||
});
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{
|
||||
hostName = "mediawiki.example.org";
|
||||
enableSSL = true;
|
||||
adminAddr = "webmaster@example.org";
|
||||
sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@ -389,31 +383,28 @@ in
|
||||
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||
extraModules = [ "proxy_fcgi" ];
|
||||
virtualHosts = [ (mkMerge [
|
||||
cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg}/share/mediawiki";
|
||||
extraConfig = ''
|
||||
<Directory "${pkg}/share/mediawiki">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg}/share/mediawiki";
|
||||
extraConfig = ''
|
||||
<Directory "${pkg}/share/mediawiki">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
|
||||
Require all granted
|
||||
DirectoryIndex index.php
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'' + optionalString (cfg.uploadsDir != null) ''
|
||||
Alias "/images" "${cfg.uploadsDir}"
|
||||
<Directory "${cfg.uploadsDir}">
|
||||
Require all granted
|
||||
</Directory>
|
||||
'';
|
||||
}
|
||||
]) ];
|
||||
Require all granted
|
||||
DirectoryIndex index.php
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'' + optionalString (cfg.uploadsDir != null) ''
|
||||
Alias "/images" "${cfg.uploadsDir}"
|
||||
<Directory "${cfg.uploadsDir}">
|
||||
Require all granted
|
||||
</Directory>
|
||||
'';
|
||||
} ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
|
@ -32,7 +32,7 @@ let
|
||||
'dbcollation' => 'utf8mb4_unicode_ci',
|
||||
);
|
||||
|
||||
$CFG->wwwroot = '${if cfg.virtualHost.enableSSL then "https" else "http"}://${cfg.virtualHost.hostName}';
|
||||
$CFG->wwwroot = '${if cfg.virtualHost.addSSL || cfg.virtualHost.forceSSL || cfg.virtualHost.onlySSL then "https" else "http"}://${cfg.virtualHost.hostName}';
|
||||
$CFG->dataroot = '${stateDir}';
|
||||
$CFG->admin = 'admin';
|
||||
|
||||
@ -140,19 +140,15 @@ in
|
||||
};
|
||||
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule ({
|
||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
};
|
||||
});
|
||||
example = {
|
||||
hostName = "moodle.example.org";
|
||||
enableSSL = true;
|
||||
adminAddr = "webmaster@example.org";
|
||||
sslServerCert = "/var/lib/acme/moodle.example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/moodle.example.org/key.pem";
|
||||
};
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{
|
||||
hostName = "moodle.example.org";
|
||||
adminAddr = "webmaster@example.org";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
|
||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||
@ -241,22 +237,20 @@ in
|
||||
enable = true;
|
||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||
extraModules = [ "proxy_fcgi" ];
|
||||
virtualHosts = [ (mkMerge [
|
||||
cfg.virtualHost {
|
||||
documentRoot = mkForce "${cfg.package}/share/moodle";
|
||||
extraConfig = ''
|
||||
<Directory "${cfg.package}/share/moodle">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
}
|
||||
]) ];
|
||||
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||
documentRoot = mkForce "${cfg.package}/share/moodle";
|
||||
extraConfig = ''
|
||||
<Directory "${cfg.package}/share/moodle">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
} ];
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
|
@ -3,7 +3,7 @@
|
||||
let
|
||||
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
|
||||
inherit (lib) any attrValues concatMapStringsSep flatten literalExample;
|
||||
inherit (lib) mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString;
|
||||
inherit (lib) mapAttrs mapAttrs' mapAttrsToList nameValuePair optional optionalAttrs optionalString;
|
||||
|
||||
eachSite = config.services.wordpress;
|
||||
user = "wordpress";
|
||||
@ -209,18 +209,12 @@ let
|
||||
};
|
||||
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule ({
|
||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
};
|
||||
});
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{
|
||||
enableSSL = true;
|
||||
adminAddr = "webmaster@example.org";
|
||||
sslServerCert = "/var/lib/acme/wordpress.example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/wordpress.example.org/key.pem";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
@ -304,41 +298,37 @@ in
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
extraModules = [ "proxy_fcgi" ];
|
||||
virtualHosts = mapAttrsToList (hostName: cfg:
|
||||
(mkMerge [
|
||||
cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg hostName cfg}/share/wordpress";
|
||||
extraConfig = ''
|
||||
<Directory "${pkg hostName cfg}/share/wordpress">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
virtualHosts = mapAttrs (hostName: cfg: mkMerge [ cfg.virtualHost {
|
||||
documentRoot = mkForce "${pkg hostName cfg}/share/wordpress";
|
||||
extraConfig = ''
|
||||
<Directory "${pkg hostName cfg}/share/wordpress">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
|
||||
# standard wordpress .htaccess contents
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
# standard wordpress .htaccess contents
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . /index.php [L]
|
||||
</IfModule>
|
||||
|
||||
DirectoryIndex index.php
|
||||
Require all granted
|
||||
Options +FollowSymLinks
|
||||
</Directory>
|
||||
DirectoryIndex index.php
|
||||
Require all granted
|
||||
Options +FollowSymLinks
|
||||
</Directory>
|
||||
|
||||
# https://wordpress.org/support/article/hardening-wordpress/#securing-wp-config-php
|
||||
<Files wp-config.php>
|
||||
Require all denied
|
||||
</Files>
|
||||
'';
|
||||
}
|
||||
])
|
||||
) eachSite;
|
||||
# https://wordpress.org/support/article/hardening-wordpress/#securing-wp-config-php
|
||||
<Files wp-config.php>
|
||||
Require all denied
|
||||
</Files>
|
||||
'';
|
||||
} ]) eachSite;
|
||||
};
|
||||
|
||||
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
|
||||
|
@ -113,19 +113,15 @@ in
|
||||
};
|
||||
|
||||
virtualHost = mkOption {
|
||||
type = types.submodule ({
|
||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
};
|
||||
});
|
||||
example = {
|
||||
hostName = "zabbix.example.org";
|
||||
enableSSL = true;
|
||||
adminAddr = "webmaster@example.org";
|
||||
sslServerCert = "/var/lib/acme/zabbix.example.org/full.pem";
|
||||
sslServerKey = "/var/lib/acme/zabbix.example.org/key.pem";
|
||||
};
|
||||
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||
example = literalExample ''
|
||||
{
|
||||
hostName = "zabbix.example.org";
|
||||
adminAddr = "webmaster@example.org";
|
||||
forceSSL = true;
|
||||
enableACME = true;
|
||||
}
|
||||
'';
|
||||
description = ''
|
||||
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||
@ -190,23 +186,21 @@ in
|
||||
enable = true;
|
||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||
extraModules = [ "proxy_fcgi" ];
|
||||
virtualHosts = [ (mkMerge [
|
||||
cfg.virtualHost {
|
||||
documentRoot = mkForce "${cfg.package}/share/zabbix";
|
||||
extraConfig = ''
|
||||
<Directory "${cfg.package}/share/zabbix">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
AllowOverride all
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
}
|
||||
]) ];
|
||||
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||
documentRoot = mkForce "${cfg.package}/share/zabbix";
|
||||
extraConfig = ''
|
||||
<Directory "${cfg.package}/share/zabbix">
|
||||
<FilesMatch "\.php$">
|
||||
<If "-f %{REQUEST_FILENAME}">
|
||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||
</If>
|
||||
</FilesMatch>
|
||||
AllowOverride all
|
||||
Options -Indexes
|
||||
DirectoryIndex index.php
|
||||
</Directory>
|
||||
'';
|
||||
} ];
|
||||
};
|
||||
|
||||
users.users.${user} = mapAttrs (name: mkDefault) {
|
||||
|
@ -18,22 +18,20 @@ let
|
||||
|
||||
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
|
||||
|
||||
defaultListen = cfg: if cfg.enableSSL
|
||||
then [{ip = "*"; port = 443;}]
|
||||
else [{ip = "*"; port = 80;}];
|
||||
vhosts = attrValues mainCfg.virtualHosts;
|
||||
|
||||
getListen = cfg:
|
||||
if cfg.listen == []
|
||||
then defaultListen cfg
|
||||
else cfg.listen;
|
||||
mkListenInfo = hostOpts:
|
||||
if hostOpts.listen != [] then hostOpts.listen
|
||||
else (
|
||||
optional (hostOpts.onlySSL || hostOpts.addSSL || hostOpts.forceSSL) { ip = "*"; port = 443; ssl = true; } ++
|
||||
optional (!hostOpts.onlySSL) { ip = "*"; port = 80; ssl = false; }
|
||||
);
|
||||
|
||||
listenToString = l: "${l.ip}:${toString l.port}";
|
||||
listenInfo = unique (concatMap mkListenInfo vhosts);
|
||||
|
||||
allHosts = [mainCfg] ++ mainCfg.virtualHosts;
|
||||
enableSSL = any (listen: listen.ssl) listenInfo;
|
||||
|
||||
enableSSL = any (vhost: vhost.enableSSL) allHosts;
|
||||
|
||||
enableUserDir = any (vhost: vhost.enableUserDir) allHosts;
|
||||
enableUserDir = any (vhost: vhost.enableUserDir) vhosts;
|
||||
|
||||
# NOTE: generally speaking order of modules is very important
|
||||
modules =
|
||||
@ -115,122 +113,137 @@ let
|
||||
</IfModule>
|
||||
'';
|
||||
|
||||
mkVHostConf = hostOpts:
|
||||
let
|
||||
adminAddr = if hostOpts.adminAddr != null then hostOpts.adminAddr else mainCfg.adminAddr;
|
||||
listen = filter (listen: !listen.ssl) (mkListenInfo hostOpts);
|
||||
listenSSL = filter (listen: listen.ssl) (mkListenInfo hostOpts);
|
||||
|
||||
perServerConf = isMainServer: cfg: let
|
||||
useACME = hostOpts.enableACME || hostOpts.useACMEHost != null;
|
||||
sslCertDir =
|
||||
if hostOpts.enableACME then config.security.acme.certs.${hostOpts.hostName}.directory
|
||||
else if hostOpts.useACMEHost != null then config.security.acme.certs.${hostOpts.useACMEHost}.directory
|
||||
else abort "This case should never happen.";
|
||||
|
||||
# Canonical name must not include a trailing slash.
|
||||
canonicalNames =
|
||||
let defaultPort = (head (defaultListen cfg)).port; in
|
||||
map (port:
|
||||
(if cfg.enableSSL then "https" else "http") + "://" +
|
||||
cfg.hostName +
|
||||
(if port != defaultPort then ":${toString port}" else "")
|
||||
) (map (x: x.port) (getListen cfg));
|
||||
sslServerCert = if useACME then "${sslCertDir}/full.pem" else hostOpts.sslServerCert;
|
||||
sslServerKey = if useACME then "${sslCertDir}/key.pem" else hostOpts.sslServerKey;
|
||||
sslServerChain = if useACME then "${sslCertDir}/fullchain.pem" else hostOpts.sslServerChain;
|
||||
|
||||
maybeDocumentRoot = fold (svc: acc:
|
||||
if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
|
||||
) null ([ cfg ]);
|
||||
acmeChallenge = optionalString useACME ''
|
||||
Alias /.well-known/acme-challenge/ "${hostOpts.acmeRoot}/.well-known/acme-challenge/"
|
||||
<Directory "${hostOpts.acmeRoot}">
|
||||
AllowOverride None
|
||||
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
|
||||
Require method GET POST OPTIONS
|
||||
Require all granted
|
||||
</Directory>
|
||||
'';
|
||||
in
|
||||
optionalString (listen != []) ''
|
||||
<VirtualHost ${concatMapStringsSep " " (listen: "${listen.ip}:${toString listen.port}") listen}>
|
||||
ServerName ${hostOpts.hostName}
|
||||
${concatMapStrings (alias: "ServerAlias ${alias}\n") hostOpts.serverAliases}
|
||||
ServerAdmin ${adminAddr}
|
||||
<IfModule mod_ssl.c>
|
||||
SSLEngine off
|
||||
</IfModule>
|
||||
${acmeChallenge}
|
||||
${if hostOpts.forceSSL then ''
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge [NC]
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
|
||||
</IfModule>
|
||||
'' else mkVHostCommonConf hostOpts}
|
||||
</VirtualHost>
|
||||
'' +
|
||||
optionalString (listenSSL != []) ''
|
||||
<VirtualHost ${concatMapStringsSep " " (listen: "${listen.ip}:${toString listen.port}") listenSSL}>
|
||||
ServerName ${hostOpts.hostName}
|
||||
${concatMapStrings (alias: "ServerAlias ${alias}\n") hostOpts.serverAliases}
|
||||
ServerAdmin ${adminAddr}
|
||||
SSLEngine on
|
||||
SSLCertificateFile ${sslServerCert}
|
||||
SSLCertificateKeyFile ${sslServerKey}
|
||||
${optionalString (sslServerChain != null) "SSLCertificateChainFile ${sslServerChain}"}
|
||||
${acmeChallenge}
|
||||
${mkVHostCommonConf hostOpts}
|
||||
</VirtualHost>
|
||||
''
|
||||
;
|
||||
|
||||
documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
|
||||
pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
|
||||
mkVHostCommonConf = hostOpts:
|
||||
let
|
||||
documentRoot = if hostOpts.documentRoot != null
|
||||
then hostOpts.documentRoot
|
||||
else pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out"
|
||||
;
|
||||
in
|
||||
''
|
||||
${optionalString mainCfg.logPerVirtualHost ''
|
||||
ErrorLog ${mainCfg.logDir}/error-${hostOpts.hostName}.log
|
||||
CustomLog ${mainCfg.logDir}/access-${hostOpts.hostName}.log ${hostOpts.logFormat}
|
||||
''}
|
||||
|
||||
documentRootConf = ''
|
||||
DocumentRoot "${documentRoot}"
|
||||
${optionalString (hostOpts.robotsEntries != "") ''
|
||||
Alias /robots.txt ${pkgs.writeText "robots.txt" hostOpts.robotsEntries}
|
||||
''}
|
||||
|
||||
<Directory "${documentRoot}">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
${allGranted}
|
||||
</Directory>
|
||||
'';
|
||||
DocumentRoot "${documentRoot}"
|
||||
|
||||
# If this is a vhost, the include the entries for the main server as well.
|
||||
robotsTxt = concatStringsSep "\n" (filter (x: x != "") ([ cfg.robotsEntries ] ++ lib.optional (!isMainServer) mainCfg.robotsEntries));
|
||||
<Directory "${documentRoot}">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
${allGranted}
|
||||
</Directory>
|
||||
|
||||
in ''
|
||||
${concatStringsSep "\n" (map (n: "ServerName ${n}") canonicalNames)}
|
||||
${optionalString hostOpts.enableUserDir ''
|
||||
UserDir public_html
|
||||
UserDir disabled root
|
||||
<Directory "/home/*/public_html">
|
||||
AllowOverride FileInfo AuthConfig Limit Indexes
|
||||
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
|
||||
<Limit GET POST OPTIONS>
|
||||
Require all granted
|
||||
</Limit>
|
||||
<LimitExcept GET POST OPTIONS>
|
||||
Require all denied
|
||||
</LimitExcept>
|
||||
</Directory>
|
||||
''}
|
||||
|
||||
${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases}
|
||||
${optionalString (hostOpts.globalRedirect != null && hostOpts.globalRedirect != "") ''
|
||||
RedirectPermanent / ${hostOpts.globalRedirect}
|
||||
''}
|
||||
|
||||
${if cfg.sslServerCert != null then ''
|
||||
SSLCertificateFile ${cfg.sslServerCert}
|
||||
SSLCertificateKeyFile ${cfg.sslServerKey}
|
||||
${if cfg.sslServerChain != null then ''
|
||||
SSLCertificateChainFile ${cfg.sslServerChain}
|
||||
'' else ""}
|
||||
'' else ""}
|
||||
${
|
||||
let makeFileConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.file}
|
||||
'';
|
||||
in concatMapStrings makeFileConf hostOpts.servedFiles
|
||||
}
|
||||
${
|
||||
let makeDirConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.dir}/
|
||||
<Directory ${elem.dir}>
|
||||
Options +Indexes
|
||||
${allGranted}
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
in concatMapStrings makeDirConf hostOpts.servedDirs
|
||||
}
|
||||
|
||||
${if cfg.enableSSL then ''
|
||||
SSLEngine on
|
||||
'' else if enableSSL then /* i.e., SSL is enabled for some host, but not this one */
|
||||
''
|
||||
SSLEngine off
|
||||
'' else ""}
|
||||
|
||||
${if isMainServer || cfg.adminAddr != null then ''
|
||||
ServerAdmin ${cfg.adminAddr}
|
||||
'' else ""}
|
||||
|
||||
${if !isMainServer && mainCfg.logPerVirtualHost then ''
|
||||
ErrorLog ${mainCfg.logDir}/error-${cfg.hostName}.log
|
||||
CustomLog ${mainCfg.logDir}/access-${cfg.hostName}.log ${cfg.logFormat}
|
||||
'' else ""}
|
||||
|
||||
${optionalString (robotsTxt != "") ''
|
||||
Alias /robots.txt ${pkgs.writeText "robots.txt" robotsTxt}
|
||||
''}
|
||||
|
||||
${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
|
||||
|
||||
${if cfg.enableUserDir then ''
|
||||
|
||||
UserDir public_html
|
||||
UserDir disabled root
|
||||
|
||||
<Directory "/home/*/public_html">
|
||||
AllowOverride FileInfo AuthConfig Limit Indexes
|
||||
Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
|
||||
<Limit GET POST OPTIONS>
|
||||
${allGranted}
|
||||
</Limit>
|
||||
<LimitExcept GET POST OPTIONS>
|
||||
${allDenied}
|
||||
</LimitExcept>
|
||||
</Directory>
|
||||
|
||||
'' else ""}
|
||||
|
||||
${if cfg.globalRedirect != null && cfg.globalRedirect != "" then ''
|
||||
RedirectPermanent / ${cfg.globalRedirect}
|
||||
'' else ""}
|
||||
|
||||
${
|
||||
let makeFileConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.file}
|
||||
'';
|
||||
in concatMapStrings makeFileConf cfg.servedFiles
|
||||
}
|
||||
|
||||
${
|
||||
let makeDirConf = elem: ''
|
||||
Alias ${elem.urlPath} ${elem.dir}/
|
||||
<Directory ${elem.dir}>
|
||||
Options +Indexes
|
||||
${allGranted}
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
in concatMapStrings makeDirConf cfg.servedDirs
|
||||
}
|
||||
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
${hostOpts.extraConfig}
|
||||
''
|
||||
;
|
||||
|
||||
|
||||
confFile = pkgs.writeText "httpd.conf" ''
|
||||
|
||||
ServerRoot ${httpd}
|
||||
|
||||
ServerName ${config.networking.hostName}
|
||||
DefaultRuntimeDir ${runtimeDir}/runtime
|
||||
|
||||
PidFile ${runtimeDir}/httpd.pid
|
||||
@ -246,10 +259,9 @@ let
|
||||
</IfModule>
|
||||
|
||||
${let
|
||||
listen = concatMap getListen allHosts;
|
||||
toStr = listen: "Listen ${listenToString listen}\n";
|
||||
uniqueListen = uniqList {inputList = map toStr listen;};
|
||||
in concatStrings uniqueListen
|
||||
toStr = listen: "Listen ${listen.ip}:${toString listen.port} ${if listen.ssl then "https" else "http"}";
|
||||
uniqueListen = uniqList {inputList = map toStr listenInfo;};
|
||||
in concatStringsSep "\n" uniqueListen
|
||||
}
|
||||
|
||||
User ${mainCfg.user}
|
||||
@ -297,17 +309,9 @@ let
|
||||
${allGranted}
|
||||
</Directory>
|
||||
|
||||
# Generate directives for the main server.
|
||||
${perServerConf true mainCfg}
|
||||
${mainCfg.extraConfig}
|
||||
|
||||
${let
|
||||
makeVirtualHost = vhost: ''
|
||||
<VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
|
||||
${perServerConf false vhost}
|
||||
</VirtualHost>
|
||||
'';
|
||||
in concatMapStrings makeVirtualHost mainCfg.virtualHosts
|
||||
}
|
||||
${concatMapStringsSep "\n" mkVHostConf vhosts}
|
||||
'';
|
||||
|
||||
# Generate the PHP configuration file. Should probably be factored
|
||||
@ -329,6 +333,21 @@ in
|
||||
imports = [
|
||||
(mkRemovedOptionModule [ "services" "httpd" "extraSubservices" ] "Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "stateDir" ] "The httpd module now uses /run/httpd as a runtime directory.")
|
||||
|
||||
# virtualHosts options
|
||||
(mkRemovedOptionModule [ "services" "httpd" "documentRoot" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "enableSSL" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "enableUserDir" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "globalRedirect" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "hostName" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "listen" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "robotsEntries" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "servedDirs" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "servedFiles" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "serverAliases" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "sslServerCert" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "sslServerChain" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
(mkRemovedOptionModule [ "services" "httpd" "sslServerKey" ] "Please define a virtual host using `services.httpd.virtualHosts`.")
|
||||
];
|
||||
|
||||
###### interface
|
||||
@ -391,9 +410,25 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
adminAddr = mkOption {
|
||||
type = types.str;
|
||||
example = "admin@example.org";
|
||||
description = "E-mail address of the server administrator.";
|
||||
};
|
||||
|
||||
logFormat = mkOption {
|
||||
type = types.str;
|
||||
default = "common";
|
||||
example = "combined";
|
||||
description = ''
|
||||
Log format for log files. Possible values are: combined, common, referer, agent.
|
||||
See <link xlink:href="https://httpd.apache.org/docs/2.4/logs.html"/> for more details.
|
||||
'';
|
||||
};
|
||||
|
||||
logPerVirtualHost = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
default = true;
|
||||
description = ''
|
||||
If enabled, each virtual host gets its own
|
||||
<filename>access.log</filename> and
|
||||
@ -429,26 +464,28 @@ in
|
||||
};
|
||||
|
||||
virtualHosts = mkOption {
|
||||
type = types.listOf (types.submodule (
|
||||
{ options = import ./per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = false;
|
||||
type = with types; attrsOf (submodule (import ./per-server-options.nix));
|
||||
default = {
|
||||
localhost = {
|
||||
documentRoot = "${httpd}/htdocs";
|
||||
};
|
||||
};
|
||||
example = literalExample ''
|
||||
{
|
||||
"foo.example.com" = {
|
||||
forceSSL = true;
|
||||
documentRoot = "/var/www/foo.example.com"
|
||||
};
|
||||
"bar.example.com" = {
|
||||
addSSL = true;
|
||||
documentRoot = "/var/www/bar.example.com";
|
||||
};
|
||||
}));
|
||||
default = [];
|
||||
example = [
|
||||
{ hostName = "foo";
|
||||
documentRoot = "/data/webroot-foo";
|
||||
}
|
||||
{ hostName = "bar";
|
||||
documentRoot = "/data/webroot-bar";
|
||||
}
|
||||
];
|
||||
'';
|
||||
description = ''
|
||||
Specification of the virtual hosts served by Apache. Each
|
||||
Specification of the virtual hosts served by Apache. Each
|
||||
element should be an attribute set specifying the
|
||||
configuration of the virtual host. The available options
|
||||
are the non-global options permissible for the main host.
|
||||
configuration of the virtual host.
|
||||
'';
|
||||
};
|
||||
|
||||
@ -534,13 +571,7 @@ in
|
||||
example = "All -SSLv2 -SSLv3";
|
||||
description = "Allowed SSL/TLS protocol versions.";
|
||||
};
|
||||
}
|
||||
|
||||
# Include the options shared between the main server and virtual hosts.
|
||||
// (import ./per-server-options.nix {
|
||||
inherit lib;
|
||||
forMainServer = true;
|
||||
});
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@ -549,11 +580,31 @@ in
|
||||
|
||||
config = mkIf config.services.httpd.enable {
|
||||
|
||||
assertions = [ { assertion = mainCfg.enableSSL == true
|
||||
-> mainCfg.sslServerCert != null
|
||||
&& mainCfg.sslServerKey != null;
|
||||
message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
|
||||
];
|
||||
assertions = [
|
||||
{
|
||||
assertion = all (hostOpts: !hostOpts.enableSSL) vhosts;
|
||||
message = ''
|
||||
The option `services.httpd.virtualHosts.<name>.enableSSL` no longer has any effect; please remove it.
|
||||
Select one of `services.httpd.virtualHosts.<name>.addSSL`, `services.httpd.virtualHosts.<name>.forceSSL`,
|
||||
or `services.httpd.virtualHosts.<name>.onlySSL`.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = all (hostOpts: with hostOpts; !(addSSL && onlySSL) && !(forceSSL && onlySSL) && !(addSSL && forceSSL)) vhosts;
|
||||
message = ''
|
||||
Options `services.httpd.virtualHosts.<name>.addSSL`,
|
||||
`services.httpd.virtualHosts.<name>.onlySSL` and `services.httpd.virtualHosts.<name>.forceSSL`
|
||||
are mutually exclusive.
|
||||
'';
|
||||
}
|
||||
{
|
||||
assertion = all (hostOpts: !(hostOpts.enableACME && hostOpts.useACMEHost != null)) vhosts;
|
||||
message = ''
|
||||
Options `services.httpd.virtualHosts.<name>.enableACME` and
|
||||
`services.httpd.virtualHosts.<name>.useACMEHost` are mutually exclusive.
|
||||
'';
|
||||
}
|
||||
];
|
||||
|
||||
users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
|
||||
{ name = "wwwrun";
|
||||
@ -567,6 +618,15 @@ in
|
||||
gid = config.ids.gids.wwwrun;
|
||||
});
|
||||
|
||||
security.acme.certs = mapAttrs (name: hostOpts: {
|
||||
user = mainCfg.user;
|
||||
group = mkDefault mainCfg.group;
|
||||
email = if hostOpts.adminAddr != null then hostOpts.adminAddr else mainCfg.adminAddr;
|
||||
webroot = hostOpts.acmeRoot;
|
||||
extraDomains = genAttrs hostOpts.serverAliases (alias: null);
|
||||
postRun = "systemctl reload httpd.service";
|
||||
}) (filterAttrs (name: hostOpts: hostOpts.enableACME) mainCfg.virtualHosts);
|
||||
|
||||
environment.systemPackages = [httpd];
|
||||
|
||||
services.httpd.phpOptions =
|
||||
@ -605,10 +665,14 @@ in
|
||||
];
|
||||
|
||||
systemd.services.httpd =
|
||||
let
|
||||
vhostsACME = filter (hostOpts: hostOpts.enableACME) vhosts;
|
||||
in
|
||||
{ description = "Apache HTTPD";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" "fs.target" ];
|
||||
wants = concatLists (map (hostOpts: [ "acme-${hostOpts.hostName}.service" "acme-selfsigned-${hostOpts.hostName}.service" ]) vhostsACME);
|
||||
after = [ "network.target" "fs.target" ] ++ map (hostOpts: "acme-selfsigned-${hostOpts.hostName}.service") vhostsACME;
|
||||
|
||||
path =
|
||||
[ httpd pkgs.coreutils pkgs.gnugrep ]
|
||||
|
@ -1,174 +1,235 @@
|
||||
# This file defines the options that can be used both for the Apache
|
||||
# main server configuration, and for the virtual hosts. (The latter
|
||||
# has additional options that affect the web server as a whole, like
|
||||
# the user/group to run under.)
|
||||
|
||||
{ forMainServer, lib }:
|
||||
|
||||
with lib;
|
||||
|
||||
{ config, lib, name, ... }:
|
||||
let
|
||||
inherit (lib) mkOption types;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
|
||||
hostName = mkOption {
|
||||
type = types.str;
|
||||
default = name;
|
||||
description = "Canonical hostname for the server.";
|
||||
};
|
||||
|
||||
serverAliases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = ["www.example.org" "www.example.org:8080" "example.org"];
|
||||
description = ''
|
||||
Additional names of virtual hosts served by this virtual host configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
listen = mkOption {
|
||||
type = with types; listOf (submodule ({
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
description = "Port to listen on";
|
||||
};
|
||||
ip = mkOption {
|
||||
type = types.str;
|
||||
default = "*";
|
||||
description = "IP to listen on. 0.0.0.0 for IPv4 only, * for all.";
|
||||
};
|
||||
ssl = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable SSL (https) support.";
|
||||
};
|
||||
};
|
||||
}));
|
||||
default = [];
|
||||
example = [
|
||||
{ ip = "195.154.1.1"; port = 443; ssl = true;}
|
||||
{ ip = "192.154.1.1"; port = 80; }
|
||||
{ ip = "*"; port = 8080; }
|
||||
];
|
||||
description = ''
|
||||
Listen addresses and ports for this virtual host.
|
||||
<note><para>
|
||||
This option overrides <literal>addSSL</literal>, <literal>forceSSL</literal> and <literal>onlySSL</literal>.
|
||||
</para></note>
|
||||
'';
|
||||
};
|
||||
|
||||
enableSSL = mkOption {
|
||||
type = types.bool;
|
||||
visible = false;
|
||||
default = false;
|
||||
};
|
||||
|
||||
addSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable HTTPS in addition to plain HTTP. This will set defaults for
|
||||
<literal>listen</literal> to listen on all interfaces on the respective default
|
||||
ports (80, 443).
|
||||
'';
|
||||
};
|
||||
|
||||
onlySSL = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable HTTPS and reject plain HTTP connections. This will set
|
||||
defaults for <literal>listen</literal> to listen on all interfaces on port 443.
|
||||
'';
|
||||
};
|
||||
|
||||
forceSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to add a separate nginx server block that permanently redirects (301)
|
||||
all plain HTTP traffic to HTTPS. This will set defaults for
|
||||
<literal>listen</literal> to listen on all interfaces on the respective default
|
||||
ports (80, 443), where the non-SSL listens are used for the redirect vhosts.
|
||||
'';
|
||||
};
|
||||
|
||||
enableACME = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to ask Let's Encrypt to sign a certificate for this vhost.
|
||||
Alternately, you can use an existing certificate through <option>useACMEHost</option>.
|
||||
'';
|
||||
};
|
||||
|
||||
useACMEHost = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
A host of an existing Let's Encrypt certificate to use.
|
||||
This is useful if you have many subdomains and want to avoid hitting the
|
||||
<link xlink:href="https://letsencrypt.org/docs/rate-limits/">rate limit</link>.
|
||||
Alternately, you can generate a certificate through <option>enableACME</option>.
|
||||
<emphasis>Note that this option does not create any certificates, nor it does add subdomains to existing ones – you will need to create them manually using <xref linkend="opt-security.acme.certs"/>.</emphasis>
|
||||
'';
|
||||
};
|
||||
|
||||
acmeRoot = mkOption {
|
||||
type = types.str;
|
||||
default = "/var/lib/acme/acme-challenges";
|
||||
description = "Directory for the acme challenge which is PUBLIC, don't put certs or keys in here";
|
||||
};
|
||||
|
||||
sslServerCert = mkOption {
|
||||
type = types.path;
|
||||
example = "/var/host.cert";
|
||||
description = "Path to server SSL certificate.";
|
||||
};
|
||||
|
||||
sslServerKey = mkOption {
|
||||
type = types.path;
|
||||
example = "/var/host.key";
|
||||
description = "Path to server SSL certificate key.";
|
||||
};
|
||||
|
||||
sslServerChain = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/var/ca.pem";
|
||||
description = "Path to server SSL chain file.";
|
||||
};
|
||||
|
||||
adminAddr = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = "admin@example.org";
|
||||
description = "E-mail address of the server administrator.";
|
||||
};
|
||||
|
||||
documentRoot = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/data/webserver/docs";
|
||||
description = ''
|
||||
The path of Apache's document root directory. If left undefined,
|
||||
an empty directory in the Nix store will be used as root.
|
||||
'';
|
||||
};
|
||||
|
||||
servedDirs = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/nix";
|
||||
dir = "/home/eelco/Dev/nix-homepage";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve static directories.
|
||||
'';
|
||||
};
|
||||
|
||||
servedFiles = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/foo/bar.png";
|
||||
file = "/home/eelco/some-file.png";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve individual, static files.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
<Directory /home>
|
||||
Options FollowSymlinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
description = ''
|
||||
These lines go to httpd.conf verbatim. They will go after
|
||||
directories and directory aliases defined by default.
|
||||
'';
|
||||
};
|
||||
|
||||
enableUserDir = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable serving <filename>~/public_html</filename> as
|
||||
<literal>/~<replaceable>username</replaceable></literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
globalRedirect = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = http://newserver.example.org/;
|
||||
description = ''
|
||||
If set, all requests for this host are redirected permanently to
|
||||
the given URL.
|
||||
'';
|
||||
};
|
||||
|
||||
logFormat = mkOption {
|
||||
type = types.str;
|
||||
default = "common";
|
||||
example = "combined";
|
||||
description = ''
|
||||
Log format for Apache's log files. Possible values are: combined, common, referer, agent.
|
||||
'';
|
||||
};
|
||||
|
||||
robotsEntries = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = "Disallow: /foo/";
|
||||
description = ''
|
||||
Specification of pages to be ignored by web crawlers. See <link
|
||||
xlink:href='http://www.robotstxt.org/'/> for details.
|
||||
'';
|
||||
};
|
||||
|
||||
hostName = mkOption {
|
||||
type = types.str;
|
||||
default = "localhost";
|
||||
description = "Canonical hostname for the server.";
|
||||
};
|
||||
|
||||
serverAliases = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
example = ["www.example.org" "www.example.org:8080" "example.org"];
|
||||
description = ''
|
||||
Additional names of virtual hosts served by this virtual host configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
listen = mkOption {
|
||||
type = types.listOf (types.submodule (
|
||||
{
|
||||
options = {
|
||||
port = mkOption {
|
||||
type = types.int;
|
||||
description = "port to listen on";
|
||||
};
|
||||
ip = mkOption {
|
||||
type = types.str;
|
||||
default = "*";
|
||||
description = "Ip to listen on. 0.0.0.0 for ipv4 only, * for all.";
|
||||
};
|
||||
};
|
||||
} ));
|
||||
description = ''
|
||||
List of { /* ip: "*"; */ port = 80;} to listen on
|
||||
'';
|
||||
|
||||
default = [];
|
||||
};
|
||||
|
||||
enableSSL = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether to enable SSL (https) support.";
|
||||
};
|
||||
|
||||
# Note: sslServerCert and sslServerKey can be left empty, but this
|
||||
# only makes sense for virtual hosts (they will inherit from the
|
||||
# main server).
|
||||
|
||||
sslServerCert = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/var/host.cert";
|
||||
description = "Path to server SSL certificate.";
|
||||
};
|
||||
|
||||
sslServerKey = mkOption {
|
||||
type = types.path;
|
||||
example = "/var/host.key";
|
||||
description = "Path to server SSL certificate key.";
|
||||
};
|
||||
|
||||
sslServerChain = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/var/ca.pem";
|
||||
description = "Path to server SSL chain file.";
|
||||
};
|
||||
|
||||
adminAddr = mkOption ({
|
||||
type = types.nullOr types.str;
|
||||
example = "admin@example.org";
|
||||
description = "E-mail address of the server administrator.";
|
||||
} // (if forMainServer then {} else {default = null;}));
|
||||
|
||||
documentRoot = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
example = "/data/webserver/docs";
|
||||
description = ''
|
||||
The path of Apache's document root directory. If left undefined,
|
||||
an empty directory in the Nix store will be used as root.
|
||||
'';
|
||||
};
|
||||
|
||||
servedDirs = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/nix";
|
||||
dir = "/home/eelco/Dev/nix-homepage";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve static directories.
|
||||
'';
|
||||
};
|
||||
|
||||
servedFiles = mkOption {
|
||||
type = types.listOf types.attrs;
|
||||
default = [];
|
||||
example = [
|
||||
{ urlPath = "/foo/bar.png";
|
||||
file = "/home/eelco/some-file.png";
|
||||
}
|
||||
];
|
||||
description = ''
|
||||
This option provides a simple way to serve individual, static files.
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = ''
|
||||
<Directory /home>
|
||||
Options FollowSymlinks
|
||||
AllowOverride All
|
||||
</Directory>
|
||||
'';
|
||||
description = ''
|
||||
These lines go to httpd.conf verbatim. They will go after
|
||||
directories and directory aliases defined by default.
|
||||
'';
|
||||
};
|
||||
|
||||
enableUserDir = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to enable serving <filename>~/public_html</filename> as
|
||||
<literal>/~<replaceable>username</replaceable></literal>.
|
||||
'';
|
||||
};
|
||||
|
||||
globalRedirect = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
example = http://newserver.example.org/;
|
||||
description = ''
|
||||
If set, all requests for this host are redirected permanently to
|
||||
the given URL.
|
||||
'';
|
||||
};
|
||||
|
||||
logFormat = mkOption {
|
||||
type = types.str;
|
||||
default = "common";
|
||||
example = "combined";
|
||||
description = ''
|
||||
Log format for Apache's log files. Possible values are: combined, common, referer, agent.
|
||||
'';
|
||||
};
|
||||
|
||||
robotsEntries = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
example = "Disallow: /foo/";
|
||||
description = ''
|
||||
Specification of pages to be ignored by web crawlers. See <link
|
||||
xlink:href='http://www.robotstxt.org/'/> for details.
|
||||
'';
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ in {
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
adminAddr = "test@example.org";
|
||||
documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
|
||||
virtualHosts.localhost.documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
}
|
||||
|
@ -23,12 +23,14 @@ import ./make-test-python.nix ({ pkgs, ...}: {
|
||||
};
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
|
||||
adminAddr = "notme@yourhost.local";
|
||||
listen = [{
|
||||
ip = "::1";
|
||||
port = 8000;
|
||||
}];
|
||||
virtualHosts.localhost = {
|
||||
documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
|
||||
adminAddr = "notme@yourhost.local";
|
||||
listen = [{
|
||||
ip = "::1";
|
||||
port = 8000;
|
||||
}];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -16,7 +16,7 @@ import ../make-test-python.nix ({ pkgs, ... }:
|
||||
|
||||
services.httpd = {
|
||||
enable = true;
|
||||
documentRoot = ./example;
|
||||
virtualHosts.localhost.documentRoot = ./example;
|
||||
adminAddr = "noone@testing.nowhere";
|
||||
};
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ./make-test.nix ({ pkgs, ...} :
|
||||
import ./make-test.nix ({ pkgs, ...} :
|
||||
|
||||
let
|
||||
|
||||
@ -7,7 +7,7 @@ let
|
||||
|
||||
{ services.httpd.enable = true;
|
||||
services.httpd.adminAddr = "foo@example.org";
|
||||
services.httpd.documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
|
||||
services.httpd.virtualHosts.localhost.documentRoot = "${pkgs.valgrind.doc}/share/doc/valgrind/html";
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
};
|
||||
|
||||
@ -26,11 +26,11 @@ in
|
||||
{ services.httpd.enable = true;
|
||||
services.httpd.adminAddr = "bar@example.org";
|
||||
services.httpd.extraModules = [ "proxy_balancer" "lbmethod_byrequests" ];
|
||||
|
||||
services.httpd.extraConfig =
|
||||
''
|
||||
ExtendedStatus on
|
||||
|
||||
services.httpd.extraConfig = ''
|
||||
ExtendedStatus on
|
||||
'';
|
||||
services.httpd.virtualHosts.localhost = {
|
||||
extraConfig = ''
|
||||
<Location /server-status>
|
||||
Require all granted
|
||||
SetHandler server-status
|
||||
@ -50,6 +50,7 @@ in
|
||||
# For testing; don't want to wait forever for dead backend servers.
|
||||
ProxyTimeout 5
|
||||
'';
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||
};
|
||||
|
@ -56,9 +56,11 @@ in
|
||||
networking.firewall.enable = false;
|
||||
|
||||
services.httpd.enable = true;
|
||||
services.httpd.listen = [{ ip = "*"; port = 9000; }];
|
||||
services.httpd.adminAddr = "foo@example.org";
|
||||
services.httpd.documentRoot = "/tmp";
|
||||
services.httpd.virtualHosts.localhost = {
|
||||
listen = [{ ip = "*"; port = 9000; }];
|
||||
adminAddr = "foo@example.org";
|
||||
documentRoot = "/tmp";
|
||||
};
|
||||
};
|
||||
|
||||
client2 =
|
||||
|
Loading…
Reference in New Issue
Block a user