2014-04-15 08:21:53 +00:00
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
|
|
|
|
with lib;
|
|
|
|
|
|
|
|
let
|
|
|
|
cfg = config.services.spiped;
|
|
|
|
in
|
|
|
|
{
|
|
|
|
options = {
|
2014-04-20 00:34:18 +00:00
|
|
|
services.spiped = {
|
|
|
|
enable = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = "Enable the spiped service module.";
|
|
|
|
};
|
2014-04-15 08:21:53 +00:00
|
|
|
|
2014-04-20 00:34:18 +00:00
|
|
|
config = mkOption {
|
|
|
|
type = types.attrsOf (types.submodule (
|
|
|
|
{
|
|
|
|
options = {
|
|
|
|
encrypt = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Take unencrypted connections from the
|
|
|
|
`source` socket and send encrypted
|
|
|
|
connections to the `target` socket.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
decrypt = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Take encrypted connections from the
|
|
|
|
`source` socket and send unencrypted
|
|
|
|
connections to the `target` socket.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
source = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
description = ''
|
|
|
|
Address on which spiped should listen for incoming
|
|
|
|
connections. Must be in one of the following formats:
|
|
|
|
`/absolute/path/to/unix/socket`,
|
|
|
|
`host.name:port`,
|
|
|
|
`[ip.v4.ad.dr]:port` or
|
|
|
|
`[ipv6::addr]:port` - note that
|
|
|
|
hostnames are resolved when spiped is launched and are
|
|
|
|
not re-resolved later; thus if DNS entries change
|
|
|
|
spiped will continue to connect to the expired
|
|
|
|
address.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
target = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
description = "Address to which spiped should connect.";
|
|
|
|
};
|
|
|
|
|
|
|
|
keyfile = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
description = ''
|
2023-01-06 20:15:08 +00:00
|
|
|
Name of a file containing the spiped key.
|
|
|
|
As the daemon runs as the `spiped` user,
|
|
|
|
the key file must be readable by that user.
|
|
|
|
To securely manage the file within your configuration
|
|
|
|
consider a tool such as agenix or sops-nix.
|
2014-04-20 00:34:18 +00:00
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
timeout = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 5;
|
|
|
|
description = ''
|
|
|
|
Timeout, in seconds, after which an attempt to connect to
|
|
|
|
the target or a protocol handshake will be aborted (and the
|
|
|
|
connection dropped) if not completed
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
maxConns = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 100;
|
|
|
|
description = ''
|
|
|
|
Limit on the number of simultaneous connections allowed.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
waitForDNS = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Wait for DNS. Normally when `spiped` is
|
|
|
|
launched it resolves addresses and binds to its source
|
|
|
|
socket before the parent process returns; with this option
|
|
|
|
it will daemonize first and retry failed DNS lookups until
|
|
|
|
they succeed. This allows `spiped` to
|
|
|
|
launch even if DNS isn't set up yet, but at the expense of
|
|
|
|
losing the guarantee that once `spiped` has
|
|
|
|
finished launching it will be ready to create pipes.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
disableKeepalives = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = "Disable transport layer keep-alives.";
|
|
|
|
};
|
|
|
|
|
|
|
|
weakHandshake = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = ''
|
|
|
|
Use fast/weak handshaking: This reduces the CPU time spent
|
|
|
|
in the initial connection setup, at the expense of losing
|
|
|
|
perfect forward secrecy.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
resolveRefresh = mkOption {
|
|
|
|
type = types.int;
|
|
|
|
default = 60;
|
|
|
|
description = ''
|
|
|
|
Resolution refresh time for the target socket, in seconds.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
disableReresolution = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = "Disable target address re-resolution.";
|
|
|
|
};
|
2014-04-15 08:21:53 +00:00
|
|
|
};
|
2014-04-20 00:34:18 +00:00
|
|
|
}
|
|
|
|
));
|
|
|
|
|
|
|
|
default = {};
|
|
|
|
|
2021-10-03 16:06:03 +00:00
|
|
|
example = literalExpression ''
|
2014-04-20 00:34:18 +00:00
|
|
|
{
|
|
|
|
pipe1 =
|
|
|
|
{ keyfile = "/var/lib/spiped/pipe1.key";
|
|
|
|
encrypt = true;
|
|
|
|
source = "localhost:6000";
|
|
|
|
target = "endpoint.example.com:7000";
|
|
|
|
};
|
|
|
|
pipe2 =
|
|
|
|
{ keyfile = "/var/lib/spiped/pipe2.key";
|
|
|
|
decrypt = true;
|
|
|
|
source = "0.0.0.0:7000";
|
|
|
|
target = "localhost:3000";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
'';
|
|
|
|
|
|
|
|
description = ''
|
|
|
|
Configuration for a secure pipe daemon. The daemon can be
|
|
|
|
started, stopped, or examined using
|
|
|
|
`systemctl`, under the name
|
|
|
|
`spiped@foo`.
|
|
|
|
'';
|
|
|
|
};
|
2014-04-15 08:21:53 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2014-04-20 00:34:18 +00:00
|
|
|
config = mkIf cfg.enable {
|
2014-04-15 08:21:53 +00:00
|
|
|
assertions = mapAttrsToList (name: c: {
|
|
|
|
assertion = (c.encrypt -> !c.decrypt) || (c.decrypt -> c.encrypt);
|
|
|
|
message = "A pipe must either encrypt or decrypt";
|
2014-04-20 00:34:18 +00:00
|
|
|
}) cfg.config;
|
2014-04-15 08:21:53 +00:00
|
|
|
|
2018-06-29 23:58:35 +00:00
|
|
|
users.groups.spiped.gid = config.ids.gids.spiped;
|
|
|
|
users.users.spiped = {
|
2014-04-15 08:21:53 +00:00
|
|
|
description = "Secure Pipe Service user";
|
|
|
|
group = "spiped";
|
|
|
|
uid = config.ids.uids.spiped;
|
|
|
|
};
|
|
|
|
|
|
|
|
systemd.services."spiped@" = {
|
|
|
|
description = "Secure pipe '%i'";
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
Restart = "always";
|
|
|
|
User = "spiped";
|
|
|
|
};
|
|
|
|
|
|
|
|
scriptArgs = "%i";
|
|
|
|
script = "exec ${pkgs.spiped}/bin/spiped -F `cat /etc/spiped/$1.spec`";
|
|
|
|
};
|
|
|
|
|
|
|
|
# Setup spiped config files
|
|
|
|
environment.etc = mapAttrs' (name: cfg: nameValuePair "spiped/${name}.spec"
|
|
|
|
{ text = concatStringsSep " "
|
|
|
|
[ (if cfg.encrypt then "-e" else "-d") # Mode
|
|
|
|
"-s ${cfg.source}" # Source
|
|
|
|
"-t ${cfg.target}" # Target
|
|
|
|
"-k ${cfg.keyfile}" # Keyfile
|
|
|
|
"-n ${toString cfg.maxConns}" # Max number of conns
|
|
|
|
"-o ${toString cfg.timeout}" # Timeout
|
|
|
|
(optionalString cfg.waitForDNS "-D") # Wait for DNS
|
|
|
|
(optionalString cfg.weakHandshake "-f") # No PFS
|
|
|
|
(optionalString cfg.disableKeepalives "-j") # Keepalives
|
|
|
|
(if cfg.disableReresolution then "-R"
|
|
|
|
else "-r ${toString cfg.resolveRefresh}")
|
|
|
|
];
|
2014-04-20 00:34:18 +00:00
|
|
|
}) cfg.config;
|
2014-04-15 08:21:53 +00:00
|
|
|
};
|
|
|
|
}
|