You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
5.7 KiB
Nix
188 lines
5.7 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
servName = "nft-update-addresses";
|
|
cfg = config.services.${servName};
|
|
settingsFormat = pkgs.formats.json { };
|
|
mkDisableOption = desc: lib.mkEnableOption desc // { default = true; };
|
|
# output options values
|
|
configFile = pkgs.writeTextFile {
|
|
name = "${servName}.json";
|
|
text = builtins.toJSON cfg.settings; # TODO can otherwise not easily check the file for errors
|
|
checkPhase = ''
|
|
${lib.getExe cfg.package} --check-config --config-file "$out"
|
|
'';
|
|
};
|
|
staticDefs = builtins.readFile (
|
|
pkgs.runCommandLocal "${servName}.nftables" { } ''
|
|
${lib.getExe cfg.package} --output-set-definitions --config-file ${configFile} > $out
|
|
''
|
|
);
|
|
in
|
|
{
|
|
|
|
options.services.${servName} = {
|
|
|
|
enable = lib.mkEnableOption "${servName} service";
|
|
|
|
package = lib.mkPackageOption pkgs (lib.singleton servName) { };
|
|
|
|
settings = lib.mkOption {
|
|
# TODO link to docu
|
|
description = "Configuration for ${servName}";
|
|
type = settingsFormat.type;
|
|
default = {
|
|
nftTable = "nixos-fw";
|
|
};
|
|
example.interfaces = {
|
|
wan0 = { };
|
|
lan0.ports.tcp = {
|
|
exposed = [
|
|
{
|
|
dest = "aa:bb:cc:dd:ee:ff";
|
|
port = 80;
|
|
}
|
|
{
|
|
dest = "aa:bb:cc:00:11:22";
|
|
port = 80;
|
|
}
|
|
];
|
|
forwarded = [
|
|
{
|
|
dest = "aabb-ccdd-eeff";
|
|
lanPort = 80;
|
|
wanPort = 80;
|
|
}
|
|
{
|
|
dest = "aa.bbcc.0011.22";
|
|
lanPort = 80;
|
|
wanPort = 8080;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
};
|
|
|
|
includeStaticDefinitions = mkDisableOption ''inclusion of static definitions from {option}`services.${servName}.nftablesStaticDefinitions` into the nftables config'';
|
|
|
|
configurationFile = lib.mkOption {
|
|
description = "Path to configuration file used by ${servName}.";
|
|
type = lib.types.path; # needs to be available at build time
|
|
readOnly = true;
|
|
default = configFile;
|
|
defaultText = lib.literalExpression "# content as generated from config.services.${servName}.settings";
|
|
};
|
|
|
|
nftablesStaticDefinitions = lib.mkOption {
|
|
description = ''
|
|
Static definitions provided by ${servName} when called with given configuration.
|
|
|
|
When {option}`services.${servName}.includeStaticDefinitions (which is default),
|
|
these will be already included in your nftables setup.
|
|
Otherwise, you can use the value of this output option as you prefer.
|
|
'';
|
|
readOnly = true;
|
|
default = staticDefs;
|
|
defaultText = lib.literalExpression "# as provided by ${servName}";
|
|
};
|
|
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
|
|
assertions = [
|
|
{
|
|
assertion = cfg.enable -> config.networking.nftables.enable;
|
|
message = "${servName} requires nftables to be configured";
|
|
}
|
|
# TODO assert for port duplications
|
|
];
|
|
|
|
networking.nftables.tables.${cfg.settings.nftTable}.content = lib.mkIf cfg.includeStaticDefinitions staticDefs;
|
|
|
|
systemd.services.${servName} = {
|
|
description = "IPv6 prefix updater for subnet & NAT rules for nftables router setup";
|
|
after = [
|
|
"nftables.service"
|
|
"network.target"
|
|
];
|
|
partOf = lib.singleton "nftables.service";
|
|
requisite = lib.singleton "nftables.service";
|
|
wantedBy = lib.singleton "multi-user.target";
|
|
upheldBy = lib.singleton "systemd-networkd.service";
|
|
unitConfig.ReloadPropagatedFrom = lib.singleton "nftables.service";
|
|
restartIfChanged = true;
|
|
restartTriggers = config.systemd.services.nftables.restartTriggers;
|
|
serviceConfig = {
|
|
# Service
|
|
Type = "notify-reload";
|
|
ExecStart = lib.singleton "${lib.getExe cfg.package} ${
|
|
lib.cli.toGNUCommandLineShell { } {
|
|
config-file = configFile;
|
|
ip-command = "${pkgs.iproute2}/bin/ip";
|
|
nft-command = lib.getExe pkgs.nftables;
|
|
}
|
|
}";
|
|
RestartSec = "250ms";
|
|
RestartSteps = 3;
|
|
RestartMaxDelaySec = "3s";
|
|
TimeoutSec = "10s";
|
|
Restart = "always";
|
|
NotifyAccess = "all"; # bash script opens subprocesses in pipes
|
|
# Paths
|
|
ProtectProc = "noaccess";
|
|
ProcSubset = "pid";
|
|
CapabilityBoundingSet = [
|
|
"CAP_BPF" # nft is compiled to bpf
|
|
"CAP_IPC_LOCK" # ?
|
|
"CAP_KILL" # ?
|
|
"CAP_NET_ADMIN"
|
|
];
|
|
# Security
|
|
NoNewPrivileges = true;
|
|
# Process
|
|
KeyringMode = "private";
|
|
OOMScoreAdjust = 10;
|
|
# Scheduling
|
|
Nice = -2;
|
|
CPUSchedulingPolicy = "fifo";
|
|
# Sandboxing
|
|
ProtectSystem = "strict";
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
PrivateDevices = true;
|
|
PrivateNetwork = false; # breaks nftables
|
|
PrivateIPC = true;
|
|
PrivateUsers = false; # breaks nftables
|
|
ProtectClock = true;
|
|
ProtectKernelTunables = true;
|
|
ProtectKernelModules = true; # are already loaded
|
|
ProtectKernelLogs = true;
|
|
ProtectControlGroups = true;
|
|
#RestrictAddressFamilies = [
|
|
# # ?
|
|
# "AF_INET"
|
|
# "AF_INET6"
|
|
# #"AF_NETLINK"
|
|
#];
|
|
RestrictNamespaces = true;
|
|
RestrictSUIDSGID = true;
|
|
#SystemCallFilter = "@basic-io @ipc @network-io @signal @timer" # definitly will break that
|
|
#SystemCallLog = "~"; # for debugging; should lock all system calls made
|
|
# Resource Control
|
|
CPUQuota = "50%";
|
|
# TODO test to gather real values
|
|
MemoryLow = "8M";
|
|
MemoryHigh = "32M";
|
|
MemoryMax = "128M";
|
|
};
|
|
};
|
|
|
|
};
|
|
|
|
}
|