diff options
author | Parnell Springmeyer <ixmatus@users.noreply.github.com> | 2017-11-16 12:21:02 -0600 |
---|---|---|
committer | Jörg Thalheim <Mic92@users.noreply.github.com> | 2017-11-16 18:21:02 +0000 |
commit | cb11bf73a511819ee36c4bddb97573aacda0b161 (patch) | |
tree | f4256571834c529fb6fa204f3163a7b965302574 /nixos/modules/services/networking/nghttpx | |
parent | 8bd10a17c6c811b423126af1d3dc37aa82a7c131 (diff) |
nixos/nghttpx: add module for the nghttpx proxy server (#31680)
* nghttpx: Add a new NixOS module for the nghttpx proxy server
This change also adds a global `uid` and `gid` for a `nghttpx` user
and group as well as an integration test.
* nixos/nghttpx: fix building manual
Diffstat (limited to 'nixos/modules/services/networking/nghttpx')
8 files changed, 579 insertions, 0 deletions
diff --git a/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix b/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix new file mode 100644 index 000000000000..6523f4b8b9e0 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/backend-params-submodule.nix @@ -0,0 +1,131 @@ +{ lib, ...}: +{ options = { + proto = lib.mkOption { + type = lib.types.enum [ "h2" "http/1.1" ]; + default = "http/1.1"; + description = '' + This option configures the protocol the backend server expects + to use. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + tls = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + This option determines whether nghttpx will negotiate its + connection with a backend server using TLS or not. The burden + is on the backend server to provide the TLS certificate! + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + sni = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + description = '' + Override the TLS SNI field value. This value (in nghttpx) + defaults to the host value of the backend configuration. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + fall = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + If nghttpx cannot connect to the backend N times in a row, the + backend is assumed to be offline and is excluded from load + balancing. If N is 0 the backend is never excluded from load + balancing. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + rise = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + If the backend is excluded from load balancing, nghttpx will + periodically attempt to make a connection to the backend. If + the connection is successful N times in a row the backend is + re-included in load balancing. If N is 0 a backend is never + reconsidered for load balancing once it falls. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + affinity = lib.mkOption { + type = lib.types.enum [ "ip" "none" ]; + default = "none"; + description = '' + If "ip" is given, client IP based session affinity is + enabled. If "none" is given, session affinity is disabled. + + Session affinity is enabled (by nghttpx) per-backend + pattern. If at least one backend has a non-"none" affinity, + then session affinity is enabled for all backend servers + sharing the same pattern. + + It is advised to set affinity on all backends explicitly if + session affinity is desired. The session affinity may break if + one of the backend gets unreachable, or backend settings are + reloaded or replaced by API. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + dns = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Name resolution of a backends host name is done at start up, + or configuration reload. If "dns" is true, name resolution + takes place dynamically. + + This is useful if a backends address changes frequently. If + "dns" is true, name resolution of a backend's host name at + start up, or configuration reload is skipped. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + + redirect-if-not-tls = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + If true, a backend match requires the frontend connection be + TLS encrypted. If it is not, nghttpx responds to the request + with a 308 status code and https URI the client should use + instead in the Location header. + + The port number in the redirect URI is 443 by default and can + be changed using 'services.nghttpx.redirect-https-port' + option. + + If at least one backend has "redirect-if-not-tls" set to true, + this feature is enabled for all backend servers with the same + pattern. It is advised to set "redirect-if-no-tls" parameter + to all backends explicitly if this feature is desired. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more detail. + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/backend-submodule.nix b/nixos/modules/services/networking/nghttpx/backend-submodule.nix new file mode 100644 index 000000000000..eb559e926e76 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/backend-submodule.nix @@ -0,0 +1,50 @@ +{ lib, ... }: +{ options = { + server = lib.mkOption { + type = + lib.types.either + (lib.types.submodule (import ./server-options.nix)) + (lib.types.path); + example = { + host = "127.0.0.1"; + port = 8888; + }; + default = { + host = "127.0.0.1"; + port = 80; + }; + description = '' + Backend server location specified as either a host:port pair + or a unix domain docket. + ''; + }; + + patterns = lib.mkOption { + type = lib.types.listOf lib.types.str; + example = [ + "*.host.net/v1/" + "host.org/v2/mypath" + "/somepath" + ]; + default = []; + description = '' + List of nghttpx backend patterns. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-b + for more information on the pattern syntax and nghttpxs behavior. + ''; + }; + + params = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./backend-params-submodule.nix)); + example = { + proto = "h2"; + tls = true; + }; + default = null; + description = '' + Parameters to configure a backend. + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/default.nix b/nixos/modules/services/networking/nghttpx/default.nix new file mode 100644 index 000000000000..d6e1906e3881 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/default.nix @@ -0,0 +1,117 @@ +{config, pkgs, lib, ...}: +let + cfg = config.services.nghttpx; + + # renderHost :: Either ServerOptions Path -> String + renderHost = server: + if builtins.isString server + then "unix://${server}" + else "${server.host},${builtins.toString server.port}"; + + # Filter out submodule parameters whose value is null or false or is + # the key _module. + # + # filterParams :: ParamsSubmodule -> ParamsSubmodule + filterParams = p: + lib.filterAttrs + (n: v: ("_module" != n) && (null != v) && (false != v)) + (lib.optionalAttrs (null != p) p); + + # renderBackend :: BackendSubmodule -> String + renderBackend = backend: + let + host = renderHost backend.server; + patterns = lib.concatStringsSep ":" backend.patterns; + + # Render a set of backend parameters, this is somewhat + # complicated because nghttpx backend patterns can be entirely + # omitted and the params may be given as a mixed collection of + # 'key=val' pairs or atoms (e.g: 'proto=h2;tls') + params = + lib.mapAttrsToList + (n: v: + if builtins.isBool v + then n + else if builtins.isString v + then "${n}=${v}" + else "${n}=${builtins.toString v}") + (filterParams backend.params); + + # NB: params are delimited by a ";" which is the same delimiter + # to separate the host;[pattern];[params] sections of a backend + sections = + builtins.filter (e: "" != e) ([ + host + patterns + ]++params); + formattedSections = lib.concatStringsSep ";" sections; + in + "backend=${formattedSections}"; + + # renderFrontend :: FrontendSubmodule -> String + renderFrontend = frontend: + let + host = renderHost frontend.server; + params0 = + lib.mapAttrsToList + (n: v: if builtins.isBool v then n else v) + (filterParams frontend.params); + + # NB: nghttpx doesn't accept "tls", you must omit "no-tls" for + # the default behavior of turning on TLS. + params1 = lib.remove "tls" params0; + + sections = [ host] ++ params1; + formattedSections = lib.concatStringsSep ";" sections; + in + "frontend=${formattedSections}"; + + configurationFile = pkgs.writeText "nghttpx.conf" '' + ${lib.optionalString (null != cfg.tls) ("private-key-file="+cfg.tls.key)} + ${lib.optionalString (null != cfg.tls) ("certificate-file="+cfg.tls.crt)} + + user=nghttpx + + ${lib.concatMapStringsSep "\n" renderFrontend cfg.frontends} + ${lib.concatMapStringsSep "\n" renderBackend cfg.backends} + + backlog=${builtins.toString cfg.backlog} + backend-address-family=${cfg.backend-address-family} + + workers=${builtins.toString cfg.workers} + rlimit-nofile=${builtins.toString cfg.rlimit-nofile} + + ${lib.optionalString cfg.single-thread "single-thread=yes"} + ${lib.optionalString cfg.single-process "single-process=yes"} + + ${cfg.extraConfig} + ''; +in +{ imports = [ + ./nghttpx-options.nix + ]; + + config = lib.mkIf cfg.enable { + + users.groups.nghttpx = { }; + users.users.nghttpx = { + group = config.users.groups.nghttpx.name; + }; + + + systemd.services = { + nghttpx = { + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + script = '' + ${pkgs.nghttp2}/bin/nghttpx --conf=${configurationFile} + ''; + + serviceConfig = { + Restart = "on-failure"; + RestartSec = 60; + }; + }; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix b/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix new file mode 100644 index 000000000000..33c8572bd14f --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/frontend-params-submodule.nix @@ -0,0 +1,64 @@ +{ lib, ...}: +{ options = { + tls = lib.mkOption { + type = lib.types.enum [ "tls" "no-tls" ]; + default = "tls"; + description = '' + Enable or disable TLS. If true (enabled) the key and + certificate must be configured for nghttpx. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + sni-fwd = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + When performing a match to select a backend server, SNI host + name received from the client is used instead of the request + host. See --backend option about the pattern match. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + api = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Enable API access for this frontend. This enables you to + dynamically modify nghttpx at run-time therefore this feature + is disabled by default and should be turned on with care. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + healthmon = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Make this frontend a health monitor endpoint. Any request + received on this frontend is responded to with a 200 OK. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + + proxyproto = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Accept PROXY protocol version 1 on frontend connection. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-f + for more detail. + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/frontend-submodule.nix b/nixos/modules/services/networking/nghttpx/frontend-submodule.nix new file mode 100644 index 000000000000..887ef4502131 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/frontend-submodule.nix @@ -0,0 +1,36 @@ +{ lib, ... }: +{ options = { + server = lib.mkOption { + type = + lib.types.either + (lib.types.submodule (import ./server-options.nix)) + (lib.types.path); + example = { + host = "127.0.0.1"; + port = 8888; + }; + default = { + host = "127.0.0.1"; + port = 80; + }; + description = '' + Frontend server interface binding specification as either a + host:port pair or a unix domain docket. + + NB: a host of "*" listens on all interfaces and includes IPv6 + addresses. + ''; + }; + + params = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./frontend-params-submodule.nix)); + example = { + tls = "tls"; + }; + default = null; + description = '' + Parameters to configure a backend. + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/nghttpx-options.nix b/nixos/modules/services/networking/nghttpx/nghttpx-options.nix new file mode 100644 index 000000000000..cce65be321a8 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/nghttpx-options.nix @@ -0,0 +1,142 @@ +{ config, lib, ... }: +{ options.services.nghttpx = { + enable = lib.mkEnableOption "nghttpx"; + + frontends = lib.mkOption { + type = lib.types.listOf (lib.types.submodule (import ./frontend-submodule.nix)); + description = '' + A list of frontend listener specifications. + ''; + example = [ + { server = { + host = "*"; + port = 80; + }; + + params = { + tls = "no-tls"; + }; + } + ]; + }; + + backends = lib.mkOption { + type = lib.types.listOf (lib.types.submodule (import ./backend-submodule.nix)); + description = '' + A list of backend specifications. + ''; + example = [ + { server = { + host = "172.16.0.22"; + port = 8443; + }; + patterns = [ "/" ]; + params = { + proto = "http/1.1"; + redirect-if-not-tls = true; + }; + } + ]; + }; + + tls = lib.mkOption { + type = lib.types.nullOr (lib.types.submodule (import ./tls-submodule.nix)); + default = null; + description = '' + TLS certificate and key paths. Note that this does not enable + TLS for a frontend listener, to do so, a frontend + specification must set <literal>params.tls</literal> to true. + ''; + example = { + key = "/etc/ssl/keys/server.key"; + crt = "/etc/ssl/certs/server.crt"; + }; + }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + description = '' + Extra configuration options to be appended to the generated + configuration file. + ''; + }; + + single-process = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run this program in a single process mode for debugging + purpose. Without this option, nghttpx creates at least 2 + processes: master and worker processes. If this option is + used, master and worker are unified into a single + process. nghttpx still spawns additional process if neverbleed + is used. In the single process mode, the signal handling + feature is disabled. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-process + ''; + }; + + backlog = lib.mkOption { + type = lib.types.int; + default = 65536; + description = '' + Listen backlog size. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backlog + ''; + }; + + backend-address-family = lib.mkOption { + type = lib.types.enum [ + "auto" + "IPv4" + "IPv6" + ]; + default = "auto"; + description = '' + Specify address family of backend connections. If "auto" is + given, both IPv4 and IPv6 are considered. If "IPv4" is given, + only IPv4 address is considered. If "IPv6" is given, only IPv6 + address is considered. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--backend-address-family + ''; + }; + + workers = lib.mkOption { + type = lib.types.int; + default = 1; + description = '' + Set the number of worker threads. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx-n + ''; + }; + + single-thread = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Run everything in one thread inside the worker process. This + feature is provided for better debugging experience, or for + the platforms which lack thread support. If threading is + disabled, this option is always enabled. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--single-thread + ''; + }; + + rlimit-nofile = lib.mkOption { + type = lib.types.int; + default = 0; + description = '' + Set maximum number of open files (RLIMIT_NOFILE) to <N>. If 0 + is given, nghttpx does not set the limit. + + Please see https://nghttp2.org/documentation/nghttpx.1.html#cmdoption-nghttpx--rlimit-nofile + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/server-options.nix b/nixos/modules/services/networking/nghttpx/server-options.nix new file mode 100644 index 000000000000..ef23bfd793c5 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/server-options.nix @@ -0,0 +1,18 @@ +{ lib, ... }: +{ options = { + host = lib.mkOption { + type = lib.types.str; + example = "127.0.0.1"; + description = '' + Server host address. + ''; + }; + port = lib.mkOption { + type = lib.types.int; + example = 5088; + description = '' + Server host port. + ''; + }; + }; +} diff --git a/nixos/modules/services/networking/nghttpx/tls-submodule.nix b/nixos/modules/services/networking/nghttpx/tls-submodule.nix new file mode 100644 index 000000000000..8f3cdaae2c81 --- /dev/null +++ b/nixos/modules/services/networking/nghttpx/tls-submodule.nix @@ -0,0 +1,21 @@ +{lib, ...}: +{ options = { + key = lib.mkOption { + type = lib.types.str; + example = "/etc/ssl/keys/mykeyfile.key"; + default = "/etc/ssl/keys/server.key"; + description = '' + Path to the TLS key file. + ''; + }; + + crt = lib.mkOption { + type = lib.types.str; + example = "/etc/ssl/certs/mycert.crt"; + default = "/etc/ssl/certs/server.crt"; + description = '' + Path to the TLS certificate file. + ''; + }; + }; +} |