Skip to content

Commit

Permalink
Merge pull request #245855 from rnhmjoj/pr-sslh
Browse files Browse the repository at this point in the history
nixos/sslh: update and refactor for RFC42
  • Loading branch information
rnhmjoj authored Oct 29, 2023
2 parents cfb3988 + fc4691c commit 800965c
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 82 deletions.
3 changes: 3 additions & 0 deletions nixos/doc/manual/release-notes/rl-2311.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,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.
Expand Down
189 changes: 124 additions & 65 deletions nixos/modules/services/networking/sslh.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,131 @@ 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-connections" ]
(config: if config.services.sslh.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" "ev" ];
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.";
};
- `ev` is implemented using libev, it's similar to `select` but
scales better to a large number of connections.
'';
};

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.timeout = mkOption {
type = types.ints.unsigned;
default = 2;
description = lib.mdDoc "Timeout in seconds.";
};

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.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;
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)`),
appendConfig = mkOption {
type = types.str;
default = defaultAppendConfig;
description = lib.mdDoc "Verbatim configuration file.";
- `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.";
};
};

Expand All @@ -96,20 +146,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;
Expand Down
18 changes: 5 additions & 13 deletions nixos/tests/sslh.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 ];
Expand Down
16 changes: 12 additions & 4 deletions pkgs/servers/sslh/default.nix
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
{ 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" ];

postInstall = ''
# 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"
'';

installFlags = [ "PREFIX=$(out)" ];

hardeningDisable = [ "format" ];
Expand Down

0 comments on commit 800965c

Please sign in to comment.