nixos/httpd: limit serving web content to virtual hosts, convert virtualHosts option type from listOf to attrsOf, add ACME integration
This commit is contained in:
parent
d5bbb86bcb
commit
79215f0df1
@ -11,50 +11,46 @@
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
{
|
{
|
||||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||||
[ { hostName = "example.org";
|
{ "blog.example.org" = {
|
||||||
documentRoot = "/webroot";
|
documentRoot = "/webroot/blog.example.org";
|
||||||
adminAddr = "alice@example.org";
|
adminAddr = "alice@example.org";
|
||||||
enableUserDir = true;
|
forceSSL = true;
|
||||||
}
|
enableACME = true;
|
||||||
{ hostName = "example.org";
|
enablePHP = true;
|
||||||
documentRoot = "/webroot";
|
};
|
||||||
|
"wiki.example.org" = {
|
||||||
|
documentRoot = "/webroot/wiki.example.org";
|
||||||
adminAddr = "alice@example.org";
|
adminAddr = "alice@example.org";
|
||||||
enableUserDir = true;
|
forceSSL = true;
|
||||||
enableSSL = true;
|
enableACME = true;
|
||||||
sslServerCert = "/root/ssl-example-org.crt";
|
enablePHP = true;
|
||||||
sslServerKey = "/root/ssl-example-org.key";
|
};
|
||||||
}
|
};
|
||||||
];
|
|
||||||
}
|
}
|
||||||
</programlisting>
|
</programlisting>
|
||||||
It defines two virtual hosts with nearly identical configuration; the only
|
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>:
|
duplication, we can use a <literal>let</literal>:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
let
|
let
|
||||||
exampleOrgCommon =
|
commonConfig =
|
||||||
{ hostName = "example.org";
|
{ adminAddr = "alice@example.org";
|
||||||
documentRoot = "/webroot";
|
forceSSL = true;
|
||||||
adminAddr = "alice@example.org";
|
enableACME = true;
|
||||||
enableUserDir = true;
|
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||||
[ exampleOrgCommon
|
{ "blog.example.org" = (commonConfig // { documentRoot = "/webroot/blog.example.org"; });
|
||||||
(exampleOrgCommon // {
|
"wiki.example.org" = (commonConfig // { documentRoot = "/webroot/wiki.example.com"; });
|
||||||
enableSSL = true;
|
};
|
||||||
sslServerCert = "/root/ssl-example-org.crt";
|
|
||||||
sslServerKey = "/root/ssl-example-org.key";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
</programlisting>
|
</programlisting>
|
||||||
The <literal>let exampleOrgCommon = <replaceable>...</replaceable></literal>
|
The <literal>let commonConfig = <replaceable>...</replaceable></literal>
|
||||||
defines a variable named <literal>exampleOrgCommon</literal>. The
|
defines a variable named <literal>commonConfig</literal>. The
|
||||||
<literal>//</literal> operator merges two attribute sets, so the
|
<literal>//</literal> operator merges two attribute sets, so the
|
||||||
configuration of the second virtual host is the set
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -63,13 +59,13 @@ in
|
|||||||
<programlisting>
|
<programlisting>
|
||||||
{
|
{
|
||||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||||
let exampleOrgCommon = <replaceable>...</replaceable>; in
|
let commonConfig = <replaceable>...</replaceable>; in
|
||||||
[ exampleOrgCommon
|
{ "blog.example.org" = (commonConfig // { <replaceable>...</replaceable> })
|
||||||
(exampleOrgCommon // { <replaceable>...</replaceable> })
|
"wiki.example.org" = (commonConfig // { <replaceable>...</replaceable> })
|
||||||
];
|
};
|
||||||
}
|
}
|
||||||
</programlisting>
|
</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
|
<replaceable>...</replaceable>; }</literal> since attributes (as opposed to
|
||||||
attribute values) are not expressions.
|
attribute values) are not expressions.
|
||||||
</para>
|
</para>
|
||||||
@ -77,80 +73,29 @@ in
|
|||||||
<para>
|
<para>
|
||||||
<emphasis>Functions</emphasis> provide another method of abstraction. For
|
<emphasis>Functions</emphasis> provide another method of abstraction. For
|
||||||
instance, suppose that we want to generate lots of different virtual hosts,
|
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:
|
as follows:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
{
|
{
|
||||||
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
<xref linkend="opt-services.httpd.virtualHosts"/> =
|
||||||
let
|
let
|
||||||
makeVirtualHost = name:
|
makeVirtualHost = webroot:
|
||||||
{ hostName = name;
|
{ documentRoot = webroot;
|
||||||
documentRoot = "/webroot";
|
|
||||||
adminAddr = "alice@example.org";
|
adminAddr = "alice@example.org";
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
[ (makeVirtualHost "example.org")
|
{ "example.org" = (makeVirtualHost "/webroot/example.org");
|
||||||
(makeVirtualHost "example.com")
|
"example.com" = (makeVirtualHost "/webroot/example.com");
|
||||||
(makeVirtualHost "example.gov")
|
"example.gov" = (makeVirtualHost "/webroot/example.gov");
|
||||||
(makeVirtualHost "example.nl")
|
"example.nl" = (makeVirtualHost "/webroot/example.nl");
|
||||||
];
|
};
|
||||||
}
|
}
|
||||||
</programlisting>
|
</programlisting>
|
||||||
Here, <varname>makeVirtualHost</varname> is a function that takes a single
|
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
|
host. That function is then called for several names to produce the list of
|
||||||
virtual host configurations.
|
virtual host configurations.
|
||||||
</para>
|
</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>
|
</section>
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
{ <xref linkend="opt-services.httpd.enable"/> = true;
|
{ <xref linkend="opt-services.httpd.enable"/> = true;
|
||||||
<xref linkend="opt-services.httpd.adminAddr"/> = "alice@example.org";
|
<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>
|
</programlisting>
|
||||||
defines a configuration with three option definitions that together enable
|
defines a configuration with three option definitions that together enable
|
||||||
@ -50,7 +50,11 @@
|
|||||||
httpd = {
|
httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = "alice@example.org";
|
adminAddr = "alice@example.org";
|
||||||
documentRoot = "/webroot";
|
virtualHosts = {
|
||||||
|
localhost = {
|
||||||
|
documentRoot = "/webroot";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -327,6 +327,28 @@ services.xserver.displayManager.defaultSession = "xfce+icewm";
|
|||||||
module.
|
module.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</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>
|
</itemizedlist>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ let
|
|||||||
|
|
||||||
nagiosState = "/var/lib/nagios";
|
nagiosState = "/var/lib/nagios";
|
||||||
nagiosLogDir = "/var/log/nagios";
|
nagiosLogDir = "/var/log/nagios";
|
||||||
|
urlPath = "/nagios";
|
||||||
|
|
||||||
nagiosObjectDefs = cfg.objectDefs;
|
nagiosObjectDefs = cfg.objectDefs;
|
||||||
|
|
||||||
@ -49,12 +50,12 @@ let
|
|||||||
''
|
''
|
||||||
main_config_file=${cfg.mainConfigFile}
|
main_config_file=${cfg.mainConfigFile}
|
||||||
use_authentication=0
|
use_authentication=0
|
||||||
url_html_path=${cfg.urlPath}
|
url_html_path=${urlPath}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
extraHttpdConfig =
|
extraHttpdConfig =
|
||||||
''
|
''
|
||||||
ScriptAlias ${cfg.urlPath}/cgi-bin ${pkgs.nagios}/sbin
|
ScriptAlias ${urlPath}/cgi-bin ${pkgs.nagios}/sbin
|
||||||
|
|
||||||
<Directory "${pkgs.nagios}/sbin">
|
<Directory "${pkgs.nagios}/sbin">
|
||||||
Options ExecCGI
|
Options ExecCGI
|
||||||
@ -62,7 +63,7 @@ let
|
|||||||
SetEnv NAGIOS_CGI_CONFIG ${cfg.cgiConfigFile}
|
SetEnv NAGIOS_CGI_CONFIG ${cfg.cgiConfigFile}
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
Alias ${cfg.urlPath} ${pkgs.nagios}/share
|
Alias ${urlPath} ${pkgs.nagios}/share
|
||||||
|
|
||||||
<Directory "${pkgs.nagios}/share">
|
<Directory "${pkgs.nagios}/share">
|
||||||
Options None
|
Options None
|
||||||
@ -72,6 +73,10 @@ let
|
|||||||
|
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
imports = [
|
||||||
|
(mkRemovedOptionModule [ "services" "nagios" "urlPath" ] "The urlPath option has been removed as it is hard coded to /nagios in the nagios package.")
|
||||||
|
];
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
services.nagios = {
|
services.nagios = {
|
||||||
enable = mkOption {
|
enable = mkOption {
|
||||||
@ -128,13 +133,20 @@ in
|
|||||||
";
|
";
|
||||||
};
|
};
|
||||||
|
|
||||||
urlPath = mkOption {
|
virtualHost = mkOption {
|
||||||
default = "/nagios";
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
description = "
|
example = literalExample ''
|
||||||
The URL path under which the Nagios web interface appears.
|
{ hostName = "example.org";
|
||||||
That is, you can access the Nagios web interface through
|
adminAddr = "webmaster@example.org";
|
||||||
<literal>http://<replaceable>server</replaceable>/<replaceable>urlPath</replaceable></literal>.
|
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
|
let
|
||||||
|
|
||||||
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption;
|
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;
|
cfg = config.services.limesurvey;
|
||||||
fpm = config.services.phpfpm.pools.limesurvey;
|
fpm = config.services.phpfpm.pools.limesurvey;
|
||||||
@ -100,19 +100,15 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHost = mkOption {
|
virtualHost = mkOption {
|
||||||
type = types.submodule ({
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
example = literalExample ''
|
||||||
inherit lib;
|
{
|
||||||
forMainServer = false;
|
hostName = "survey.example.org";
|
||||||
};
|
adminAddr = "webmaster@example.org";
|
||||||
});
|
forceSSL = true;
|
||||||
example = {
|
enableACME = true;
|
||||||
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";
|
|
||||||
};
|
|
||||||
description = ''
|
description = ''
|
||||||
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
||||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||||
@ -184,7 +180,7 @@ in
|
|||||||
config = {
|
config = {
|
||||||
tempdir = "${stateDir}/tmp";
|
tempdir = "${stateDir}/tmp";
|
||||||
uploaddir = "${stateDir}/upload";
|
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";
|
config.defaultlang = "en";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -215,38 +211,36 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||||
extraModules = [ "proxy_fcgi" ];
|
extraModules = [ "proxy_fcgi" ];
|
||||||
virtualHosts = [ (mkMerge [
|
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||||
cfg.virtualHost {
|
documentRoot = mkForce "${pkg}/share/limesurvey";
|
||||||
documentRoot = mkForce "${pkg}/share/limesurvey";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
Alias "/tmp" "${stateDir}/tmp"
|
||||||
Alias "/tmp" "${stateDir}/tmp"
|
<Directory "${stateDir}">
|
||||||
<Directory "${stateDir}">
|
AllowOverride all
|
||||||
AllowOverride all
|
Require all granted
|
||||||
Require all granted
|
Options -Indexes +FollowSymlinks
|
||||||
Options -Indexes +FollowSymlinks
|
</Directory>
|
||||||
</Directory>
|
|
||||||
|
|
||||||
Alias "/upload" "${stateDir}/upload"
|
Alias "/upload" "${stateDir}/upload"
|
||||||
<Directory "${stateDir}/upload">
|
<Directory "${stateDir}/upload">
|
||||||
AllowOverride all
|
AllowOverride all
|
||||||
Require all granted
|
Require all granted
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
<Directory "${pkg}/share/limesurvey">
|
<Directory "${pkg}/share/limesurvey">
|
||||||
<FilesMatch "\.php$">
|
<FilesMatch "\.php$">
|
||||||
<If "-f %{REQUEST_FILENAME}">
|
<If "-f %{REQUEST_FILENAME}">
|
||||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||||
</If>
|
</If>
|
||||||
</FilesMatch>
|
</FilesMatch>
|
||||||
|
|
||||||
AllowOverride all
|
AllowOverride all
|
||||||
Options -Indexes
|
Options -Indexes
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
</Directory>
|
</Directory>
|
||||||
'';
|
'';
|
||||||
}
|
} ];
|
||||||
]) ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
|
@ -64,7 +64,7 @@ let
|
|||||||
$wgScriptPath = "";
|
$wgScriptPath = "";
|
||||||
|
|
||||||
## The protocol and server name to use in fully-qualified URLs
|
## 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.)
|
## The URL path to static resources (images, scripts, etc.)
|
||||||
$wgResourceBasePath = $wgScriptPath;
|
$wgResourceBasePath = $wgScriptPath;
|
||||||
@ -290,19 +290,13 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHost = mkOption {
|
virtualHost = mkOption {
|
||||||
type = types.submodule ({
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
|
||||||
inherit lib;
|
|
||||||
forMainServer = false;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
example = literalExample ''
|
example = literalExample ''
|
||||||
{
|
{
|
||||||
hostName = "mediawiki.example.org";
|
hostName = "mediawiki.example.org";
|
||||||
enableSSL = true;
|
|
||||||
adminAddr = "webmaster@example.org";
|
adminAddr = "webmaster@example.org";
|
||||||
sslServerCert = "/var/lib/acme/mediawiki.example.org/full.pem";
|
forceSSL = true;
|
||||||
sslServerKey = "/var/lib/acme/mediawiki.example.org/key.pem";
|
enableACME = true;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
description = ''
|
description = ''
|
||||||
@ -389,31 +383,28 @@ in
|
|||||||
|
|
||||||
services.httpd = {
|
services.httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
|
||||||
extraModules = [ "proxy_fcgi" ];
|
extraModules = [ "proxy_fcgi" ];
|
||||||
virtualHosts = [ (mkMerge [
|
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||||
cfg.virtualHost {
|
documentRoot = mkForce "${pkg}/share/mediawiki";
|
||||||
documentRoot = mkForce "${pkg}/share/mediawiki";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
<Directory "${pkg}/share/mediawiki">
|
||||||
<Directory "${pkg}/share/mediawiki">
|
<FilesMatch "\.php$">
|
||||||
<FilesMatch "\.php$">
|
<If "-f %{REQUEST_FILENAME}">
|
||||||
<If "-f %{REQUEST_FILENAME}">
|
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
</If>
|
||||||
</If>
|
</FilesMatch>
|
||||||
</FilesMatch>
|
|
||||||
|
|
||||||
Require all granted
|
Require all granted
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
AllowOverride All
|
AllowOverride All
|
||||||
</Directory>
|
</Directory>
|
||||||
'' + optionalString (cfg.uploadsDir != null) ''
|
'' + optionalString (cfg.uploadsDir != null) ''
|
||||||
Alias "/images" "${cfg.uploadsDir}"
|
Alias "/images" "${cfg.uploadsDir}"
|
||||||
<Directory "${cfg.uploadsDir}">
|
<Directory "${cfg.uploadsDir}">
|
||||||
Require all granted
|
Require all granted
|
||||||
</Directory>
|
</Directory>
|
||||||
'';
|
'';
|
||||||
}
|
} ];
|
||||||
]) ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
|
@ -32,7 +32,7 @@ let
|
|||||||
'dbcollation' => 'utf8mb4_unicode_ci',
|
'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->dataroot = '${stateDir}';
|
||||||
$CFG->admin = 'admin';
|
$CFG->admin = 'admin';
|
||||||
|
|
||||||
@ -140,19 +140,15 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHost = mkOption {
|
virtualHost = mkOption {
|
||||||
type = types.submodule ({
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
example = literalExample ''
|
||||||
inherit lib;
|
{
|
||||||
forMainServer = false;
|
hostName = "moodle.example.org";
|
||||||
};
|
adminAddr = "webmaster@example.org";
|
||||||
});
|
forceSSL = true;
|
||||||
example = {
|
enableACME = true;
|
||||||
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";
|
|
||||||
};
|
|
||||||
description = ''
|
description = ''
|
||||||
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
|
Apache configuration can be done by adapting <option>services.httpd.virtualHosts</option>.
|
||||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||||
@ -241,22 +237,20 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||||
extraModules = [ "proxy_fcgi" ];
|
extraModules = [ "proxy_fcgi" ];
|
||||||
virtualHosts = [ (mkMerge [
|
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||||
cfg.virtualHost {
|
documentRoot = mkForce "${cfg.package}/share/moodle";
|
||||||
documentRoot = mkForce "${cfg.package}/share/moodle";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
<Directory "${cfg.package}/share/moodle">
|
||||||
<Directory "${cfg.package}/share/moodle">
|
<FilesMatch "\.php$">
|
||||||
<FilesMatch "\.php$">
|
<If "-f %{REQUEST_FILENAME}">
|
||||||
<If "-f %{REQUEST_FILENAME}">
|
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
</If>
|
||||||
</If>
|
</FilesMatch>
|
||||||
</FilesMatch>
|
Options -Indexes
|
||||||
Options -Indexes
|
DirectoryIndex index.php
|
||||||
DirectoryIndex index.php
|
</Directory>
|
||||||
</Directory>
|
'';
|
||||||
'';
|
} ];
|
||||||
}
|
|
||||||
]) ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = [
|
systemd.tmpfiles.rules = [
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
let
|
let
|
||||||
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
|
inherit (lib) mkDefault mkEnableOption mkForce mkIf mkMerge mkOption types;
|
||||||
inherit (lib) any attrValues concatMapStringsSep flatten literalExample;
|
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;
|
eachSite = config.services.wordpress;
|
||||||
user = "wordpress";
|
user = "wordpress";
|
||||||
@ -209,18 +209,12 @@ let
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHost = mkOption {
|
virtualHost = mkOption {
|
||||||
type = types.submodule ({
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
|
||||||
inherit lib;
|
|
||||||
forMainServer = false;
|
|
||||||
};
|
|
||||||
});
|
|
||||||
example = literalExample ''
|
example = literalExample ''
|
||||||
{
|
{
|
||||||
enableSSL = true;
|
|
||||||
adminAddr = "webmaster@example.org";
|
adminAddr = "webmaster@example.org";
|
||||||
sslServerCert = "/var/lib/acme/wordpress.example.org/full.pem";
|
forceSSL = true;
|
||||||
sslServerKey = "/var/lib/acme/wordpress.example.org/key.pem";
|
enableACME = true;
|
||||||
}
|
}
|
||||||
'';
|
'';
|
||||||
description = ''
|
description = ''
|
||||||
@ -304,41 +298,37 @@ in
|
|||||||
services.httpd = {
|
services.httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
extraModules = [ "proxy_fcgi" ];
|
extraModules = [ "proxy_fcgi" ];
|
||||||
virtualHosts = mapAttrsToList (hostName: cfg:
|
virtualHosts = mapAttrs (hostName: cfg: mkMerge [ cfg.virtualHost {
|
||||||
(mkMerge [
|
documentRoot = mkForce "${pkg hostName cfg}/share/wordpress";
|
||||||
cfg.virtualHost {
|
extraConfig = ''
|
||||||
documentRoot = mkForce "${pkg hostName cfg}/share/wordpress";
|
<Directory "${pkg hostName cfg}/share/wordpress">
|
||||||
extraConfig = ''
|
<FilesMatch "\.php$">
|
||||||
<Directory "${pkg hostName cfg}/share/wordpress">
|
<If "-f %{REQUEST_FILENAME}">
|
||||||
<FilesMatch "\.php$">
|
SetHandler "proxy:unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}|fcgi://localhost/"
|
||||||
<If "-f %{REQUEST_FILENAME}">
|
</If>
|
||||||
SetHandler "proxy:unix:${config.services.phpfpm.pools."wordpress-${hostName}".socket}|fcgi://localhost/"
|
</FilesMatch>
|
||||||
</If>
|
|
||||||
</FilesMatch>
|
|
||||||
|
|
||||||
# standard wordpress .htaccess contents
|
# standard wordpress .htaccess contents
|
||||||
<IfModule mod_rewrite.c>
|
<IfModule mod_rewrite.c>
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
RewriteBase /
|
RewriteBase /
|
||||||
RewriteRule ^index\.php$ - [L]
|
RewriteRule ^index\.php$ - [L]
|
||||||
RewriteCond %{REQUEST_FILENAME} !-f
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
RewriteCond %{REQUEST_FILENAME} !-d
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
RewriteRule . /index.php [L]
|
RewriteRule . /index.php [L]
|
||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
DirectoryIndex index.php
|
DirectoryIndex index.php
|
||||||
Require all granted
|
Require all granted
|
||||||
Options +FollowSymLinks
|
Options +FollowSymLinks
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
# https://wordpress.org/support/article/hardening-wordpress/#securing-wp-config-php
|
# https://wordpress.org/support/article/hardening-wordpress/#securing-wp-config-php
|
||||||
<Files wp-config.php>
|
<Files wp-config.php>
|
||||||
Require all denied
|
Require all denied
|
||||||
</Files>
|
</Files>
|
||||||
'';
|
'';
|
||||||
}
|
} ]) eachSite;
|
||||||
])
|
|
||||||
) eachSite;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
|
systemd.tmpfiles.rules = flatten (mapAttrsToList (hostName: cfg: [
|
||||||
|
@ -113,19 +113,15 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHost = mkOption {
|
virtualHost = mkOption {
|
||||||
type = types.submodule ({
|
type = types.submodule (import ../web-servers/apache-httpd/per-server-options.nix);
|
||||||
options = import ../web-servers/apache-httpd/per-server-options.nix {
|
example = literalExample ''
|
||||||
inherit lib;
|
{
|
||||||
forMainServer = false;
|
hostName = "zabbix.example.org";
|
||||||
};
|
adminAddr = "webmaster@example.org";
|
||||||
});
|
forceSSL = true;
|
||||||
example = {
|
enableACME = true;
|
||||||
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";
|
|
||||||
};
|
|
||||||
description = ''
|
description = ''
|
||||||
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
Apache configuration can be done by adapting <literal>services.httpd.virtualHosts.<name></literal>.
|
||||||
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
See <xref linkend="opt-services.httpd.virtualHosts"/> for further information.
|
||||||
@ -190,23 +186,21 @@ in
|
|||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
adminAddr = mkDefault cfg.virtualHost.adminAddr;
|
||||||
extraModules = [ "proxy_fcgi" ];
|
extraModules = [ "proxy_fcgi" ];
|
||||||
virtualHosts = [ (mkMerge [
|
virtualHosts.${cfg.virtualHost.hostName} = mkMerge [ cfg.virtualHost {
|
||||||
cfg.virtualHost {
|
documentRoot = mkForce "${cfg.package}/share/zabbix";
|
||||||
documentRoot = mkForce "${cfg.package}/share/zabbix";
|
extraConfig = ''
|
||||||
extraConfig = ''
|
<Directory "${cfg.package}/share/zabbix">
|
||||||
<Directory "${cfg.package}/share/zabbix">
|
<FilesMatch "\.php$">
|
||||||
<FilesMatch "\.php$">
|
<If "-f %{REQUEST_FILENAME}">
|
||||||
<If "-f %{REQUEST_FILENAME}">
|
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
||||||
SetHandler "proxy:unix:${fpm.socket}|fcgi://localhost/"
|
</If>
|
||||||
</If>
|
</FilesMatch>
|
||||||
</FilesMatch>
|
AllowOverride all
|
||||||
AllowOverride all
|
Options -Indexes
|
||||||
Options -Indexes
|
DirectoryIndex index.php
|
||||||
DirectoryIndex index.php
|
</Directory>
|
||||||
</Directory>
|
'';
|
||||||
'';
|
} ];
|
||||||
}
|
|
||||||
]) ];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.${user} = mapAttrs (name: mkDefault) {
|
users.users.${user} = mapAttrs (name: mkDefault) {
|
||||||
|
@ -18,22 +18,20 @@ let
|
|||||||
|
|
||||||
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
|
mod_perl = pkgs.apacheHttpdPackages.mod_perl.override { apacheHttpd = httpd; };
|
||||||
|
|
||||||
defaultListen = cfg: if cfg.enableSSL
|
vhosts = attrValues mainCfg.virtualHosts;
|
||||||
then [{ip = "*"; port = 443;}]
|
|
||||||
else [{ip = "*"; port = 80;}];
|
|
||||||
|
|
||||||
getListen = cfg:
|
mkListenInfo = hostOpts:
|
||||||
if cfg.listen == []
|
if hostOpts.listen != [] then hostOpts.listen
|
||||||
then defaultListen cfg
|
else (
|
||||||
else cfg.listen;
|
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) vhosts;
|
||||||
|
|
||||||
enableUserDir = any (vhost: vhost.enableUserDir) allHosts;
|
|
||||||
|
|
||||||
# NOTE: generally speaking order of modules is very important
|
# NOTE: generally speaking order of modules is very important
|
||||||
modules =
|
modules =
|
||||||
@ -115,122 +113,137 @@ let
|
|||||||
</IfModule>
|
</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.
|
sslServerCert = if useACME then "${sslCertDir}/full.pem" else hostOpts.sslServerCert;
|
||||||
canonicalNames =
|
sslServerKey = if useACME then "${sslCertDir}/key.pem" else hostOpts.sslServerKey;
|
||||||
let defaultPort = (head (defaultListen cfg)).port; in
|
sslServerChain = if useACME then "${sslCertDir}/fullchain.pem" else hostOpts.sslServerChain;
|
||||||
map (port:
|
|
||||||
(if cfg.enableSSL then "https" else "http") + "://" +
|
|
||||||
cfg.hostName +
|
|
||||||
(if port != defaultPort then ":${toString port}" else "")
|
|
||||||
) (map (x: x.port) (getListen cfg));
|
|
||||||
|
|
||||||
maybeDocumentRoot = fold (svc: acc:
|
acmeChallenge = optionalString useACME ''
|
||||||
if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
|
Alias /.well-known/acme-challenge/ "${hostOpts.acmeRoot}/.well-known/acme-challenge/"
|
||||||
) null ([ cfg ]);
|
<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
|
mkVHostCommonConf = hostOpts:
|
||||||
pkgs.runCommand "empty" { preferLocalBuild = true; } "mkdir -p $out";
|
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 = ''
|
${optionalString (hostOpts.robotsEntries != "") ''
|
||||||
DocumentRoot "${documentRoot}"
|
Alias /robots.txt ${pkgs.writeText "robots.txt" hostOpts.robotsEntries}
|
||||||
|
''}
|
||||||
|
|
||||||
<Directory "${documentRoot}">
|
DocumentRoot "${documentRoot}"
|
||||||
Options Indexes FollowSymLinks
|
|
||||||
AllowOverride None
|
|
||||||
${allGranted}
|
|
||||||
</Directory>
|
|
||||||
'';
|
|
||||||
|
|
||||||
# If this is a vhost, the include the entries for the main server as well.
|
<Directory "${documentRoot}">
|
||||||
robotsTxt = concatStringsSep "\n" (filter (x: x != "") ([ cfg.robotsEntries ] ++ lib.optional (!isMainServer) mainCfg.robotsEntries));
|
Options Indexes FollowSymLinks
|
||||||
|
AllowOverride None
|
||||||
|
${allGranted}
|
||||||
|
</Directory>
|
||||||
|
|
||||||
in ''
|
${optionalString hostOpts.enableUserDir ''
|
||||||
${concatStringsSep "\n" (map (n: "ServerName ${n}") canonicalNames)}
|
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}
|
let makeFileConf = elem: ''
|
||||||
SSLCertificateKeyFile ${cfg.sslServerKey}
|
Alias ${elem.urlPath} ${elem.file}
|
||||||
${if cfg.sslServerChain != null then ''
|
'';
|
||||||
SSLCertificateChainFile ${cfg.sslServerChain}
|
in concatMapStrings makeFileConf hostOpts.servedFiles
|
||||||
'' else ""}
|
}
|
||||||
'' else ""}
|
${
|
||||||
|
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 ''
|
${hostOpts.extraConfig}
|
||||||
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}
|
|
||||||
'';
|
|
||||||
|
|
||||||
|
|
||||||
confFile = pkgs.writeText "httpd.conf" ''
|
confFile = pkgs.writeText "httpd.conf" ''
|
||||||
|
|
||||||
ServerRoot ${httpd}
|
ServerRoot ${httpd}
|
||||||
|
ServerName ${config.networking.hostName}
|
||||||
DefaultRuntimeDir ${runtimeDir}/runtime
|
DefaultRuntimeDir ${runtimeDir}/runtime
|
||||||
|
|
||||||
PidFile ${runtimeDir}/httpd.pid
|
PidFile ${runtimeDir}/httpd.pid
|
||||||
@ -246,10 +259,9 @@ let
|
|||||||
</IfModule>
|
</IfModule>
|
||||||
|
|
||||||
${let
|
${let
|
||||||
listen = concatMap getListen allHosts;
|
toStr = listen: "Listen ${listen.ip}:${toString listen.port} ${if listen.ssl then "https" else "http"}";
|
||||||
toStr = listen: "Listen ${listenToString listen}\n";
|
uniqueListen = uniqList {inputList = map toStr listenInfo;};
|
||||||
uniqueListen = uniqList {inputList = map toStr listen;};
|
in concatStringsSep "\n" uniqueListen
|
||||||
in concatStrings uniqueListen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
User ${mainCfg.user}
|
User ${mainCfg.user}
|
||||||
@ -297,17 +309,9 @@ let
|
|||||||
${allGranted}
|
${allGranted}
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
# Generate directives for the main server.
|
${mainCfg.extraConfig}
|
||||||
${perServerConf true mainCfg}
|
|
||||||
|
|
||||||
${let
|
${concatMapStringsSep "\n" mkVHostConf vhosts}
|
||||||
makeVirtualHost = vhost: ''
|
|
||||||
<VirtualHost ${concatStringsSep " " (map listenToString (getListen vhost))}>
|
|
||||||
${perServerConf false vhost}
|
|
||||||
</VirtualHost>
|
|
||||||
'';
|
|
||||||
in concatMapStrings makeVirtualHost mainCfg.virtualHosts
|
|
||||||
}
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# Generate the PHP configuration file. Should probably be factored
|
# Generate the PHP configuration file. Should probably be factored
|
||||||
@ -329,6 +333,21 @@ in
|
|||||||
imports = [
|
imports = [
|
||||||
(mkRemovedOptionModule [ "services" "httpd" "extraSubservices" ] "Most existing subservices have been ported to the NixOS module system. Please update your configuration accordingly.")
|
(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.")
|
(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
|
###### 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 {
|
logPerVirtualHost = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
If enabled, each virtual host gets its own
|
If enabled, each virtual host gets its own
|
||||||
<filename>access.log</filename> and
|
<filename>access.log</filename> and
|
||||||
@ -429,26 +464,28 @@ in
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualHosts = mkOption {
|
virtualHosts = mkOption {
|
||||||
type = types.listOf (types.submodule (
|
type = with types; attrsOf (submodule (import ./per-server-options.nix));
|
||||||
{ options = import ./per-server-options.nix {
|
default = {
|
||||||
inherit lib;
|
localhost = {
|
||||||
forMainServer = false;
|
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 = ''
|
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
|
element should be an attribute set specifying the
|
||||||
configuration of the virtual host. The available options
|
configuration of the virtual host.
|
||||||
are the non-global options permissible for the main host.
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -534,13 +571,7 @@ in
|
|||||||
example = "All -SSLv2 -SSLv3";
|
example = "All -SSLv2 -SSLv3";
|
||||||
description = "Allowed SSL/TLS protocol versions.";
|
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 {
|
config = mkIf config.services.httpd.enable {
|
||||||
|
|
||||||
assertions = [ { assertion = mainCfg.enableSSL == true
|
assertions = [
|
||||||
-> mainCfg.sslServerCert != null
|
{
|
||||||
&& mainCfg.sslServerKey != null;
|
assertion = all (hostOpts: !hostOpts.enableSSL) vhosts;
|
||||||
message = "SSL is enabled for httpd, but sslServerCert and/or sslServerKey haven't been specified."; }
|
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
|
users.users = optionalAttrs (mainCfg.user == "wwwrun") (singleton
|
||||||
{ name = "wwwrun";
|
{ name = "wwwrun";
|
||||||
@ -567,6 +618,15 @@ in
|
|||||||
gid = config.ids.gids.wwwrun;
|
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];
|
environment.systemPackages = [httpd];
|
||||||
|
|
||||||
services.httpd.phpOptions =
|
services.httpd.phpOptions =
|
||||||
@ -605,10 +665,14 @@ in
|
|||||||
];
|
];
|
||||||
|
|
||||||
systemd.services.httpd =
|
systemd.services.httpd =
|
||||||
|
let
|
||||||
|
vhostsACME = filter (hostOpts: hostOpts.enableACME) vhosts;
|
||||||
|
in
|
||||||
{ description = "Apache HTTPD";
|
{ description = "Apache HTTPD";
|
||||||
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
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 =
|
path =
|
||||||
[ httpd pkgs.coreutils pkgs.gnugrep ]
|
[ httpd pkgs.coreutils pkgs.gnugrep ]
|
||||||
|
@ -1,174 +1,235 @@
|
|||||||
# This file defines the options that can be used both for the Apache
|
{ config, lib, name, ... }:
|
||||||
# main server configuration, and for the virtual hosts. (The latter
|
let
|
||||||
# has additional options that affect the web server as a whole, like
|
inherit (lib) mkOption types;
|
||||||
# the user/group to run under.)
|
in
|
||||||
|
|
||||||
{ forMainServer, lib }:
|
|
||||||
|
|
||||||
with lib;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
|
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 = {
|
services.httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
adminAddr = "test@example.org";
|
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 ];
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
}
|
}
|
||||||
|
@ -23,12 +23,14 @@ import ./make-test-python.nix ({ pkgs, ...}: {
|
|||||||
};
|
};
|
||||||
services.httpd = {
|
services.httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
|
virtualHosts.localhost = {
|
||||||
adminAddr = "notme@yourhost.local";
|
documentRoot = pkgs.writeTextDir "index.txt" "We are all good!";
|
||||||
listen = [{
|
adminAddr = "notme@yourhost.local";
|
||||||
ip = "::1";
|
listen = [{
|
||||||
port = 8000;
|
ip = "::1";
|
||||||
}];
|
port = 8000;
|
||||||
|
}];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,7 @@ import ../make-test-python.nix ({ pkgs, ... }:
|
|||||||
|
|
||||||
services.httpd = {
|
services.httpd = {
|
||||||
enable = true;
|
enable = true;
|
||||||
documentRoot = ./example;
|
virtualHosts.localhost.documentRoot = ./example;
|
||||||
adminAddr = "noone@testing.nowhere";
|
adminAddr = "noone@testing.nowhere";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import ./make-test.nix ({ pkgs, ...} :
|
import ./make-test.nix ({ pkgs, ...} :
|
||||||
|
|
||||||
let
|
let
|
||||||
|
|
||||||
@ -7,7 +7,7 @@ let
|
|||||||
|
|
||||||
{ services.httpd.enable = true;
|
{ services.httpd.enable = true;
|
||||||
services.httpd.adminAddr = "foo@example.org";
|
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 ];
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,11 +26,11 @@ in
|
|||||||
{ services.httpd.enable = true;
|
{ services.httpd.enable = true;
|
||||||
services.httpd.adminAddr = "bar@example.org";
|
services.httpd.adminAddr = "bar@example.org";
|
||||||
services.httpd.extraModules = [ "proxy_balancer" "lbmethod_byrequests" ];
|
services.httpd.extraModules = [ "proxy_balancer" "lbmethod_byrequests" ];
|
||||||
|
services.httpd.extraConfig = ''
|
||||||
services.httpd.extraConfig =
|
ExtendedStatus on
|
||||||
''
|
'';
|
||||||
ExtendedStatus on
|
services.httpd.virtualHosts.localhost = {
|
||||||
|
extraConfig = ''
|
||||||
<Location /server-status>
|
<Location /server-status>
|
||||||
Require all granted
|
Require all granted
|
||||||
SetHandler server-status
|
SetHandler server-status
|
||||||
@ -50,6 +50,7 @@ in
|
|||||||
# For testing; don't want to wait forever for dead backend servers.
|
# For testing; don't want to wait forever for dead backend servers.
|
||||||
ProxyTimeout 5
|
ProxyTimeout 5
|
||||||
'';
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [ 80 ];
|
networking.firewall.allowedTCPPorts = [ 80 ];
|
||||||
};
|
};
|
||||||
|
@ -56,9 +56,11 @@ in
|
|||||||
networking.firewall.enable = false;
|
networking.firewall.enable = false;
|
||||||
|
|
||||||
services.httpd.enable = true;
|
services.httpd.enable = true;
|
||||||
services.httpd.listen = [{ ip = "*"; port = 9000; }];
|
services.httpd.virtualHosts.localhost = {
|
||||||
services.httpd.adminAddr = "foo@example.org";
|
listen = [{ ip = "*"; port = 9000; }];
|
||||||
services.httpd.documentRoot = "/tmp";
|
adminAddr = "foo@example.org";
|
||||||
|
documentRoot = "/tmp";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
client2 =
|
client2 =
|
||||||
|
Loading…
Reference in New Issue
Block a user