2024-01-31 21:59:11 +01:00
|
|
|
# Taken from https://git.tempest.dev/ashe/nixos-wrapper-pounce/src/branch/main/flake.nix
|
|
|
|
# I have removed a lot of the things I didn't need, and modified a few things to fit
|
|
|
|
# my preferences (specifically, simplified the cert situation)
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
|
|
cfg = config.services.pounce;
|
|
|
|
pkg = pkgs.pounce;
|
|
|
|
defaultUser = "pounce";
|
|
|
|
|
|
|
|
# {{{ systemd hardening flags
|
|
|
|
hardeningFlags = {
|
|
|
|
CapabilityBoundingSet = [ "" ];
|
|
|
|
NoNewPrivileges = true;
|
|
|
|
PrivateDevices = true;
|
|
|
|
PrivateMounts = true;
|
|
|
|
PrivateTmp = true;
|
|
|
|
PrivateUsers = true;
|
|
|
|
ProtectClock = true;
|
|
|
|
ProtectControlGroups = true;
|
|
|
|
ProtectKernelLogs = true;
|
|
|
|
ProtectKernelModules = true;
|
|
|
|
ProtectKernelTunables = true;
|
|
|
|
ProtectProc = "invisible";
|
|
|
|
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
|
|
|
|
RestrictNamespaces = true;
|
|
|
|
RestrictSUIDSGID = true;
|
|
|
|
SystemCallArchitectures = "native";
|
|
|
|
SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ];
|
|
|
|
};
|
|
|
|
# }}}
|
|
|
|
in
|
|
|
|
{
|
|
|
|
# {{{ Options
|
|
|
|
options.services.pounce = {
|
|
|
|
# {{{ general options
|
2024-05-21 01:37:39 +02:00
|
|
|
enable = mkEnableOption "the Pounce IRC bouncer and Calico dispatcher";
|
2024-01-31 21:59:11 +01:00
|
|
|
|
|
|
|
user = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = defaultUser;
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
User account under which Pounce runs. If not specified, a default user
|
|
|
|
will be created.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
dataDir = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "/run/pounce";
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Directory where each Pounce instance's UNIX-domain socket is stored for
|
|
|
|
Calico to route to.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
externalHost = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
example = "example.org";
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Base domain name Calico will be accessible at. Each instance
|
|
|
|
will be at a subdomain of this.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
bindHost = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
default = "localhost";
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
The IP or host for Calico to bind to.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
port = mkOption {
|
|
|
|
type = types.port;
|
|
|
|
default = 6697;
|
|
|
|
description = lib.mdDoc "Port for Calico to listen on.";
|
|
|
|
};
|
|
|
|
|
|
|
|
openFirewall = mkOption {
|
|
|
|
type = types.bool;
|
|
|
|
default = false;
|
|
|
|
description = lib.mdDoc "Open port in the firewall for Calico.";
|
|
|
|
};
|
|
|
|
|
|
|
|
timeout = mkOption {
|
|
|
|
type = types.ints.positive;
|
|
|
|
default = 1000;
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Timeout parameter (in milliseconds) for Calico to close a connection
|
|
|
|
if no `ClientHello` message is sent.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
|
|
|
|
certDir = mkOption {
|
|
|
|
type = types.str;
|
|
|
|
example = "/var/lib/acme/wildcard-irc.exmaple.com";
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Directory where each Pounce instance's TLS certificates and private
|
|
|
|
keys are stored. The folder must contain wildcard certs as generated by acme.
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
# }}}
|
|
|
|
# {{{ networks
|
|
|
|
networks = mkOption {
|
|
|
|
type = types.attrsOf (types.submodule {
|
|
|
|
options = {
|
|
|
|
config = mkOption {
|
|
|
|
type = types.path;
|
|
|
|
description = lib.mdDoc ''
|
|
|
|
Location to load pounce configuration from
|
|
|
|
'';
|
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
|
|
|
default = { };
|
|
|
|
description = lib.mdDoc "Attribute set of IRC networks to connect to.";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
# }}}
|
|
|
|
# }}}
|
|
|
|
# {{{ config
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
systemd.tmpfiles.rules = [ "d ${cfg.dataDir} 0700 ${cfg.user} ${cfg.user} -" ];
|
|
|
|
systemd.services = mkMerge (
|
|
|
|
# {{{ calico service
|
|
|
|
[
|
|
|
|
{
|
|
|
|
calico = {
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
|
|
after = [ "network.target" ];
|
|
|
|
|
|
|
|
description = "Calico dispatcher for Pounce IRC bouncer.";
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
User = cfg.user;
|
|
|
|
Group = cfg.user;
|
|
|
|
ExecStart = ''
|
|
|
|
${pkg}/bin/calico \
|
|
|
|
-H ${cfg.bindHost} -P ${toString cfg.port} \
|
|
|
|
-t ${toString cfg.timeout} ${cfg.dataDir}
|
|
|
|
'';
|
|
|
|
Restart = "on-failure";
|
|
|
|
} // hardeningFlags;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
] ++
|
|
|
|
# }}}
|
|
|
|
# {{{ pounce service
|
|
|
|
(mapAttrsToList
|
|
|
|
(name: value: mkMerge [
|
|
|
|
{
|
|
|
|
"pounce-${name}" = {
|
|
|
|
wantedBy = [ "calico.service" ];
|
|
|
|
after = [ "network.target" ];
|
|
|
|
before = [ "calico.service" ];
|
|
|
|
|
|
|
|
description = "Pounce IRC bouncer for the ${name} network.";
|
|
|
|
|
|
|
|
serviceConfig = {
|
|
|
|
User = cfg.user;
|
|
|
|
Group = cfg.user;
|
|
|
|
ExecStart = ''
|
|
|
|
${pkg}/bin/pounce \
|
|
|
|
-C ${cfg.certDir}/fullchain.pem \
|
|
|
|
-K ${cfg.certDir}/key.pem \
|
|
|
|
-U ${cfg.dataDir} -H ${name}.${cfg.externalHost} \
|
|
|
|
${value.config}
|
|
|
|
'';
|
|
|
|
Restart = "on-failure";
|
|
|
|
} // hardeningFlags;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
])
|
|
|
|
cfg.networks)
|
|
|
|
# }}}
|
|
|
|
);
|
|
|
|
|
|
|
|
users = optionalAttrs (cfg.user == defaultUser) {
|
|
|
|
users.${defaultUser} = {
|
|
|
|
group = defaultUser;
|
|
|
|
isSystemUser = true;
|
|
|
|
};
|
|
|
|
|
|
|
|
groups.${defaultUser} = { };
|
|
|
|
};
|
|
|
|
|
|
|
|
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
|
|
|
};
|
|
|
|
# }}}
|
|
|
|
}
|