summaryrefslogtreecommitdiff
path: root/modules/firewall.nix
diff options
context:
space:
mode:
authorJakub Stachurski <jakub@wilkuu.nl>2026-07-30 21:58:26 +0200
committerJakub Stachurski <jakub@wilkuu.nl>2026-07-30 21:58:26 +0200
commit869cecc8a0dd9f5af2e36c89ddabe54f25bc2ebc (patch)
tree6db251dbe2b3ee774dd7e07f679193c104a5cd97 /modules/firewall.nix
parent8da770badb7fe21df138e770c8d1a8b6c4604882 (diff)
Get inventory to manage monitoring connections
This makes it so you can define the monitoring in the inventory and tacitus will automatically, pick it up.
Diffstat (limited to 'modules/firewall.nix')
-rw-r--r--modules/firewall.nix132
1 files changed, 132 insertions, 0 deletions
diff --git a/modules/firewall.nix b/modules/firewall.nix
new file mode 100644
index 0000000..575de7d
--- /dev/null
+++ b/modules/firewall.nix
@@ -0,0 +1,132 @@
+{
+ config,
+ inventory,
+ self_name,
+ lib,
+ ...
+}:
+let
+ inherit (lib)
+ mkOption
+ types
+ mkIf
+ mkEnableOption
+ mkDefault
+ ;
+ inv = inventory.${self_name};
+ net = inv.interfaces;
+ cfg = config.wilkuu.firewall;
+
+ portRangeType = types.addCheck (types.submodule {
+ options = {
+ from = mkOption {
+ type = types.port;
+ example = 100;
+ description = "lowest part of the range (inclusive)";
+ };
+ to = mkOption {
+ type = types.port;
+ example = 200;
+ description = "highest part of the range (inclusive)";
+ };
+ };
+ }) (range: range.from < range.to);
+
+ mkPortsOption =
+ protocol:
+ mkOption {
+ type = types.listOf types.port;
+ default = [ ];
+ example = [ 1234 ];
+ description = "List of ${protocol} ports to accept";
+ };
+
+ mkPortRangesOption =
+ protocol:
+ mkOption {
+ type = types.listOf portRangeType;
+ default = [ ];
+ example = [ 1234 ];
+ description = "List of ${protocol} port ranges to accept";
+ };
+
+ layerAssertions = lib.mapAttrsToList (
+ layerName: layerConfig:
+ let
+ missing = builtins.filter (importLayer: !(cfg.layers ? ${importLayer})) layerConfig.import-layer;
+ in
+ {
+ assertion = missing == [ ];
+ message = "Layer ${layerName} imports undefined layer(s): ${toString missing}";
+ }
+ ) cfg.layers;
+in
+{
+ options.wilkuu.firewall = {
+ enable = mkEnableOption "firewall module";
+ defaultLayer = lib.mkOption {
+ type = types.str;
+ default = "external";
+ example = "eth";
+ };
+ layers = mkOption {
+ default = { };
+ description = "Layers and which ports should be open";
+ type = types.attrsOf (
+ types.submodule {
+ options = {
+ import-layer = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "external" ];
+ description = "Layer names which ports will also be opened on this layer";
+ };
+ allowedTCPPorts = mkPortsOption "TCP";
+ allowedUDPPorts = mkPortsOption "UDP";
+ allowedTCPPortRanges = mkPortRangesOption "TCP";
+ allowedUDPPortRanges = mkPortRangesOption "UDP";
+ };
+ }
+ );
+ };
+ };
+
+ config = mkIf cfg.enable {
+ assertions = layerAssertions;
+ networking.nftables.enable = true;
+ networking.firewall =
+ let
+ # TODO: reconsider doing this as this will only do a flat-import anything more complex would require building an import tree.
+ resolvedLayers = lib.mapAttrs (
+ _: l1:
+ lib.foldl' (acc: set2: {
+ allowedTCPPorts = acc.allowedTCPPorts ++ set2.allowedTCPPorts;
+ allowedUDPPorts = acc.allowedUDPPorts ++ set2.allowedUDPPorts;
+ allowedTCPPortRanges = acc.allowedTCPPortRanges ++ set2.allowedTCPPortRanges;
+ allowedUDPPortRanges = acc.allowedUDPPortRanges ++ set2.allowedUDPPortRanges;
+ }) l1 (map (lk: cfg.layers.${lk}) l1.import-layer)
+ ) cfg.layers;
+
+ interfaces = lib.mapAttrs (_n: interface: {
+ inherit (resolvedLayers.${interface.layer})
+ allowedTCPPorts
+ allowedUDPPorts
+ allowedTCPPortRanges
+ allowedUDPPortRanges
+ ;
+ }) (lib.filterAttrs (_n: interface: !(builtins.elem interface.type [ "roaming" ])) net);
+
+ in
+ {
+ enable = true;
+ checkReversePath = mkDefault false;
+ inherit interfaces;
+ inherit (resolvedLayers.${cfg.defaultLayer})
+ allowedTCPPorts
+ allowedUDPPorts
+ allowedTCPPortRanges
+ allowedUDPPortRanges
+ ;
+ };
+ };
+}