diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8ee54ce7ef4b..6734fa0b862b 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -393,6 +393,7 @@ ./services/web-servers/lighttpd/default.nix ./services/web-servers/lighttpd/gitweb.nix ./services/web-servers/nginx/default.nix + ./services/web-servers/nginx/reverse_proxy.nix ./services/web-servers/phpfpm.nix ./services/web-servers/shellinabox.nix ./services/web-servers/tomcat.nix diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index fe50c182bfe5..b16f701a0c9f 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -11,7 +11,9 @@ let ${cfg.config} ${optionalString (cfg.httpConfig != "") '' http { - ${cfg.httpConfig} + ${cfg.httpConfig} + ${cfg.httpServers} + ${cfg.httpDefaultServer} } ''} ${cfg.appendConfig} @@ -60,7 +62,32 @@ in httpConfig = mkOption { type = types.lines; default = ""; - description = "Configuration lines to be appended inside of the http {} block."; + description = '' + Configuration lines to be placed at the top inside of + the http {} block. The option is intended to be used for + the default configuration of the servers. + ''; + }; + + httpServers = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to be placed inside of the http {} + block. The option is intended to be used for defining + individual servers. + ''; + }; + + httpDefaultServer = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to be placed at the bottom inside of + the http {} block. The option is intended to be used for + setting up the default servers. The default server is used + if no previously specified server matches a request. + ''; }; stateDir = mkOption { diff --git a/nixos/modules/services/web-servers/nginx/reverse_proxy.nix b/nixos/modules/services/web-servers/nginx/reverse_proxy.nix new file mode 100644 index 000000000000..c21406dff29a --- /dev/null +++ b/nixos/modules/services/web-servers/nginx/reverse_proxy.nix @@ -0,0 +1,233 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.services.nginx; + + defaultSSL = cfg.httpDefaultKey != null || cfg.httpDefaultCertificate != null; + + validSSL = key: cert: cert != null && key != null || cert == null && key == null; + +in + +{ + options = { + + services.nginx = { + + reverseProxies = mkOption { + type = types.attrsOf (types.submodule ( + { + options = { + proxy = mkOption { + type = types.str; + default = []; + description = '' + Exclude files and directories matching these patterns. + ''; + }; + + key = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Exclude files and directories matching these patterns. + ''; + }; + + certificate = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Exclude files and directories matching these patterns. + ''; + }; + }; + } + )); + + default = {}; + + example = literalExample '' + { + "hydra.yourdomain.org" = + { proxy = "localhost:3000"; + key = "/etc/nixos/certs/hydra_key.key"; + certificate = "/etc/nixos/certs/hydra_cert.crt"; + }; + } + ''; + + description = '' + A reverse proxy server configuration is created for every attribute. + The attribute name corresponds to the name the server is listening to, + and the proxy option defines the target to forward the requests to. + If a key and certificate are given, then the server is secured through + a SSL connection. Non-SSL requests on port 80 are automatically + re-directed to the SSL server on port 443. + ''; + }; + + httpDefaultKey = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/nixos/certs/defaut_key.key"; + description = '' + Key of SSL certificate for default server. + The default certificate is presented by the default server during + the SSL handshake when no specialized server configuration matches + a request. + A default SSL certificate is also helpful if browsers do not + support the TLS Server Name Indication extension (SNI, RFC 6066). + ''; + }; + + httpDefaultCertificate = mkOption { + type = types.nullOr types.path; + default = null; + example = "/etc/nixos/certs/defaut_key.crt"; + description = '' + SSL certificate for default server. + The default certificate is presented by the default server during + the SSL handshake when no specialized server configuration matches + a request. + A default SSL certificate is also helpful if browsers do not + support the TLS Server Name Indication extension (SNI, RFC 6066). + ''; + }; + + }; + + }; + + + config = mkIf (cfg.reverseProxies != {}) { + + assertions = [ + { assertion = all id (mapAttrsToList (n: v: validSSL v.certificate v.key) cfg.reverseProxies); + message = '' + One (or more) reverse proxy configurations specify only either + the key option or the certificate option. Both certificate + with associated key have to be configured to enable SSL for a + server configuration. + + services.nginx.reverseProxies: ${toString cfg.reverseProxies} + ''; + } + { assertion = validSSL cfg.httpDefaultCertificate cfg.httpDefaultKey; + message = '' + The default server configuration specifies only either the key + option or the certificate option. Both httpDefaultCertificate + with associated httpDefaultKey have to be configured to enable + SSL for the default server configuration. + + services.nginx.httpDefaultCertificate: ${toString cfg.httpDefaultCertificate} + + services.nginx.httpDefaultKey : ${toString cfg.httpDefaultKey} + ''; + } + ]; + + services.nginx.config = mkBefore '' + worker_processes 1; + error_log logs/error.log debug; + pid logs/nginx.pid; + events { + worker_connections 1024; + } + ''; + + services.nginx.httpConfig = mkBefore '' + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + access_log logs/access.log main; + sendfile on; + tcp_nopush on; + keepalive_timeout 10; + gzip on; + + ${lib.optionalString defaultSSL '' + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_ciphers HIGH:!aNULL:!MD5; + ssl_certificate ${cfg.httpDefaultCertificate}; + ssl_certificate_key ${cfg.httpDefaultKey}; + ''} + ''; + + services.nginx.httpDefaultServer = mkBefore '' + # reject as default policy + server { + listen 80 default_server; + listen [::]:80 default_server; + ${lib.optionalString defaultSSL "listen 443 default_server ssl;"} + return 444; + } + ''; + + services.nginx.httpServers = + let + useSSL = certificate: key: certificate != null && key != null; + + server = servername: proxy: certificate: key: useSSL: '' + server { + server_name ${servername}; + keepalive_timeout 70; + + ${if !useSSL then '' + listen 80; + listen [::]:80; + '' else '' + listen 443 ssl; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_certificate ${certificate}; + ssl_certificate_key ${key}; + ''} + + location / { + proxy_pass ${proxy}; + + ### force timeouts if one of backend is dead ## + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + + ### Set headers #### + proxy_set_header Accept-Encoding ""; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + ${lib.optionalString useSSL '' + ### Most PHP, Python, Rails, Java App can use this header ### + #proxy_set_header X-Forwarded-Proto https;## + #This is better## + proxy_set_header X-Forwarded-Proto $scheme; + add_header Front-End-Https on; + ''} + + ### By default we don't want to redirect it #### + proxy_redirect off; + proxy_buffering off; + } + } + + ${lib.optionalString useSSL '' + # redirect http to https + server { + listen 80; + listen [::]:80; + server_name ${servername}; + return 301 https://$server_name$request_uri; + } + ''} + ''; + in + concatStrings (mapAttrsToList (n: v: server n v.proxy v.certificate v.key (useSSL v.proxy v.certificate)) cfg.reverseProxies); + + }; + +}