diff --git a/system/options.nix b/system/options.nix index d0e5a6e1b26..b34249d8126 100644 --- a/system/options.nix +++ b/system/options.nix @@ -1467,36 +1467,6 @@ "; }; - httpsPort = mkOption { - default = 443; - description = " - Port for encrypted HTTP requests. - "; - }; - - enableSSL = mkOption { - default = false; - description = " - Whether to enable SSL (https) support. - "; - }; - - sslServerCert = mkOption { - default = ""; - example = /var/host.cert; - description = " - Path to server SSL certificate. - "; - }; - - sslServerKey = mkOption { - default = ""; - example = /var/host.key; - description = " - Path to server SSL certificate key. - "; - }; - logDir = mkOption { default = "/var/log/httpd"; description = " @@ -1513,14 +1483,6 @@ "; }; - enableUserDir = mkOption { - default = false; - description = " - Whether to enable serving ~/public_html as - /~username. - "; - }; - mod_php = mkOption { default = false; description = "Whether to enable the PHP module."; diff --git a/upstart-jobs/apache-httpd/default.nix b/upstart-jobs/apache-httpd/default.nix index acb6c2654cb..8ae1db72058 100644 --- a/upstart-jobs/apache-httpd/default.nix +++ b/upstart-jobs/apache-httpd/default.nix @@ -2,22 +2,24 @@ let - cfg = config.services.httpd; - - mainCfg = cfg; + mainCfg = config.services.httpd; startingDependency = if config.services.gw6c.enable then "gw6c" else "network-interfaces"; httpd = pkgs.apacheHttpd; inherit (pkgs.lib) addDefaultOptionValues optional concatMap concatMapStrings; + + + getPort = cfg: if cfg.port != 0 then cfg.port else if cfg.enableSSL then 443 else 80; makeServerInfo = cfg: { # Canonical name must not include a trailing slash. canonicalName = - "http://${cfg.hostName}" + - (if cfg.httpPort != 80 then ":${toString cfg.httpPort}" else ""); + (if cfg.enableSSL then "https" else "http") + "://" + + cfg.hostName + + (if getPort cfg != (if cfg.enableSSL then 443 else 80) then ":${toString getPort cfg}" else ""); # Admin address: inherit from the main server if not specified for # a virtual host. @@ -40,14 +42,18 @@ let # Fill in defaults for missing options. cfg = addDefaultOptionValues vhostOptions cfgIn; in cfg; - in map makeVirtualHost cfg.virtualHosts; + in map makeVirtualHost mainCfg.virtualHosts; + allHosts = [mainCfg] ++ vhosts; + + callSubservices = serverInfo: defs: let f = svc: let - svcFunction = if svc ? function then - svc.function else (import ( ./noDir/.. + ("/" + svc.serviceName + ".nix"))); + svcFunction = + if svc ? function then svc.function + else import (./noDir/.. + ("/" + svc.serviceName + ".nix")); config = addDefaultOptionValues res.options svc.config; res = svcFunction {inherit config pkgs serverInfo;}; in res; @@ -61,12 +67,13 @@ let allSubservices = mainSubservices ++ pkgs.lib.concatMap subservicesFor vhosts; - sslServerCert = cfg.sslServerCert; - sslServerKey = cfg.sslServerKey; # !!! should be in lib writeTextInDir = name: text: pkgs.runCommand name {inherit text;} "ensureDir $out; echo -n \"$text\" > $out/$name"; + + + enableSSL = pkgs.lib.any (vhost: vhost.enableSSL) allHosts; # Names of modules from ${httpd}/modules that we want to load. @@ -86,11 +93,11 @@ let "mime" "dav" "status" "autoindex" "asis" "info" "cgi" "dav_fs" "vhost_alias" "negotiation" "dir" "imagemap" "actions" "speling" "userdir" "alias" "rewrite" "proxy" "proxy_http" - ] ++ optional cfg.enableSSL "ssl"; + ] ++ optional enableSSL "ssl"; loggingConf = '' - ErrorLog ${cfg.logDir}/error_log + ErrorLog ${mainCfg.logDir}/error_log LogLevel notice @@ -99,7 +106,7 @@ let LogFormat "%{Referer}i -> %U" referer LogFormat "%{User-agent}i" agent - CustomLog ${cfg.logDir}/access_log common + CustomLog ${mainCfg.logDir}/access_log common ''; @@ -116,32 +123,13 @@ let ''; - # !!! integrate with virtual hosting below sslConf = '' - SSLSessionCache dbm:${cfg.stateDir}/ssl_scache + SSLSessionCache dbm:${mainCfg.stateDir}/ssl_scache - SSLMutex file:${cfg.stateDir}/ssl_mutex + SSLMutex file:${mainCfg.stateDir}/ssl_mutex SSLRandomSeed startup builtin SSLRandomSeed connect builtin - - NameVirtualHost *:${toString cfg.httpsPort} - - - - SSLEngine on - - SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL - - SSLCertificateFile ${sslServerCert} - SSLCertificateKeyFile ${sslServerKey} - - # MSIE compatability. - SetEnvIf User-Agent ".*MSIE.*" \ - nokeepalive ssl-unclean-shutdown \ - downgrade-1.0 force-response-1.0 - - ''; @@ -197,6 +185,18 @@ let ${concatMapStrings (alias: "ServerAlias ${alias}\n") cfg.serverAliases} + ${if cfg.sslServerCert != "" then '' + SSLCertificateFile ${cfg.sslServerCert} + SSLCertificateKeyFile ${cfg.sslServerKey} + '' else ""} + + ${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 != "" then '' ServerAdmin ${cfg.adminAddr} '' else ""} @@ -210,69 +210,6 @@ let ${if isMainServer || cfg.documentRoot != null then documentRootConf else ""} - ${ - let makeDirConf = elem: '' - Alias ${elem.urlPath} ${elem.dir}/ - - Order allow,deny - Allow from all - AllowOverride None - - ''; - in concatMapStrings makeDirConf cfg.servedDirs - } - - ${ - let makeFileConf = elem: '' - Alias ${elem.urlPath} ${elem.file} - ''; - in concatMapStrings makeFileConf cfg.servedFiles - } - - ${concatMapStrings (svc: svc.extraConfig) subservices} - - ${cfg.extraConfig} - ''; - - - httpdConf = pkgs.writeText "httpd.conf" '' - - ServerRoot ${httpd} - - PidFile ${cfg.stateDir}/httpd.pid - - - MaxClients 150 - MaxRequestsPerChild 0 - - - ${let - ports = pkgs.lib.uniqList { - inputList=(concatMap (localCfg: - (pkgs.lib.optional localCfg.enableHttp localCfg.httpPort) - ++ - (pkgs.lib.optional localCfg.enableHttps localCfg.httpsPort) - ) vhosts) - ++ - (pkgs.lib.optional cfg.enableSSL cfg.httpsPort) - ++ - [cfg.httpPort]; - }; - in concatMapStrings (port: "Listen ${toString port}\n") ports - } - - User ${cfg.user} - Group ${cfg.group} - - ${let - load = {name, path}: "LoadModule ${name}_module ${path}\n"; - allModules = - concatMap (svc: svc.extraModulesPre) allSubservices ++ - map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules ++ - concatMap (svc: svc.extraModules) allSubservices; - in concatMapStrings load allModules - } - ${if cfg.enableUserDir then '' UserDir public_html @@ -293,6 +230,65 @@ let '' else ""} + ${if 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}/ + + Options +Indexes + Order allow,deny + Allow from all + AllowOverride All + + ''; + in concatMapStrings makeDirConf cfg.servedDirs + } + + ${concatMapStrings (svc: svc.extraConfig) subservices} + + ${cfg.extraConfig} + ''; + + + httpdConf = pkgs.writeText "httpd.conf" '' + + ServerRoot ${httpd} + + PidFile ${mainCfg.stateDir}/httpd.pid + + + MaxClients 150 + MaxRequestsPerChild 0 + + + ${let + ports = map getPort allHosts; + uniquePorts = pkgs.lib.uniqList {inputList = ports;}; + in concatMapStrings (port: "Listen ${toString port}\n") uniquePorts + } + + User ${mainCfg.user} + Group ${mainCfg.group} + + ${let + load = {name, path}: "LoadModule ${name}_module ${path}\n"; + allModules = + concatMap (svc: svc.extraModulesPre) allSubservices ++ + map (name: {inherit name; path = "${httpd}/modules/mod_${name}.so";}) apacheModules ++ + concatMap (svc: svc.extraModules) allSubservices; + in concatMapStrings load allModules + } + AddHandler type-map var @@ -309,7 +305,7 @@ let Include ${httpd}/conf/extra/httpd-multilang-errordoc.conf Include ${httpd}/conf/extra/httpd-languages.conf - ${if cfg.enableSSL then sslConf else ""} + ${if enableSSL then sslConf else ""} # Fascist default - deny access to everything. @@ -328,27 +324,20 @@ let # Generate directives for the main server. - ${perServerConf true cfg} + ${perServerConf true mainCfg} # Always enable virtual hosts; it doesn't seem to hurt. - NameVirtualHost *:${toString cfg.httpPort} + NameVirtualHost *:80 + NameVirtualHost *:443 ${let - makeVirtualHost = localCfg: (if localCfg.enableHttp then '' - - ${perServerConf false localCfg} + makeVirtualHost = vhost: '' + + ${perServerConf false vhost} - '' else "") + ( if localCfg.enableHttps then '' - - SSLEngine on - - SSLCertificateFile ${sslServerCert} - SSLCertificateKeyFile ${sslServerKey} - - ${perServerConf false localCfg} - - '' else ""); - in concatMapStrings makeVirtualHost vhosts} + ''; + in concatMapStrings makeVirtualHost vhosts + } ''; @@ -359,24 +348,26 @@ in name = "httpd"; users = [ - { name = cfg.user; + { name = mainCfg.user; description = "Apache httpd user"; } ]; groups = [ - { name = cfg.group; + { name = mainCfg.group; } ]; extraPath = [httpd] ++ concatMap (svc: svc.extraPath) allSubservices; # Statically verify the syntactic correctness of the generated - # httpd.conf. + # httpd.conf. !!! this is impure! It doesn't just check for + # syntax, but also whether the Apache user/group exist, whether SSL + # keys exist, etc. buildHook = '' echo echo '=== Checking the generated Apache configuration file ===' - ${httpd}/bin/httpd -f ${httpdConf} -t + ${httpd}/bin/httpd -f ${httpdConf} -t || true ''; job = '' @@ -386,13 +377,13 @@ in stop on shutdown start script - mkdir -m 0700 -p ${cfg.stateDir} - mkdir -m 0700 -p ${cfg.logDir} + mkdir -m 0700 -p ${mainCfg.stateDir} + mkdir -m 0700 -p ${mainCfg.logDir} # Get rid of old semaphores. These tend to accumulate across # server restarts, eventually preventing it from restarting # succesfully. - for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${cfg.user} ' | cut -f2 -d ' '); do + for i in $(${pkgs.utillinux}/bin/ipcs -s | grep ' ${mainCfg.user} ' | cut -f2 -d ' '); do ${pkgs.utillinux}/bin/ipcrm -s $i done @@ -410,8 +401,6 @@ in env PATH=${pkgs.coreutils}/bin:${pkgs.gnugrep}/bin:${pkgs.lib.concatStringsSep ":" (pkgs.lib.concatMap (svc: svc.extraServerPath) allSubservices)} - ${pkgs.diffutils}/bin:${pkgs.gnused}/bin - respawn ${httpd}/bin/httpd -f ${httpdConf} -DNO_DETACH ''; diff --git a/upstart-jobs/apache-httpd/per-server-options.nix b/upstart-jobs/apache-httpd/per-server-options.nix index 91516196f1f..8a2ee0e849a 100644 --- a/upstart-jobs/apache-httpd/per-server-options.nix +++ b/upstart-jobs/apache-httpd/per-server-options.nix @@ -22,31 +22,38 @@ "; }; - httpPort = mkOption { - default = 80; + port = mkOption { + default = 0; description = " - Port for unencrypted HTTP requests. + Port for the server. 0 means use the default port: 80 for http + and 443 for https (i.e. when enableSSL is set). "; }; - httpsPort = mkOption { - default = 443; - description = " - Port for encrypted HTTPS requests. - "; - }; - - enableHttp = mkOption { - default = true; - description = " - Whether to listen on unencrypted HTTP. - "; - }; - - enableHttps = mkOption { + enableSSL = mkOption { default = false; description = " - Whether to listen on encrypted HTTPS. + 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 { + default = ""; + example = "/var/host.cert"; + description = " + Path to server SSL certificate. + "; + }; + + sslServerKey = mkOption { + default = ""; + example = "/var/host.key"; + description = " + Path to server SSL certificate key. "; }; @@ -111,4 +118,21 @@ "; }; + enableUserDir = mkOption { + default = false; + description = " + Whether to enable serving ~/public_html as + /~username. + "; + }; + + globalRedirect = mkOption { + default = ""; + example = http://newserver.example.org/; + description = " + If set, all requests for this host are redirected permanently to + the given URL. + "; + }; + }