nixpkgs-suyu/nixos/modules/services/web-servers/nginx/reverse_proxy.nix

233 lines
7.4 KiB
Nix

{ 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);
};
}