From f298be9ef44657fdd267f7c296b40d4c97c9c1fc Mon Sep 17 00:00:00 2001 From: Robin Gloster Date: Sun, 24 Jan 2016 15:50:54 +0000 Subject: [PATCH] nginx module: declarative config --- .../services/web-servers/nginx/default.nix | 113 +++++++++++++++++- .../web-servers/nginx/location-options.nix | 32 +++++ .../web-servers/nginx/vhost-options.nix | 96 +++++++++++++++ 3 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 nixos/modules/services/web-servers/nginx/location-options.nix create mode 100644 nixos/modules/services/web-servers/nginx/vhost-options.nix diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index 27a33f33ff93..ab5657a9aa92 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -7,18 +7,107 @@ let nginx = cfg.package; configFile = pkgs.writeText "nginx.conf" '' user ${cfg.user} ${cfg.group}; + error_log stderr; daemon off; ${cfg.config} - ${optionalString (cfg.httpConfig != "") '' http { include ${cfg.package}/conf/mime.types; + include ${cfg.package}/conf/fastcgi.conf; + + # optimisation + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # use secure TLS defaults + ssl_protocols TLSv1.2; + ssl_session_cache shared:SSL:42m; + ssl_session_timeout 23m; + + ssl_ciphers EDH+aRSA+AES256:+AESGCM:ECDHE+aRSA+AES256; + ssl_ecdh_curve secp521r1; + ssl_prefer_server_ciphers on; + + ssl_stapling on; + ssl_stapling_verify on; + + gzip on; + gzip_disable "msie6"; + gzip_proxied any; + gzip_comp_level 9; + gzip_buffers 16 8k; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # sane proxy settings/headers + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Accept-Encoding ""; + + proxy_redirect off; + client_max_body_size 10m; + client_body_buffer_size 128k; + proxy_connect_timeout 90; + proxy_send_timeout 90; + proxy_read_timeout 90; + proxy_buffers 32 4k; + proxy_buffer_size 8k; + proxy_http_version 1.0; + + server_tokens ${if cfg.serverTokens then "on" else "off"}; + ${vhosts} ${cfg.httpConfig} } - ''} ${cfg.appendConfig} ''; + + vhosts = concatStringsSep "\n" (mapAttrsToList (serverName: vhost: + let + ssl = vhost.enableSSL || vhost.forceSSL; + port = if vhost.port != null then vhost.port else (if ssl then 443 else 80); + listenString = toString port + optionalString ssl " ssl spdy"; + in '' + ${if vhost.forceSSL then '' + server { + listen 80; + listen [::]:80; + + server_name ${serverName} ${concatStringsSep " " vhost.serverAliases}; + return 301 https://$host${optionalString (port != 443) ":${port}"}$request_uri; + } + '' else ""} + + server { + listen ${listenString}; + listen [::]:${listenString}; + + server_name ${serverName} ${concatStringsSep " " vhost.serverAliases}; + ${optionalString (vhost.root != null) "root ${vhost.root};"} + ${optionalString (vhost.globalRedirect != null) '' + return 301 https://${vhost.globalRedirect}$request_uri; + ''} + ${optionalString ssl '' + ssl_certificate ${vhost.sslCertificate}; + ssl_certificate_key ${vhost.sslCertificateKey}; + ''} + + ${genLocations vhost.locations} + + ${vhost.extraConfig} + } + '' + ) cfg.virtualHosts); + genLocations = locations: concatStringsSep "\n" (mapAttrsToList (location: config: '' + location ${location} { + ${optionalString (config.proxyPass != null) "proxy_pass ${config.proxyPass};"} + ${optionalString (config.root != null) "root ${config.root};"} + } + '') locations); in { @@ -86,8 +175,24 @@ in description = "Group account under which nginx runs."; }; - }; + serverTokens = mkOption { + type = types.bool; + default = false; + description = "Show nginx version in headers and error pages"; + }; + virtualHosts = mkOption { + type = types.attrsOf (types.submodule (import ./vhost-options.nix { + inherit lib; + })); + default = { + localhost = {}; + }; + example = []; + description = '' + ''; + }; + }; }; config = mkIf cfg.enable { @@ -107,7 +212,7 @@ in serviceConfig = { ExecStart = "${nginx}/bin/nginx -c ${configFile} -p ${cfg.stateDir}"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - Restart = "on-failure"; + Restart = "always"; RestartSec = "10s"; StartLimitInterval = "1min"; }; diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix new file mode 100644 index 000000000000..6537e45a459d --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/location-options.nix @@ -0,0 +1,32 @@ +# 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.) + +{ lib }: + +with lib; + +{ + options = { + proxyPass = mkOption { + type = types.nullOr types.str; + default = null; + example = "http://www.example.org/"; + description = '' + Adds proxy_pass directive and sets default proxy headers Host, X-Real-Ip + and X-Forwarded-For. + ''; + }; + + root = mkOption { + type = types.nullOr types.path; + default = null; + example = /your/root/directory; + description = '' + Root directory for requests. + ''; + }; + }; +} + diff --git a/nixos/modules/services/web-servers/nginx/vhost-options.nix b/nixos/modules/services/web-servers/nginx/vhost-options.nix new file mode 100644 index 000000000000..2acb3743677d --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/vhost-options.nix @@ -0,0 +1,96 @@ +# 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.) + +{ lib }: + +with lib; +{ + options = { + serverAliases = mkOption { + type = types.listOf types.str; + default = []; + example = ["www.example.org" "example.org"]; + description = '' + Additional names of virtual hosts served by this virtual host configuration. + ''; + }; + + port = mkOption { + type = types.nullOr types.int; + default = null; + description = '' + Port for the server. 80 for http + and 443 for https (i.e. when enableSSL is set). + ''; + }; + + enableSSL = mkOption { + type = types.bool; + default = false; + description = "Whether to enable SSL (https) support."; + }; + + forceSSL = mkOption { + type = types.bool; + default = false; + description = "Whether to always redirect to https."; + }; + + sslCertificate = mkOption { + type = types.path; + example = "/var/host.cert"; + description = "Path to server SSL certificate."; + }; + + sslCertificateKey= mkOption { + type = types.path; + example = "/var/host.key"; + description = "Path to server SSL certificate key."; + }; + + root = mkOption { + type = types.nullOr types.path; + default = null; + example = "/data/webserver/docs"; + description = '' + The path of the web root directory. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + These lines go to the end of the vhost verbatim. + ''; + }; + + 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. + ''; + }; + + basicAuth = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "user = password"; + }; + + locations = mkOption { + type = types.attrsOf (types.submodule (import ./location-options.nix { + inherit lib; + })); + default = {}; + example = {}; + description = '' + ''; + }; + }; +}