From ad8570f8dbb539935f0feadbee4d2ee93a2978f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 05:22:06 +0100 Subject: [PATCH 1/5] homelab-deploy: add NATS-based deployment system Add homelab-deploy flake input and NixOS module for message-based deployments across the fleet. Configure DEPLOY account in NATS with tiered access control (listener, test-deployer, admin-deployer). Enable listener on vaulttest01 as initial test host. Co-Authored-By: Claude Opus 4.5 --- flake.lock | 22 ++++++++++++ flake.nix | 6 ++++ hosts/vaulttest01/configuration.nix | 16 +++++++++ services/nats/default.nix | 56 +++++++++++++++++++++++++---- terraform/vault/approle.tf | 8 +++++ terraform/vault/secrets.tf | 16 +++++++++ terraform/vault/variables.tf | 21 +++++++++++ 7 files changed, 139 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 72a66d8..e5f9e99 100644 --- a/flake.lock +++ b/flake.lock @@ -21,6 +21,27 @@ "url": "https://git.t-juice.club/torjus/alerttonotify" } }, + "homelab-deploy": { + "inputs": { + "nixpkgs": [ + "nixpkgs-unstable" + ] + }, + "locked": { + "lastModified": 1770437282, + "narHash": "sha256-7C6hheIP8JUkK0Aoib/lQ4xbOaXHoqSe9SJjU2u3t/Q=", + "ref": "master", + "rev": "cf3b1ce2c9e85ad954d8c230161553c5473e9579", + "revCount": 12, + "type": "git", + "url": "https://git.t-juice.club/torjus/homelab-deploy" + }, + "original": { + "ref": "master", + "type": "git", + "url": "https://git.t-juice.club/torjus/homelab-deploy" + } + }, "labmon": { "inputs": { "nixpkgs": [ @@ -97,6 +118,7 @@ "root": { "inputs": { "alerttonotify": "alerttonotify", + "homelab-deploy": "homelab-deploy", "labmon": "labmon", "nixos-exporter": "nixos-exporter", "nixpkgs": "nixpkgs", diff --git a/flake.nix b/flake.nix index ebcbd6c..ebd8aeb 100644 --- a/flake.nix +++ b/flake.nix @@ -21,6 +21,10 @@ url = "git+https://git.t-juice.club/torjus/nixos-exporter"; inputs.nixpkgs.follows = "nixpkgs-unstable"; }; + homelab-deploy = { + url = "git+https://git.t-juice.club/torjus/homelab-deploy?ref=master"; + inputs.nixpkgs.follows = "nixpkgs-unstable"; + }; }; outputs = @@ -32,6 +36,7 @@ alerttonotify, labmon, nixos-exporter, + homelab-deploy, ... }@inputs: let @@ -58,6 +63,7 @@ ) sops-nix.nixosModules.sops nixos-exporter.nixosModules.default + homelab-deploy.nixosModules.default ./modules/homelab ]; allSystems = [ diff --git a/hosts/vaulttest01/configuration.nix b/hosts/vaulttest01/configuration.nix index fd2bb57..5701ddf 100644 --- a/hosts/vaulttest01/configuration.nix +++ b/hosts/vaulttest01/configuration.nix @@ -101,6 +101,22 @@ in services = [ "vault-test" ]; }; + # Homelab-deploy listener NKey + vault.secrets.homelab-deploy-nkey = { + secretPath = "shared/homelab-deploy/listener-nkey"; + extractKey = "nkey"; + }; + + # Enable homelab-deploy listener + services.homelab-deploy.listener = { + enable = true; + tier = "test"; + role = "vault"; + natsUrl = "nats://nats1.home.2rjus.net:4222"; + nkeyFile = "/run/secrets/homelab-deploy-nkey"; + flakeUrl = "git+https://git.t-juice.club/torjus/nixos-servers.git"; + }; + # Create a test service that uses the secret systemd.services.vault-test = { description = "Test Vault secret fetching"; diff --git a/services/nats/default.nix b/services/nats/default.nix index 20b7efa..fdb7ce3 100644 --- a/services/nats/default.nix +++ b/services/nats/default.nix @@ -1,16 +1,18 @@ { ... }: { - homelab.monitoring.scrapeTargets = [{ - job_name = "nats"; - port = 7777; - }]; + homelab.monitoring.scrapeTargets = [ + { + job_name = "nats"; + port = 7777; + } + ]; services.prometheus.exporters.nats = { enable = true; url = "http://localhost:8222"; extraFlags = [ - "-varz" # General server info - "-connz" # Connection info + "-varz" # General server info + "-connz" # Connection info "-jsz=all" # JetStream info ]; }; @@ -38,6 +40,48 @@ } ]; }; + + DEPLOY = { + users = [ + # Shared listener (all hosts use this) + { + nkey = "UCCZJSUGLCSLBBKHBPL4QA66TUMQUGIXGLIFTWDEH43MGWM3LDD232X4"; + permissions = { + subscribe = [ + "deploy.test.>" + "deploy.prod.>" + "deploy.discover" + ]; + publish = [ + "deploy.responses.>" + "deploy.discover" + ]; + }; + } + # Test deployer (MCP without admin) + { + nkey = "UBR66CX2ZNY5XNVQF5VBG4WFAF54LSGUYCUNNCEYRILDQ4NXDAD2THZU"; + permissions = { + publish = [ + "deploy.test.>" + "deploy.discover" + ]; + subscribe = [ + "deploy.responses.>" + "deploy.discover" + ]; + }; + } + # Admin deployer (full access) + { + nkey = "UD2BFB7DLM67P5UUVCKBUJMCHADIZLGGVUNSRLZE2ZC66FW2XT44P73Y"; + permissions = { + publish = [ "deploy.>" ]; + subscribe = [ "deploy.>" ]; + }; + } + ]; + }; }; system_account = "ADMIN"; jetstream = { diff --git a/terraform/vault/approle.tf b/terraform/vault/approle.tf index 86269e6..f09903f 100644 --- a/terraform/vault/approle.tf +++ b/terraform/vault/approle.tf @@ -89,6 +89,14 @@ locals { "secret/data/hosts/nix-cache01/*", ] } + + # Vault test host with homelab-deploy access + "vaulttest01" = { + paths = [ + "secret/data/hosts/vaulttest01/*", + "secret/data/shared/homelab-deploy/*", + ] + } } } diff --git a/terraform/vault/secrets.tf b/terraform/vault/secrets.tf index 5db851c..bfc9bd7 100644 --- a/terraform/vault/secrets.tf +++ b/terraform/vault/secrets.tf @@ -92,6 +92,22 @@ locals { auto_generate = false data = { token = var.actions_token_1 } } + + # Homelab-deploy NKeys + "shared/homelab-deploy/listener-nkey" = { + auto_generate = false + data = { nkey = var.homelab_deploy_listener_nkey } + } + + "shared/homelab-deploy/test-deployer-nkey" = { + auto_generate = false + data = { nkey = var.homelab_deploy_test_deployer_nkey } + } + + "shared/homelab-deploy/admin-deployer-nkey" = { + auto_generate = false + data = { nkey = var.homelab_deploy_admin_deployer_nkey } + } } } diff --git a/terraform/vault/variables.tf b/terraform/vault/variables.tf index 28ba799..f53cb4f 100644 --- a/terraform/vault/variables.tf +++ b/terraform/vault/variables.tf @@ -52,3 +52,24 @@ variable "actions_token_1" { sensitive = true } +variable "homelab_deploy_listener_nkey" { + description = "NKey seed for homelab-deploy listeners" + type = string + default = "PLACEHOLDER" + sensitive = true +} + +variable "homelab_deploy_test_deployer_nkey" { + description = "NKey seed for test-tier deployer" + type = string + default = "PLACEHOLDER" + sensitive = true +} + +variable "homelab_deploy_admin_deployer_nkey" { + description = "NKey seed for admin deployer" + type = string + default = "PLACEHOLDER" + sensitive = true +} + -- 2.49.1 From 0643f232810842822eb467a414d62308053c72be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 05:29:29 +0100 Subject: [PATCH 2/5] vaulttest01: add vault secret dependency to listener Ensure homelab-deploy-listener waits for the NKey secret to be fetched from Vault before starting. Co-Authored-By: Claude Opus 4.5 --- hosts/vaulttest01/configuration.nix | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hosts/vaulttest01/configuration.nix b/hosts/vaulttest01/configuration.nix index 5701ddf..4270abf 100644 --- a/hosts/vaulttest01/configuration.nix +++ b/hosts/vaulttest01/configuration.nix @@ -117,6 +117,12 @@ in flakeUrl = "git+https://git.t-juice.club/torjus/nixos-servers.git"; }; + # Ensure listener starts after vault secret is available + systemd.services.homelab-deploy-listener = { + after = [ "vault-secret-homelab-deploy-nkey.service" ]; + requires = [ "vault-secret-homelab-deploy-nkey.service" ]; + }; + # Create a test service that uses the secret systemd.services.vault-test = { description = "Test Vault secret fetching"; -- 2.49.1 From 13c3897e867aff4d20b9afc4116480204bf613c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 05:45:54 +0100 Subject: [PATCH 3/5] flake: update homelab-deploy, add to devShell Update homelab-deploy to include bugfix. Add CLI to devShell for easier testing and deployment operations. Co-Authored-By: Claude Opus 4.5 --- flake.lock | 8 ++++---- flake.nix | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index e5f9e99..64264c2 100644 --- a/flake.lock +++ b/flake.lock @@ -28,11 +28,11 @@ ] }, "locked": { - "lastModified": 1770437282, - "narHash": "sha256-7C6hheIP8JUkK0Aoib/lQ4xbOaXHoqSe9SJjU2u3t/Q=", + "lastModified": 1770442013, + "narHash": "sha256-JZbm6X0A770bb+VlbJYvRHrVqWOSsmu6Hn4B8nvsPc8=", "ref": "master", - "rev": "cf3b1ce2c9e85ad954d8c230161553c5473e9579", - "revCount": 12, + "rev": "71d6aa8b614f557d029bfc2e64375c812ed7bc10", + "revCount": 19, "type": "git", "url": "https://git.t-juice.club/torjus/homelab-deploy" }, diff --git a/flake.nix b/flake.nix index ebd8aeb..95cbcb6 100644 --- a/flake.nix +++ b/flake.nix @@ -225,11 +225,12 @@ { pkgs }: { default = pkgs.mkShell { - packages = with pkgs; [ - ansible - opentofu - openbao + packages = [ + pkgs.ansible + pkgs.opentofu + pkgs.openbao (pkgs.callPackage ./scripts/create-host { }) + homelab-deploy.packages.${pkgs.system}.default ]; }; } -- 2.49.1 From 7933127d77ca71ef8f2940b6ad39da694157d8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 06:41:03 +0100 Subject: [PATCH 4/5] system: enable homelab-deploy listener for all vault hosts Add system/homelab-deploy.nix module that automatically enables the listener on all hosts with vault.enable=true. Uses homelab.host.tier and homelab.host.role for NATS subject subscriptions. - Add homelab-deploy access to all host AppRole policies - Remove manual listener config from vaulttest01 (now handled by system module) Co-Authored-By: Claude Opus 4.5 --- hosts/vaulttest01/configuration.nix | 22 -------------------- system/default.nix | 1 + system/homelab-deploy.nix | 31 +++++++++++++++++++++++++++++ terraform/vault/approle.tf | 10 +++++++++- 4 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 system/homelab-deploy.nix diff --git a/hosts/vaulttest01/configuration.nix b/hosts/vaulttest01/configuration.nix index 4270abf..fd2bb57 100644 --- a/hosts/vaulttest01/configuration.nix +++ b/hosts/vaulttest01/configuration.nix @@ -101,28 +101,6 @@ in services = [ "vault-test" ]; }; - # Homelab-deploy listener NKey - vault.secrets.homelab-deploy-nkey = { - secretPath = "shared/homelab-deploy/listener-nkey"; - extractKey = "nkey"; - }; - - # Enable homelab-deploy listener - services.homelab-deploy.listener = { - enable = true; - tier = "test"; - role = "vault"; - natsUrl = "nats://nats1.home.2rjus.net:4222"; - nkeyFile = "/run/secrets/homelab-deploy-nkey"; - flakeUrl = "git+https://git.t-juice.club/torjus/nixos-servers.git"; - }; - - # Ensure listener starts after vault secret is available - systemd.services.homelab-deploy-listener = { - after = [ "vault-secret-homelab-deploy-nkey.service" ]; - requires = [ "vault-secret-homelab-deploy-nkey.service" ]; - }; - # Create a test service that uses the secret systemd.services.vault-test = { description = "Test Vault secret fetching"; diff --git a/system/default.nix b/system/default.nix index 7e3c80f..a4d9949 100644 --- a/system/default.nix +++ b/system/default.nix @@ -3,6 +3,7 @@ imports = [ ./acme.nix ./autoupgrade.nix + ./homelab-deploy.nix ./monitoring ./motd.nix ./packages.nix diff --git a/system/homelab-deploy.nix b/system/homelab-deploy.nix new file mode 100644 index 0000000..05a55a8 --- /dev/null +++ b/system/homelab-deploy.nix @@ -0,0 +1,31 @@ +{ config, lib, ... }: + +let + cfg = config.vault; + hostCfg = config.homelab.host; +in +{ + config = lib.mkIf cfg.enable { + # Fetch listener NKey from Vault + vault.secrets.homelab-deploy-nkey = { + secretPath = "shared/homelab-deploy/listener-nkey"; + extractKey = "nkey"; + }; + + # Enable homelab-deploy listener + services.homelab-deploy.listener = { + enable = true; + tier = hostCfg.tier; + role = hostCfg.role; + natsUrl = "nats://nats1.home.2rjus.net:4222"; + nkeyFile = "/run/secrets/homelab-deploy-nkey"; + flakeUrl = "git+https://git.t-juice.club/torjus/nixos-servers.git"; + }; + + # Ensure listener starts after vault secret is available + systemd.services.homelab-deploy-listener = { + after = [ "vault-secret-homelab-deploy-nkey.service" ]; + requires = [ "vault-secret-homelab-deploy-nkey.service" ]; + }; + }; +} diff --git a/terraform/vault/approle.tf b/terraform/vault/approle.tf index f09903f..b1ee161 100644 --- a/terraform/vault/approle.tf +++ b/terraform/vault/approle.tf @@ -30,6 +30,7 @@ locals { paths = [ "secret/data/hosts/ha1/*", "secret/data/shared/backup/*", + "secret/data/shared/homelab-deploy/*", ] } @@ -38,6 +39,7 @@ locals { "secret/data/hosts/monitoring01/*", "secret/data/shared/backup/*", "secret/data/shared/nats/*", + "secret/data/shared/homelab-deploy/*", ] extra_policies = ["prometheus-metrics"] } @@ -46,18 +48,21 @@ locals { "nats1" = { paths = [ "secret/data/hosts/nats1/*", + "secret/data/shared/homelab-deploy/*", ] } "jelly01" = { paths = [ "secret/data/hosts/jelly01/*", + "secret/data/shared/homelab-deploy/*", ] } "pgdb1" = { paths = [ "secret/data/hosts/pgdb1/*", + "secret/data/shared/homelab-deploy/*", ] } @@ -66,6 +71,7 @@ locals { paths = [ "secret/data/hosts/ns1/*", "secret/data/shared/dns/*", + "secret/data/shared/homelab-deploy/*", ] } @@ -73,6 +79,7 @@ locals { paths = [ "secret/data/hosts/ns2/*", "secret/data/shared/dns/*", + "secret/data/shared/homelab-deploy/*", ] } @@ -80,6 +87,7 @@ locals { "http-proxy" = { paths = [ "secret/data/hosts/http-proxy/*", + "secret/data/shared/homelab-deploy/*", ] } @@ -87,10 +95,10 @@ locals { "nix-cache01" = { paths = [ "secret/data/hosts/nix-cache01/*", + "secret/data/shared/homelab-deploy/*", ] } - # Vault test host with homelab-deploy access "vaulttest01" = { paths = [ "secret/data/hosts/vaulttest01/*", -- 2.49.1 From c214f8543c40f1118f64ac738a2ac3d8a46a6cbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torjus=20H=C3=A5kestad?= Date: Sat, 7 Feb 2026 06:47:12 +0100 Subject: [PATCH 5/5] homelab: add deploy.enable option with assertion - Add homelab.deploy.enable option (requires vault.enable) - Create shared homelab-deploy Vault policy for all hosts - Enable homelab.deploy on all vault-enabled hosts Co-Authored-By: Claude Opus 4.5 --- flake.lock | 8 ++++---- hosts/ha1/configuration.nix | 1 + hosts/http-proxy/configuration.nix | 1 + hosts/monitoring01/configuration.nix | 1 + hosts/nix-cache01/configuration.nix | 1 + hosts/ns1/configuration.nix | 1 + hosts/ns2/configuration.nix | 1 + hosts/vaulttest01/configuration.nix | 1 + modules/homelab/default.nix | 1 + modules/homelab/deploy.nix | 16 ++++++++++++++++ system/homelab-deploy.nix | 3 +-- terraform/vault/approle.tf | 23 ++++++++++++----------- 12 files changed, 41 insertions(+), 17 deletions(-) create mode 100644 modules/homelab/deploy.nix diff --git a/flake.lock b/flake.lock index 64264c2..6aed47d 100644 --- a/flake.lock +++ b/flake.lock @@ -28,11 +28,11 @@ ] }, "locked": { - "lastModified": 1770442013, - "narHash": "sha256-JZbm6X0A770bb+VlbJYvRHrVqWOSsmu6Hn4B8nvsPc8=", + "lastModified": 1770443536, + "narHash": "sha256-UufZIVggiioMFDSjKx+ifgkDOk9alNSiRmkvc4/+HIA=", "ref": "master", - "rev": "71d6aa8b614f557d029bfc2e64375c812ed7bc10", - "revCount": 19, + "rev": "95b795dcfd86b7b36045bba67e536b3a1c61dd33", + "revCount": 20, "type": "git", "url": "https://git.t-juice.club/torjus/homelab-deploy" }, diff --git a/hosts/ha1/configuration.nix b/hosts/ha1/configuration.nix index dcb8133..ce43676 100644 --- a/hosts/ha1/configuration.nix +++ b/hosts/ha1/configuration.nix @@ -57,6 +57,7 @@ # Vault secrets management vault.enable = true; + homelab.deploy.enable = true; vault.secrets.backup-helper = { secretPath = "shared/backup/password"; extractKey = "password"; diff --git a/hosts/http-proxy/configuration.nix b/hosts/http-proxy/configuration.nix index ab494f1..8524075 100644 --- a/hosts/http-proxy/configuration.nix +++ b/hosts/http-proxy/configuration.nix @@ -61,6 +61,7 @@ "flakes" ]; vault.enable = true; + homelab.deploy.enable = true; nix.settings.tarball-ttl = 0; environment.systemPackages = with pkgs; [ diff --git a/hosts/monitoring01/configuration.nix b/hosts/monitoring01/configuration.nix index 3a95d73..713dbf8 100644 --- a/hosts/monitoring01/configuration.nix +++ b/hosts/monitoring01/configuration.nix @@ -58,6 +58,7 @@ # Vault secrets management vault.enable = true; + homelab.deploy.enable = true; vault.secrets.backup-helper = { secretPath = "shared/backup/password"; extractKey = "password"; diff --git a/hosts/nix-cache01/configuration.nix b/hosts/nix-cache01/configuration.nix index c3192a8..46dcff1 100644 --- a/hosts/nix-cache01/configuration.nix +++ b/hosts/nix-cache01/configuration.nix @@ -55,6 +55,7 @@ "flakes" ]; vault.enable = true; + homelab.deploy.enable = true; nix.settings.tarball-ttl = 0; environment.systemPackages = with pkgs; [ diff --git a/hosts/ns1/configuration.nix b/hosts/ns1/configuration.nix index c5b9e88..aef3c38 100644 --- a/hosts/ns1/configuration.nix +++ b/hosts/ns1/configuration.nix @@ -48,6 +48,7 @@ "flakes" ]; vault.enable = true; + homelab.deploy.enable = true; homelab.host = { role = "dns"; diff --git a/hosts/ns2/configuration.nix b/hosts/ns2/configuration.nix index c49c5e5..c1baca7 100644 --- a/hosts/ns2/configuration.nix +++ b/hosts/ns2/configuration.nix @@ -48,6 +48,7 @@ "flakes" ]; vault.enable = true; + homelab.deploy.enable = true; homelab.host = { role = "dns"; diff --git a/hosts/vaulttest01/configuration.nix b/hosts/vaulttest01/configuration.nix index fd2bb57..570ca31 100644 --- a/hosts/vaulttest01/configuration.nix +++ b/hosts/vaulttest01/configuration.nix @@ -92,6 +92,7 @@ in # Testing config # Enable Vault secrets management vault.enable = true; + homelab.deploy.enable = true; # Define a test secret vault.secrets.test-service = { diff --git a/modules/homelab/default.nix b/modules/homelab/default.nix index a803d45..130c64b 100644 --- a/modules/homelab/default.nix +++ b/modules/homelab/default.nix @@ -1,6 +1,7 @@ { ... }: { imports = [ + ./deploy.nix ./dns.nix ./host.nix ./monitoring.nix diff --git a/modules/homelab/deploy.nix b/modules/homelab/deploy.nix new file mode 100644 index 0000000..38cae58 --- /dev/null +++ b/modules/homelab/deploy.nix @@ -0,0 +1,16 @@ +{ config, lib, ... }: + +{ + options.homelab.deploy = { + enable = lib.mkEnableOption "homelab-deploy listener for NATS-based deployments"; + }; + + config = { + assertions = [ + { + assertion = config.homelab.deploy.enable -> config.vault.enable; + message = "homelab.deploy.enable requires vault.enable to be true (needed for NKey secret)"; + } + ]; + }; +} diff --git a/system/homelab-deploy.nix b/system/homelab-deploy.nix index 05a55a8..68edc04 100644 --- a/system/homelab-deploy.nix +++ b/system/homelab-deploy.nix @@ -1,11 +1,10 @@ { config, lib, ... }: let - cfg = config.vault; hostCfg = config.homelab.host; in { - config = lib.mkIf cfg.enable { + config = lib.mkIf config.homelab.deploy.enable { # Fetch listener NKey from Vault vault.secrets.homelab-deploy-nkey = { secretPath = "shared/homelab-deploy/listener-nkey"; diff --git a/terraform/vault/approle.tf b/terraform/vault/approle.tf index b1ee161..6f2fd05 100644 --- a/terraform/vault/approle.tf +++ b/terraform/vault/approle.tf @@ -4,6 +4,17 @@ resource "vault_auth_backend" "approle" { path = "approle" } +# Shared policy for homelab-deploy (all hosts need this for NATS-based deployments) +resource "vault_policy" "homelab_deploy" { + name = "homelab-deploy" + + policy = <