summaryrefslogtreecommitdiff
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
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.
-rw-r--r--.gitignore3
-rw-r--r--hosts/apocalypse/default.nix2
-rw-r--r--hosts/apocalypse/firewall.nix29
-rw-r--r--hosts/omega-relay/firewall.nix47
-rw-r--r--hosts/tacitus/network.nix26
-rw-r--r--inventory.nix60
-rw-r--r--modules/default.nix1
-rw-r--r--modules/firewall.nix132
-rw-r--r--modules/prometheus.nix185
9 files changed, 361 insertions, 124 deletions
diff --git a/.gitignore b/.gitignore
index 9e91979..150aa83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@
*.pass
*_ed25519.pub
*_ed25519
-secrets/*.pem
+secrets/*.pem
+.hidden
diff --git a/hosts/apocalypse/default.nix b/hosts/apocalypse/default.nix
index cb81aa7..238ab09 100644
--- a/hosts/apocalypse/default.nix
+++ b/hosts/apocalypse/default.nix
@@ -13,6 +13,8 @@
"127.0.0.1" = [ "apocalypse.local" ];
};
+ wilkuu.services.prometheus.enableExporters = true;
+
services.logind.settings.Login = {
HandleLidSwitch = "suspend";
HandleLidSwitchExternalPower = "lock";
diff --git a/hosts/apocalypse/firewall.nix b/hosts/apocalypse/firewall.nix
index f45d4f7..4bc4b32 100644
--- a/hosts/apocalypse/firewall.nix
+++ b/hosts/apocalypse/firewall.nix
@@ -3,6 +3,7 @@ let
baseTCP = [
22000 # Syncthng
5352 # Zeroconf for spotifyd
+ 22 # ssh
];
baseUDP = [
22000 # Syncthing
@@ -24,7 +25,6 @@ let
];
secureTCP = [
- 22
80
433
5900 # SSH HTTP VNC
@@ -42,23 +42,28 @@ let
];
in
{
- environment.etc.hosts.mode = "0644";
- networking.nftables.enable = true;
- networking.firewall = {
- enable = false;
- checkReversePath = false;
- allowedTCPPorts = baseTCP;
- allowedUDPPorts = baseUDP;
- allowedUDPPortRanges = baseUDPRanges;
- allowedTCPPortRanges = baseTCPRanges;
- interfaces = {
- "nix-laptop" = {
+ wilkuu.firewall = {
+ enable = true;
+ defaultLayer = "external";
+ layers = {
+ internal = {
allowedTCPPorts = secureTCP;
allowedUDPPorts = secureUDP;
allowedUDPPortRanges = secureUDPRanges;
allowedTCPPortRanges = secureTCPRanges;
};
+ external = {
+ allowedTCPPorts = baseTCP;
+ allowedUDPPorts = baseUDP;
+ allowedUDPPortRanges = baseUDPRanges;
+ allowedTCPPortRanges = baseTCPRanges;
+ };
};
+ };
+ environment.etc.hosts.mode = "0644";
+ networking.nftables.enable = true;
+ networking.firewall = {
+ enable = true;
trustedInterfaces = [
"docker0"
"br-*"
diff --git a/hosts/omega-relay/firewall.nix b/hosts/omega-relay/firewall.nix
index 0e82fea..8e35fa3 100644
--- a/hosts/omega-relay/firewall.nix
+++ b/hosts/omega-relay/firewall.nix
@@ -1,5 +1,12 @@
-{ config, lib, ... }:
+{
+ config,
+ lib,
+ inventory,
+ self_name,
+ ...
+}:
let
+ net = inventory.${self_name}.interfaces;
wgHomePort = 16888;
baseTCP = [
20
@@ -23,16 +30,7 @@ let
secureTCP = [
# config.wilkuu.services.mysql.port
- ]
- ++ lib.mapAttrsToList (_: opt: opt.port) (
- lib.filterAttrs (
- _: e:
- let
- evaluated = builtins.tryEval e;
- in
- evaluated.success && e ? enable && e.enable
- ) config.services.prometheus.exporters
- );
+ ];
secureUDP = [
];
@@ -63,7 +61,7 @@ in
useNetworkd = true;
interfaces = {
wg-home = {
- ips = [ "192.168.80.100/24" ];
+ ips = [ "${net.wg-home.ip}/24" ];
extraOptions = {
DNS = "192.168.88.1";
};
@@ -113,24 +111,27 @@ in
linkConfig.RequiredForOnline = "routable";
};
- networking.useDHCP = false;
- networking.useNetworkd = true;
- networking.nftables.enable = true;
- networking.firewall = {
+ wilkuu.firewall = {
enable = true;
- checkReversePath = false;
- allowedTCPPorts = baseTCP;
- allowedUDPPorts = baseUDP;
- allowedUDPPortRanges = baseUDPRanges;
- allowedTCPPortRanges = baseTCPRanges;
- interfaces = {
- "wg-home" = {
+ defaultLayer = "external";
+ layers = {
+ external = {
+ allowedTCPPorts = baseTCP;
+ allowedUDPPorts = baseUDP;
+ allowedUDPPortRanges = baseUDPRanges;
+ allowedTCPPortRanges = baseTCPRanges;
+ };
+ internal = {
allowedTCPPorts = secureTCP;
allowedUDPPorts = secureUDP;
allowedUDPPortRanges = secureUDPRanges;
allowedTCPPortRanges = secureTCPRanges;
};
};
+ };
+ networking.useDHCP = false;
+ networking.useNetworkd = true;
+ networking.firewall = {
trustedInterfaces = [
"docker0"
"br-*"
diff --git a/hosts/tacitus/network.nix b/hosts/tacitus/network.nix
index 513d89c..9b3beef 100644
--- a/hosts/tacitus/network.nix
+++ b/hosts/tacitus/network.nix
@@ -48,27 +48,21 @@ in
];
};
};
- networking = {
- useNetworkd = true;
- nftables.enable = true;
- useDHCP = true;
- firewall = {
- # check enable = true;
- checkReversePath = false;
+
+ wilkuu.firewall = {
+ enable = true;
+ defaultLayer = "internal";
+ layers.internal = {
allowedTCPPorts = baseTCP;
allowedUDPPorts = baseUDP;
allowedUDPPortRanges = baseUDPRanges;
allowedTCPPortRanges = baseTCPRanges;
- # TODO: Figure out how to do FW that allows only on the internal ip range
- #interfaces = {
- # "wg-home" = {
- # allowedTCPPorts = secureTCP;
- # allowedUDPPorts = secureUDP;
- # allowedUDPPortRanges = secureUDPRanges;
- # allowedTCPPortRanges = secureTCPRanges;
- # };
- #};
};
};
+ networking = {
+ useNetworkd = true;
+ nftables.enable = true;
+ useDHCP = true;
+ };
}
diff --git a/inventory.nix b/inventory.nix
index db3d3f9..a476191 100644
--- a/inventory.nix
+++ b/inventory.nix
@@ -7,7 +7,27 @@
./users/wilkuu-server.nix
inputs.stalwart-nix.nixosModules.default
];
- interfaces = { };
+ monitoring = {
+ internal = [
+ "wireguard"
+ "fail2ban"
+ "node"
+ ];
+ };
+ interfaces = {
+ wg-home = {
+ type = "wireguard";
+ layer = "internal";
+ ip = "192.168.80.100";
+ };
+ enp6s18 = {
+ type = "eth-networkd";
+ layer = "external";
+ ip = "45.136.141.133";
+ ip6 = "2a12:bec0:650:128::133/64";
+ };
+ };
+
};
apocalypse = {
type = "desktop";
@@ -16,8 +36,24 @@
nix-modules = [
./users/wilkuu.nix
];
- interfaces = { };
+ monitoring = {
+ internal = [ "node" ];
+ };
+ interfaces = {
+ nix-laptop = {
+ type = "wireguard";
+ layer = "internal";
+ ip = "192.168.80.99";
+ };
+ wifi = {
+ type = "roaming";
+ };
+ eth = {
+ type = "roaming";
+ };
+ };
};
+
tacitus = {
type = "desktop";
system = "x86_64-linux";
@@ -25,7 +61,25 @@
nix-modules = [
./users/wilkuu-server.nix
];
- interfaces = { };
+ monitoring = {
+ local = [
+ "node"
+ "mikrotik"
+ ];
+ };
+ interfaces = {
+ enp7s0 = {
+ type = "eth-networkd";
+ layer = "internal";
+ ip = "192.168.88.5";
+ };
+ lo = {
+ # A little workaround for not needing to go over the net to get own stats
+ type = "roaming"; # TODO Set to something more sensible
+ layer = "local";
+ ip = "localhost";
+ };
+ };
};
# TODO: Support for live images as packages
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" ];
+ }
];
-
- }
- ];
+ }
+ ];
};
})
];