From 3728338d4039a925c1c131a4f305d32c04cc3657 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Tue, 25 Jul 2023 08:19:43 +0200 Subject: nixos/sslh: refactor for RFC42 --- nixos/modules/services/networking/sslh.nix | 181 ++++++++++++++++++----------- nixos/tests/sslh.nix | 18 +-- 2 files changed, 121 insertions(+), 78 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/services/networking/sslh.nix b/nixos/modules/services/networking/sslh.nix index daf2f2f3668ee..820c72924582a 100644 --- a/nixos/modules/services/networking/sslh.nix +++ b/nixos/modules/services/networking/sslh.nix @@ -5,81 +5,123 @@ with lib; let cfg = config.services.sslh; user = "sslh"; - configFile = pkgs.writeText "sslh.conf" '' - verbose: ${boolToString cfg.verbose}; - foreground: true; - inetd: false; - numeric: false; - transparent: ${boolToString cfg.transparent}; - timeout: "${toString cfg.timeout}"; - - listen: - ( - ${ - concatMapStringsSep ",\n" - (addr: ''{ host: "${addr}"; port: "${toString cfg.port}"; }'') - cfg.listenAddresses - } - ); - - ${cfg.appendConfig} - ''; - defaultAppendConfig = '' - protocols: - ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, - { name: "xmpp"; host: "localhost"; port: "5222"; probe: "builtin"; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - { name: "tls"; host: "localhost"; port: "443"; probe: "builtin"; }, - { name: "anyprot"; host: "localhost"; port: "443"; probe: "builtin"; } - ); - ''; + + configFormat = pkgs.formats.libconfig {}; + configFile = configFormat.generate "sslh.conf" cfg.settings; in + { imports = [ (mkRenamedOptionModule [ "services" "sslh" "listenAddress" ] [ "services" "sslh" "listenAddresses" ]) + (mkRenamedOptionModule [ "services" "sslh" "timeout" ] [ "services" "sslh" "settings" "timeout" ]) + (mkRenamedOptionModule [ "services" "sslh" "transparent" ] [ "services" "sslh" "settings" "transparent" ]) + (mkRemovedOptionModule [ "services" "sslh" "appendConfig" ] "Use services.sslh.settings instead") + (mkChangedOptionModule [ "services" "sslh" "verbose" ] [ "services" "sslh" "settings" "verbose" ] + (verbose: if verbose then 1 else 0)) ]; - options = { - services.sslh = { - enable = mkEnableOption (lib.mdDoc "sslh"); + meta.buildDocsInSandbox = false; - verbose = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Verbose logs."; - }; + options.services.sslh = { + enable = mkEnableOption (lib.mdDoc "sslh, protocol demultiplexer"); - timeout = mkOption { - type = types.int; - default = 2; - description = lib.mdDoc "Timeout in seconds."; - }; + method = mkOption { + type = types.enum [ "fork" "select" ]; + default = "fork"; + description = lib.mdDoc '' + The method to use for handling connections: - transparent = mkOption { - type = types.bool; - default = false; - description = lib.mdDoc "Will the services behind sslh (Apache, sshd and so on) see the external IP and ports as if the external world connected directly to them"; - }; + - `fork` forks a new process for each incoming connection. It is + well-tested and very reliable, but incurs the overhead of many + processes. - listenAddresses = mkOption { - type = types.coercedTo types.str singleton (types.listOf types.str); - default = [ "0.0.0.0" "[::]" ]; - description = lib.mdDoc "Listening addresses or hostnames."; - }; + - `select` uses only one thread, which monitors all connections at once. + It has lower overhead per connection, but if it stops, you'll lose all + connections. + ''; + }; - port = mkOption { - type = types.port; - default = 443; - description = lib.mdDoc "Listening port."; - }; + listenAddresses = mkOption { + type = with types; coercedTo str singleton (listOf str); + default = [ "0.0.0.0" "[::]" ]; + description = lib.mdDoc "Listening addresses or hostnames."; + }; + + port = mkOption { + type = types.port; + default = 443; + description = lib.mdDoc "Listening port."; + }; + + settings = mkOption { + type = types.submodule { + freeformType = configFormat.type; + + options.verbose = mkOption { + type = types.int; + default = 0; + example = 3; + description = lib.mdDoc '' + Logging verbosity: higher values for more information. + ''; + }; + + options.timeout = mkOption { + type = types.ints.unsigned; + default = 2; + description = lib.mdDoc "Timeout in seconds."; + }; - appendConfig = mkOption { - type = types.str; - default = defaultAppendConfig; - description = lib.mdDoc "Verbatim configuration file."; + options.transparent = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Whether the services behind sslh (Apache, sshd and so on) will see the + external IP and ports as if the external world connected directly to + them. + ''; + }; + + options.numeric = mkOption { + type = types.bool; + default = true; + description = lib.mdDoc '' + Whether to disable reverse DNS lookups, thus keeping IP + address literals in the log. + ''; + }; + + options.protocols = mkOption { + type = types.listOf configFormat.type; + default = [ + { name = "ssh"; host = "localhost"; port = "22"; service= "ssh"; } + { name = "openvpn"; host = "localhost"; port = "1194"; } + { name = "xmpp"; host = "localhost"; port = "5222"; } + { name = "http"; host = "localhost"; port = "80"; } + { name = "tls"; host = "localhost"; port = "443"; } + { name = "anyprot"; host = "localhost"; port = "443"; } + ]; + description = lib.mdDoc '' + List of protocols sslh will probe for and redirect. + Each protocol entry consists of: + + - `name`: name of the probe. + + - `service`: libwrap service name (see {manpage}`hosts_access(5)`), + + - `host`, `port`: where to connect when this probe succeeds, + + - `log_level`: to log incoming connections, + + - `transparent`: proxy this protocol transparently, + + - etc. + + See the documentation for all options, including probe-specific ones. + ''; + }; }; + description = lib.mdDoc "sslh configuration. See {manpage}`sslh(8)` for available settings."; }; }; @@ -96,20 +138,29 @@ in PermissionsStartOnly = true; Restart = "always"; RestartSec = "1s"; - ExecStart = "${pkgs.sslh}/bin/sslh -F${configFile}"; + ExecStart = "${pkgs.sslh}/bin/sslh-${cfg.method} -F${configFile}"; KillMode = "process"; - AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_NET_ADMIN CAP_SETGID CAP_SETUID"; + AmbientCapabilities = ["CAP_NET_BIND_SERVICE" "CAP_NET_ADMIN" "CAP_SETGID" "CAP_SETUID"]; PrivateTmp = true; PrivateDevices = true; ProtectSystem = "full"; ProtectHome = true; }; }; + + services.sslh.settings = { + # Settings defined here are not supposed to be changed: doing so will + # break the module, as such you need `lib.mkForce` to override them. + foreground = true; + inetd = false; + listen = map (addr: { host = addr; port = toString cfg.port; }) cfg.listenAddresses; + }; + }) # code from https://github.com/yrutschle/sslh#transparent-proxy-support # the only difference is using iptables mark 0x2 instead of 0x1 to avoid conflicts with nixos/nat module - (mkIf (cfg.enable && cfg.transparent) { + (mkIf (cfg.enable && cfg.settings.transparent) { # Set route_localnet = 1 on all interfaces so that ssl can use "localhost" as destination boot.kernel.sysctl."net.ipv4.conf.default.route_localnet" = 1; boot.kernel.sysctl."net.ipv4.conf.all.route_localnet" = 1; diff --git a/nixos/tests/sslh.nix b/nixos/tests/sslh.nix index 17094606e8e6b..30ffd389d4422 100644 --- a/nixos/tests/sslh.nix +++ b/nixos/tests/sslh.nix @@ -10,21 +10,13 @@ import ./make-test-python.nix { prefixLength = 64; } ]; - # sslh is really slow when reverse dns does not work - networking.hosts = { - "fe00:aa:bb:cc::2" = [ "server" ]; - "fe00:aa:bb:cc::1" = [ "client" ]; - }; services.sslh = { enable = true; - transparent = true; - appendConfig = '' - protocols: - ( - { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, - { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, - ); - ''; + settings.transparent = true; + settings.protocols = [ + { name = "ssh"; service = "ssh"; host = "localhost"; port = "22"; probe = "builtin"; } + { name = "http"; host = "localhost"; port = "80"; probe = "builtin"; } + ]; }; services.openssh.enable = true; users.users.root.openssh.authorizedKeys.keyFiles = [ ./initrd-network-ssh/id_ed25519.pub ]; -- cgit 1.4.1 From 7ecac99da7567f4f0e60c1c97678e85b997ffd89 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Thu, 27 Jul 2023 23:41:08 +0200 Subject: sslh: 1.22c -> 2.0.0 --- nixos/modules/services/networking/sslh.nix | 32 +++++++++++++++++++----------- pkgs/servers/sslh/default.nix | 9 +++++---- 2 files changed, 25 insertions(+), 16 deletions(-) (limited to 'nixos') diff --git a/nixos/modules/services/networking/sslh.nix b/nixos/modules/services/networking/sslh.nix index 820c72924582a..dd29db510020a 100644 --- a/nixos/modules/services/networking/sslh.nix +++ b/nixos/modules/services/networking/sslh.nix @@ -16,8 +16,8 @@ in (mkRenamedOptionModule [ "services" "sslh" "timeout" ] [ "services" "sslh" "settings" "timeout" ]) (mkRenamedOptionModule [ "services" "sslh" "transparent" ] [ "services" "sslh" "settings" "transparent" ]) (mkRemovedOptionModule [ "services" "sslh" "appendConfig" ] "Use services.sslh.settings instead") - (mkChangedOptionModule [ "services" "sslh" "verbose" ] [ "services" "sslh" "settings" "verbose" ] - (verbose: if verbose then 1 else 0)) + (mkChangedOptionModule [ "services" "sslh" "verbose" ] [ "services" "sslh" "settings" "verbose-connections" ] + (config: if config.services.sslh.verbose then 1 else 0)) ]; meta.buildDocsInSandbox = false; @@ -26,7 +26,7 @@ in enable = mkEnableOption (lib.mdDoc "sslh, protocol demultiplexer"); method = mkOption { - type = types.enum [ "fork" "select" ]; + type = types.enum [ "fork" "select" "ev" ]; default = "fork"; description = lib.mdDoc '' The method to use for handling connections: @@ -38,6 +38,9 @@ in - `select` uses only one thread, which monitors all connections at once. It has lower overhead per connection, but if it stops, you'll lose all connections. + + - `ev` is implemented using libev, it's similar to `select` but + scales better to a large number of connections. ''; }; @@ -57,15 +60,6 @@ in type = types.submodule { freeformType = configFormat.type; - options.verbose = mkOption { - type = types.int; - default = 0; - example = 3; - description = lib.mdDoc '' - Logging verbosity: higher values for more information. - ''; - }; - options.timeout = mkOption { type = types.ints.unsigned; default = 2; @@ -82,6 +76,20 @@ in ''; }; + options.verbose-connections = mkOption { + type = types.ints.between 0 4; + default = 0; + description = lib.mdDoc '' + Where to log connections information. Possible values are: + + 0. don't log anything + 1. write log to stdout + 2. write log to syslog + 3. write log to both stdout and syslog + 4. write to a log file ({option}`sslh.settings.logfile`) + ''; + }; + options.numeric = mkOption { type = types.bool; default = true; diff --git a/pkgs/servers/sslh/default.nix b/pkgs/servers/sslh/default.nix index 25e40294747cc..97ec06306fc4a 100644 --- a/pkgs/servers/sslh/default.nix +++ b/pkgs/servers/sslh/default.nix @@ -1,19 +1,19 @@ -{ lib, stdenv, fetchFromGitHub, libcap, libconfig, perl, tcp_wrappers, pcre2, nixosTests }: +{ lib, stdenv, fetchFromGitHub, fetchpatch, libcap, libev, libconfig, perl, tcp_wrappers, pcre2, nixosTests }: stdenv.mkDerivation rec { pname = "sslh"; - version = "1.22c"; + version = "2.0.0"; src = fetchFromGitHub { owner = "yrutschle"; repo = pname; rev = "v${version}"; - sha256 = "sha256-A+nUWiOPoz/T5afZUzt5In01e049TgHisTF8P5Vj180="; + hash = "sha256-KfNQWSmAf86AFoInKlNZoiSuSwVLaJVnfo7SjZVY/VU="; }; postPatch = "patchShebangs *.sh"; - buildInputs = [ libcap libconfig perl tcp_wrappers pcre2 ]; + buildInputs = [ libcap libev libconfig perl tcp_wrappers pcre2 ]; makeFlags = [ "USELIBCAP=1" "USELIBWRAP=1" ]; @@ -21,6 +21,7 @@ stdenv.mkDerivation rec { # install all flavours install -p sslh-fork "$out/sbin/sslh-fork" install -p sslh-select "$out/sbin/sslh-select" + install -p sslh-ev "$out/sbin/sslh-ev" ln -sf sslh-fork "$out/sbin/sslh" ''; -- cgit 1.4.1 From fc4691cd06adeb4bebb00fa7137e135055a5e491 Mon Sep 17 00:00:00 2001 From: rnhmjoj Date: Sun, 29 Oct 2023 15:53:57 +0100 Subject: nixos/release-notes: mention sslh changes --- nixos/doc/manual/release-notes/rl-2311.section.md | 3 +++ 1 file changed, 3 insertions(+) (limited to 'nixos') diff --git a/nixos/doc/manual/release-notes/rl-2311.section.md b/nixos/doc/manual/release-notes/rl-2311.section.md index 76d9b026aa08a..b91e7d09866b3 100644 --- a/nixos/doc/manual/release-notes/rl-2311.section.md +++ b/nixos/doc/manual/release-notes/rl-2311.section.md @@ -388,6 +388,9 @@ - The `fonts.fonts` and `fonts.enableDefaultFonts` options have been renamed to `fonts.packages` and `fonts.enableDefaultPackages` respectively. +- The `services.sslh` module has been updated to follow [RFC 0042](https://github.com/NixOS/rfcs/blob/master/rfcs/0042-config-option.md). As such, several options have been moved to the freeform attribute set [services.sslh.settings](#opt-services.sslh.settings), which allows to change any of the settings in {manpage}`sslh(8)`. + In addition, the newly added option [services.sslh.method](#opt-services.sslh.method) allows to switch between the {manpage}`fork(2)`, {manpage}`select(2)` and `libev`-based connection handling method; see the [sslh docs](https://github.com/yrutschle/sslh/blob/master/doc/INSTALL.md#binaries) for a comparison. + - `pkgs.openvpn3` now optionally supports systemd-resolved. `programs.openvpn3` will automatically enable systemd-resolved support if `config.services.resolved.enable` is enabled. - `services.fail2ban.jails` can now be configured with attribute sets defining settings and filters instead of lines. The stringed options `daemonConfig` and `extraSettings` have respectively been replaced by `daemonSettings` and `jails.DEFAULT.settings` which use attribute sets. -- cgit 1.4.1