nixos-config/nixos/modules/services/enthalpy/ipsec.nix

199 lines
5.7 KiB
Nix

# Portions of this file are sourced from
# https://github.com/NickCao/flakes/blob/3b03efb676ea602575c916b2b8bc9d9cd13b0d85/modules/gravity/default.nix
{
config,
lib,
pkgs,
mylib,
...
}:
with lib;
let
cfg = config.services.enthalpy;
in
{
options.services.enthalpy.ipsec = {
enable = mkEnableOption "IPSec/IKEv2 for link-scope connectivity";
organization = mkOption {
type = types.str;
description = ''
Unique identifier of a keypair.
'';
};
commonName = mkOption {
type = types.str;
description = ''
Name of this node, should be unique within an organization.
'';
};
endpoints = mkOption {
type = types.listOf (
types.submodule {
options = {
serialNumber = mkOption { type = types.str; };
addressFamily = mkOption { type = types.str; };
address = mkOption {
type = types.nullOr types.str;
default = null;
};
};
}
);
description = ''
List of endpoints available on this node.
'';
};
interfaces = mkOption {
type = types.listOf types.str;
default = [ ];
description = ''
List of network interfaces that should be used by charon daemon.
'';
};
privateKeyPath = mkOption {
type = types.str;
description = ''
Path to the private key of this organization.
'';
};
registry = mkOption {
type = types.path;
description = ''
Path to the registry.
'';
};
blacklist = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
description = ''
A list of organizations that are blacklisted.
'';
};
whitelist = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
description = ''
A list of organizations that are whitelisted.
'';
};
};
config = mkIf (cfg.enable && cfg.ipsec.enable) {
assertions = [
{
assertion = builtins.all id [
(cfg.ipsec.blacklist != null -> cfg.ipsec.whitelist == null)
(cfg.ipsec.whitelist != null -> cfg.ipsec.blacklist == null)
];
message = ''
Only one of `config.services.enthalpy.ipsec.blacklist` or
`config.services.enthalpy.ipsec.whitelist` can be defined at a time.
'';
}
];
environment.etc."enthalpy/ranet/config.json".source =
(pkgs.formats.json { }).generate "enthalpy-ranet-config-json"
{
organization = cfg.ipsec.organization;
common_name = cfg.ipsec.commonName;
endpoints = builtins.map (ep: {
serial_number = ep.serialNumber;
address_family = ep.addressFamily;
address = ep.address;
port = config.networking.ports.enthalpy-ipsec;
updown = pkgs.writeShellScript "updown" ''
LINK=enta$(printf '%08x\n' "$PLUTO_IF_ID_OUT")
case "$PLUTO_VERB" in
up-client)
ip link add "$LINK" type xfrm if_id "$PLUTO_IF_ID_OUT"
ip link set "$LINK" netns enthalpy multicast on mtu 1400 up
;;
down-client)
ip -n enthalpy link del "$LINK"
;;
esac
'';
}) cfg.ipsec.endpoints;
};
services.strongswan-swanctl = {
enable = true;
strongswan.extraConfig = ''
charon {
interfaces_use = ${strings.concatStringsSep "," cfg.ipsec.interfaces}
port = 0
port_nat_t = ${toString config.networking.ports.enthalpy-ipsec}
retransmit_base = 1
plugins {
socket-default {
set_source = yes
set_sourceif = yes
}
dhcp {
load = no
}
}
}
charon-systemd {
journal {
default = -1
ike = 0
}
}
'';
};
systemd.services.enthalpy-ipsec =
let
registry =
if cfg.ipsec.whitelist != null then
pkgs.runCommand "filtered-registry" { } ''
${pkgs.jq}/bin/jq "[.[] | select(.organization | IN(${
concatMapStringsSep "," (org: "\\\"${org}\\\"") cfg.ipsec.whitelist
}))]" ${cfg.ipsec.registry} > $out
''
else if cfg.ipsec.blacklist != null then
pkgs.runCommand "filtered-registry" { } ''
${pkgs.jq}/bin/jq "[.[] | select(.organization | IN(${
concatMapStringsSep "," (org: "\\\"${org}\\\"") cfg.ipsec.blacklist
}) | not)]" ${cfg.ipsec.registry} > $out
''
else
cfg.ipsec.registry;
command = "ranet -c /etc/enthalpy/ranet/config.json -r ${registry} -k ${cfg.ipsec.privateKeyPath}";
in
{
path = with pkgs; [
iproute2
ranet
];
script = "${command} up";
reload = "${command} up";
preStop = "${command} down";
serviceConfig = mylib.misc.serviceHardened // {
Type = "oneshot";
RemainAfterExit = true;
};
bindsTo = [
"strongswan-swanctl.service"
];
wants = [
"network-online.target"
"strongswan-swanctl.service"
];
after = [
"network-online.target"
"netns-enthalpy.service"
"strongswan-swanctl.service"
];
partOf = [ "netns-enthalpy.service" ];
wantedBy = [
"multi-user.target"
"netns-enthalpy.service"
];
reloadTriggers = [ config.environment.etc."enthalpy/ranet/config.json".source ];
};
};
}