178 lines
5.1 KiB
Nix
178 lines
5.1 KiB
Nix
![]() |
{
|
||
|
dag,
|
||
|
lib,
|
||
|
...
|
||
|
}: let
|
||
|
inherit (lib.options) mkOption mkEnableOption;
|
||
|
inherit (lib.strings) optionalString concatMapStringsSep concatStringsSep;
|
||
|
inherit (lib.attrsets) filterAttrs mapAttrsToList;
|
||
|
inherit (lib.lists) concatLists;
|
||
|
inherit (lib) types;
|
||
|
inherit (dag) dagOf topoSort;
|
||
|
|
||
|
mkTable = desc: body:
|
||
|
mkOption {
|
||
|
default = {};
|
||
|
description = "Containers for chains, sets, and other stateful objects.";
|
||
|
type = types.submodule ({config, ...}: {
|
||
|
options =
|
||
|
{
|
||
|
enable = mkEnableOption desc;
|
||
|
objects = mkOption {
|
||
|
type = with types; listOf str;
|
||
|
description = "Objects associated with this table.";
|
||
|
default = [];
|
||
|
};
|
||
|
}
|
||
|
// body;
|
||
|
|
||
|
config = let
|
||
|
buildChainDag = chain:
|
||
|
concatMapStringsSep "\n" ({
|
||
|
name,
|
||
|
data,
|
||
|
}: let
|
||
|
protocol =
|
||
|
if builtins.isNull data.protocol
|
||
|
then ""
|
||
|
else data.protocol;
|
||
|
field =
|
||
|
if builtins.isNull data.field
|
||
|
then ""
|
||
|
else data.field;
|
||
|
inherit (data) policy;
|
||
|
values = map toString data.value;
|
||
|
value =
|
||
|
if builtins.isNull data.value
|
||
|
then ""
|
||
|
else
|
||
|
(
|
||
|
if builtins.length data.value == 1
|
||
|
then builtins.head values
|
||
|
else "{ ${concatStringsSep ", " values} }"
|
||
|
);
|
||
|
in ''
|
||
|
${protocol} ${field} ${value} ${policy} comment ${name}
|
||
|
'') ((topoSort chain).result or (throw "Cycle in DAG"));
|
||
|
|
||
|
buildChain = chainType: chain:
|
||
|
mapAttrsToList (chainName: chainDag: ''
|
||
|
chain ${chainName} {
|
||
|
type ${chainType} hook ${chainName} priority 0;
|
||
|
|
||
|
${buildChainDag chainDag}
|
||
|
}
|
||
|
'') (filterAttrs (_: g: builtins.length (builtins.attrNames g) > 0) chain);
|
||
|
in {
|
||
|
objects = let
|
||
|
chains = concatLists [
|
||
|
(
|
||
|
if config ? filter
|
||
|
then buildChain "filter" config.filter
|
||
|
else []
|
||
|
)
|
||
|
(
|
||
|
if config ? nat
|
||
|
then buildChain "nat" config.nat
|
||
|
else []
|
||
|
)
|
||
|
(
|
||
|
if config ? route
|
||
|
then buildChain "route" config.route
|
||
|
else []
|
||
|
)
|
||
|
];
|
||
|
in
|
||
|
chains;
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
|
||
|
mkChain = _: description:
|
||
|
mkOption {
|
||
|
inherit description;
|
||
|
default = {};
|
||
|
type = dagOf (types.submodule {
|
||
|
options = {
|
||
|
protocol = mkOption {
|
||
|
default = null;
|
||
|
description = "Protocol to match.";
|
||
|
type = with types;
|
||
|
nullOr (either (enum [
|
||
|
"ether"
|
||
|
"vlan"
|
||
|
"arp"
|
||
|
"ip"
|
||
|
"icmp"
|
||
|
"igmp"
|
||
|
"ip6"
|
||
|
"icmpv6"
|
||
|
"tcp"
|
||
|
"udp"
|
||
|
"udplite"
|
||
|
"sctp"
|
||
|
"dccp"
|
||
|
"ah"
|
||
|
"esp"
|
||
|
"comp"
|
||
|
])
|
||
|
str);
|
||
|
};
|
||
|
|
||
|
field = mkOption {
|
||
|
default = null;
|
||
|
description = "Field value to match.";
|
||
|
type = with types;
|
||
|
nullOr (enum [
|
||
|
"dport"
|
||
|
"sport"
|
||
|
"daddr"
|
||
|
"saddr"
|
||
|
"type"
|
||
|
"state"
|
||
|
"iifname"
|
||
|
"pkttype"
|
||
|
]);
|
||
|
};
|
||
|
|
||
|
value = mkOption {
|
||
|
default = null;
|
||
|
description = "Associated value.";
|
||
|
type = with types; let
|
||
|
valueType = oneOf [port str];
|
||
|
in
|
||
|
nullOr (coercedTo valueType (value: [value]) (listOf valueType));
|
||
|
};
|
||
|
|
||
|
policy = mkOption {
|
||
|
description = "What to do with matching packets.";
|
||
|
type = types.enum [
|
||
|
"accept"
|
||
|
"reject"
|
||
|
"drop"
|
||
|
"log"
|
||
|
];
|
||
|
};
|
||
|
};
|
||
|
});
|
||
|
};
|
||
|
|
||
|
mkRuleset = ruleset:
|
||
|
concatStringsSep "\n" (mapAttrsToList (name: table:
|
||
|
optionalString (builtins.length table.objects > 0) ''
|
||
|
table ${name} nixos {
|
||
|
${concatStringsSep "\n" table.objects}
|
||
|
}
|
||
|
'')
|
||
|
ruleset);
|
||
|
|
||
|
mkIngressChain = mkChain "Process all packets before they enter the system";
|
||
|
mkPrerouteChain = mkChain "Process all packets entering the system";
|
||
|
mkInputChain = mkChain "Process packets delivered to the local system";
|
||
|
mkForwardChain = mkChain "Process packets forwarded to a different host";
|
||
|
mkOutputChain = mkChain "Process packets sent by local processes";
|
||
|
mkPostrouteChain = mkChain "Process all packets leaving the system";
|
||
|
in {
|
||
|
inherit mkTable mkRuleset mkIngressChain mkPrerouteChain mkInputChain mkForwardChain mkOutputChain mkPostrouteChain;
|
||
|
}
|