diff options
| author | Jakub Stachurski <jakub@wilkuu.nl> | 2026-07-30 21:58:26 +0200 |
|---|---|---|
| committer | Jakub Stachurski <jakub@wilkuu.nl> | 2026-07-30 21:58:26 +0200 |
| commit | 869cecc8a0dd9f5af2e36c89ddabe54f25bc2ebc (patch) | |
| tree | 6db251dbe2b3ee774dd7e07f679193c104a5cd97 /modules | |
| parent | 8da770badb7fe21df138e770c8d1a8b6c4604882 (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')
| -rw-r--r-- | modules/default.nix | 1 | ||||
| -rw-r--r-- | modules/firewall.nix | 132 | ||||
| -rw-r--r-- | modules/prometheus.nix | 185 |
3 files changed, 249 insertions, 69 deletions
diff --git a/modules/default.nix b/modules/default.nix index 6cc52d8..39915dd 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -17,6 +17,7 @@ ./test_endpoint.nix ./prometheus.nix ./mjmap.nix + ./firewall.nix ]; nixpkgs.overlays = [ 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 + ; + }; + }; +} diff --git a/modules/prometheus.nix b/modules/prometheus.nix index 63b761f..83eda86 100644 --- a/modules/prometheus.nix +++ b/modules/prometheus.nix @@ -1,11 +1,18 @@ -{ lib, config, ... }: +{ + lib, + config, + inventory, + self_name, + options, + ... +}: let cfg = config.wilkuu.services.prometheus; + mon = inventory.${self_name}.monitoring; inherit (lib) mkEnableOption mkIf mkMerge - mkDefault ; in { @@ -15,16 +22,45 @@ in }; config = mkMerge [ - (mkIf cfg.enableExporters { - services.prometheus.exporters = { - wireguard.enable = mkDefault config.networking.wireguard.enable; - fail2ban.enable = mkDefault config.services.fail2ban.enable; - node = { + (mkIf cfg.enableExporters ( + let + data = lib.mapAttrs ( + _name: mn: + let + default = (builtins.filter (a: builtins.isString a) mn); + custom = (builtins.filter (a: builtins.isAttrs a) mn); + in + { + ports = + builtins.map (n: options.services.prometheus.exporters.value.${n}.port) default + ++ (builtins.map (a: a.port) custom); + custom_port_mappings = builtins.listToAttrs ( + map ({ name, port, ... }: { + inherit name; + value = port; + }) custom + ); + exporter_services = + default + ++ (builtins.map (a: a.name) (builtins.filter ((a: !(a.skipExporterConfig or false))) custom)); + } + ) mon; + data_all = (lib.foldl lib.recursiveUpdate { } (lib.attrValues data)); + in + { + # Enable all the exporters outlined in the inventory + services.prometheus.exporters = lib.genAttrs data_all.exporter_services (name: { enable = true; - }; - }; + port = lib.mkIf (data_all.custom_port_mappings ? name) data_all.custom_port_mappings.${name}; + + }); + # Let all the exporters export on all layers + wilkuu.firewall.layers = lib.mapAttrs (_n: a: { + allowedTCPPorts = a.ports; + }) data; + } + )) - }) (mkIf cfg.enableScraper { sops.secrets = { "prometheus/stalwart-pass" = { @@ -52,76 +88,87 @@ in services.prometheus = let - omega-relay-ip = "192.168.80.100"; + nets_in_layer = inv: layer: lib.filterAttrs (_n: ifi: (ifi.layer or "") == layer) inv.interfaces; + dests_in_layer = + inv: layer: port: + (map (net: "${net.ip}:${toString port}") (lib.attrValues (nets_in_layer inv layer))); + + job_destinations = ( + builtins.foldl' + ( + acc: perHost: + (builtins.foldl' ( + acc2: hostMonitorName: + let + current = acc2.${hostMonitorName} or [ ]; + new = perHost.${hostMonitorName}; + in + acc2 // { ${hostMonitorName} = current ++ new; } + ) acc (lib.attrNames perHost)) + ) + { } + ( + lib.mapAttrsToList ( + _host: inv: + (lib.foldl' (a: b: a // b) { } ( + lib.mapAttrsToList ( + + layer: lst: + (lib.listToAttrs ( + builtins.map ( + a: + let + ifSimple = + a: t: f: + if builtins.isString a then t else f; + name = ifSimple a a a.name; + port = ifSimple a options.services.prometheus.exporters.value.${a}.port ( + a.port or options.services.prometheus.exporters.${a.name}.port.default + ); + in + { + inherit name; + value = (dests_in_layer inv layer port); + } + ) lst + )) + ) inv.monitoring + )) + ) inventory + ) + ); + in { enable = true; globalConfig = { scrape_interval = "10s"; }; - scrapeConfigs = [ - { - job_name = "node"; - static_configs = - let - port = toString config.services.prometheus.exporters.node.port; - in - [ - { - targets = [ - "localhost:${port}" - "${omega-relay-ip}:${port}" - ]; - } - ]; - } - { - job_name = "wireguard"; - static_configs = - let - port = toString config.services.prometheus.exporters.wireguard.port; - in - [ - { targets = [ "${omega-relay-ip}:${port}" ]; } - ]; - } - { - job_name = "fail2ban"; - static_configs = - let - port = toString config.services.prometheus.exporters.fail2ban.port; - in - [ - { targets = [ "${omega-relay-ip}:${port}" ]; } - ]; - - } - { - job_name = "stalwart"; - metrics_path = "/metrics/prometheus"; - scheme = "https"; - basic_auth = { - username = "prometheus_wilkuu"; - password_file = config.sops.secrets."prometheus/stalwart-pass".path; - }; + scrapeConfigs = + (lib.mapAttrsToList (name: dests: { + job_name = name; static_configs = [ { - targets = [ "mail.wilkuu.xyz:443" ]; + targets = dests; } ]; - } - { - job_name = "mikrotik"; - static_configs = - let - port = toString config.services.prometheus.exporters.mikrotik.port; - in - [ - { targets = [ "localhost:${port}" ]; } + }) job_destinations) + ++ [ + { + job_name = "stalwart"; + metrics_path = "/metrics/prometheus"; + scheme = "https"; + basic_auth = { + username = "prometheus_wilkuu"; + password_file = config.sops.secrets."prometheus/stalwart-pass".path; + }; + static_configs = [ + { + targets = [ "mail.wilkuu.xyz:443" ]; + } ]; - - } - ]; + } + ]; }; }) ]; |
