From 4c560abffdc05287d27cf46fa73a9d13131c82ab Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:22:33 +0100 Subject: [PATCH 01/10] split forgejo-runner into own service file for staged deployment - Extract forgejo-runner config from forgejo.nix into forgejo-runner.nix - Move forgejo-runner to stage 2 (requires running Forgejo for token) - Remove all stage-2 secrets from sops.nix (each service file owns its secrets) - Update first-install docs with corrected staged deployment flow - Fixes deployment failure caused by runner crashing with placeholder token --- docs/getting-started/first-install.md | 26 +++---------------- hosts/cryodev-main/services/default.nix | 8 ++++-- .../cryodev-main/services/forgejo-runner.nix | 22 ++++++++++++++++ hosts/cryodev-main/services/forgejo.nix | 12 --------- hosts/cryodev-main/services/sops.nix | 8 +++--- result | 1 - 6 files changed, 36 insertions(+), 41 deletions(-) create mode 100644 hosts/cryodev-main/services/forgejo-runner.nix delete mode 120000 result diff --git a/docs/getting-started/first-install.md b/docs/getting-started/first-install.md index 1d489b3..01b96e4 100644 --- a/docs/getting-started/first-install.md +++ b/docs/getting-started/first-install.md @@ -243,30 +243,13 @@ Services **ohne externe Abhaengigkeiten** aktivieren: ./sops.nix # Stufe 2: Erst nach Schritt 4 aktivieren - # ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) - # ./tailscale.nix # braucht: tailscale/auth-key (Headscale) + # ./forgejo-runner.nix # braucht: forgejo-runner/token (Forgejo) + # ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) + # ./tailscale.nix # braucht: tailscale/auth-key (Headscale) ]; } ``` -Ebenso in `hosts//services/sops.nix` die Secrets-Definitionen wieder -einkommentieren, **aber nur die fuer Stufe-1-Services**: - -```nix -sops = { - defaultSopsFile = ../secrets.yaml; - secrets = { - # "forgejo-runner/token" = { }; # Stufe 2 - "tailscale/auth-key" = { }; - }; -}; -``` - -> **Hinweis:** `tailscale/auth-key` muss in `sops.nix` definiert bleiben, da das -> Tailscale-Modul es referenziert. Es wird aber erst in Schritt 4 mit einem -> echten Wert befuellt. Solange Tailscale nicht importiert ist, hat das keinen -> Effekt. - ### 3.5 Deployen (Stufe 1) ```bash @@ -329,6 +312,7 @@ Nachdem der Server mit Headscale und Forgejo laeuft: { imports = [ ./forgejo.nix + ./forgejo-runner.nix ./headplane.nix ./headscale.nix ./mailserver.nix @@ -341,8 +325,6 @@ Nachdem der Server mit Headscale und Forgejo laeuft: } ``` - Und in `sops.nix` auch `forgejo-runner/token` einkommentieren. - 6. **Erneut deployen**: ```bash diff --git a/hosts/cryodev-main/services/default.nix b/hosts/cryodev-main/services/default.nix index 0bd3c67..485b746 100644 --- a/hosts/cryodev-main/services/default.nix +++ b/hosts/cryodev-main/services/default.nix @@ -1,13 +1,17 @@ { imports = [ + # Stufe 1: Services ohne externe Abhaengigkeiten ./forgejo.nix - ./headplane.nix ./headscale.nix ./mailserver.nix ./netdata.nix ./nginx.nix ./openssh.nix ./sops.nix - ./tailscale.nix + + # Stufe 2: Erst aktivieren wenn Headscale/Forgejo laufen und echte Secrets existieren + # ./forgejo-runner.nix # braucht: forgejo-runner/token (Forgejo) + # ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) + # ./tailscale.nix # braucht: tailscale/auth-key (Headscale) ]; } diff --git a/hosts/cryodev-main/services/forgejo-runner.nix b/hosts/cryodev-main/services/forgejo-runner.nix new file mode 100644 index 0000000..6c8362f --- /dev/null +++ b/hosts/cryodev-main/services/forgejo-runner.nix @@ -0,0 +1,22 @@ +{ + config, + outputs, + constants, + ... +}: + +{ + imports = [ + outputs.nixosModules.forgejo-runner + ]; + + services.forgejo-runner = { + enable = true; + url = "https://${constants.services.forgejo.fqdn}"; + tokenFile = config.sops.secrets."forgejo-runner/token".path; + }; + + sops.secrets."forgejo-runner/token" = { + mode = "0400"; + }; +} diff --git a/hosts/cryodev-main/services/forgejo.nix b/hosts/cryodev-main/services/forgejo.nix index b911241..5d54830 100644 --- a/hosts/cryodev-main/services/forgejo.nix +++ b/hosts/cryodev-main/services/forgejo.nix @@ -8,7 +8,6 @@ { imports = [ outputs.nixosModules.forgejo - outputs.nixosModules.forgejo-runner ]; services.forgejo = { @@ -32,17 +31,6 @@ }; }; - services.forgejo-runner = { - enable = true; - url = "https://${constants.services.forgejo.fqdn}"; - tokenFile = config.sops.secrets."forgejo-runner/token".path; - }; - - sops.secrets."forgejo-runner/token" = { - # gitea-runner user is created by gitea-actions-runner service - mode = "0400"; - }; - services.nginx.virtualHosts."${constants.services.forgejo.fqdn}" = { forceSSL = true; enableACME = true; diff --git a/hosts/cryodev-main/services/sops.nix b/hosts/cryodev-main/services/sops.nix index 8df48e1..ca01a72 100644 --- a/hosts/cryodev-main/services/sops.nix +++ b/hosts/cryodev-main/services/sops.nix @@ -13,9 +13,9 @@ sops = { defaultSopsFile = ../secrets.yaml; # age.keyFile is not set, sops-nix defaults to using /etc/ssh/ssh_host_ed25519_key - secrets = { - "forgejo-runner/token" = { }; - "tailscale/auth-key" = { }; - }; + + # Secrets fuer Stufe-2-Services werden in deren eigenen Dateien definiert: + # forgejo-runner/token -> forgejo-runner.nix + # tailscale/auth-key -> tailscale.nix (via Modul) }; } diff --git a/result b/result deleted file mode 120000 index e931342..0000000 --- a/result +++ /dev/null @@ -1 +0,0 @@ -/nix/store/xmcpz8rawfcbzr528rlnm5v0fmnrd8dj-nixos-system-cryodev-main-25.11.20260309.44bae27 \ No newline at end of file From dbf98e2f22b8c90d29f09b3cffc778e3906bf58e Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:28:47 +0100 Subject: [PATCH 02/10] add .gitignore, fix headscale CLI to use numeric user IDs - Add .gitignore for nix build result symlinks - Fix all headscale CLI commands: --user now requires numeric ID, not username (changed in newer headscale versions) - Add 'headscale users list' step to docs where preauth keys are created --- .gitignore | 2 ++ docs/getting-started/first-install.md | 30 +++++++++++++++++---------- docs/getting-started/new-client.md | 5 ++++- docs/services/headplane.md | 9 ++++---- docs/services/sops.md | 2 +- docs/services/tailscale.md | 7 +++++-- 6 files changed, 36 insertions(+), 19 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..750baeb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +result +result-* diff --git a/docs/getting-started/first-install.md b/docs/getting-started/first-install.md index 01b96e4..9878a3e 100644 --- a/docs/getting-started/first-install.md +++ b/docs/getting-started/first-install.md @@ -204,8 +204,8 @@ Diese Secrets koennen erst nach Schritt 4 erstellt werden. **Jetzt noch nicht ei | Secret | Befehl | Voraussetzung | |--------|--------|---------------| -| `tailscale/auth-key` | `sudo headscale preauthkeys create --expiration 99y --reusable --user default` | Headscale laeuft | -| `headplane/agent_pre_authkey` | `sudo headscale users create headplane-agent && sudo headscale preauthkeys create --expiration 99y --user headplane-agent` | Headscale laeuft | +| `tailscale/auth-key` | Siehe Schritt 4.1-4.2 | Headscale laeuft | +| `headplane/agent_pre_authkey` | Siehe Schritt 4.1-4.2 | Headscale laeuft | | `forgejo-runner/token` | Forgejo Admin Panel > Actions > Runners > Create Runner | Forgejo laeuft | #### Beispiel secrets.yaml (Klartext vor Verschluesselung) @@ -276,20 +276,28 @@ Nachdem der Server mit Headscale und Forgejo laeuft: sudo headscale users create headplane-agent ``` -2. **Preauth-Keys generieren**: +2. **User-IDs ermitteln** (wird fuer die Preauth-Keys benoetigt): ```bash - # Fuer Tailscale - sudo headscale preauthkeys create --expiration 99y --reusable --user default - - # Fuer Headplane Agent - sudo headscale preauthkeys create --expiration 99y --user headplane-agent + sudo headscale users list ``` -3. **Forgejo-Runner-Token** ueber das Forgejo Admin Panel erstellen: + Die Ausgabe zeigt die numerischen IDs (z.B. `1` fuer default, `2` fuer headplane-agent). + +3. **Preauth-Keys generieren** (mit den IDs aus Schritt 2): + + ```bash + # Fuer Tailscale (User-ID von "default" einsetzen) + sudo headscale preauthkeys create --expiration 99y --reusable --user + + # Fuer Headplane Agent (User-ID von "headplane-agent" einsetzen) + sudo headscale preauthkeys create --expiration 99y --user + ``` + +4. **Forgejo-Runner-Token** ueber das Forgejo Admin Panel erstellen: Administration > Actions > Runners > Create new Runner -4. **Secrets ergaenzen**: +5. **Secrets ergaenzen**: ```bash sops hosts//secrets.yaml @@ -306,7 +314,7 @@ Nachdem der Server mit Headscale und Forgejo laeuft: agent_pre_authkey: "..." ``` -5. **Stufe-2-Services aktivieren** in `hosts//services/default.nix`: +6. **Stufe-2-Services aktivieren** in `hosts//services/default.nix`: ```nix { diff --git a/docs/getting-started/new-client.md b/docs/getting-started/new-client.md index 93b1d0a..cc2ec9e 100644 --- a/docs/getting-started/new-client.md +++ b/docs/getting-started/new-client.md @@ -36,7 +36,10 @@ Diese Anleitung beschreibt das Hinzufügen eines **neuen Raspberry Pi Clients** **Auf cryodev-main** (per SSH): ```bash -sudo headscale preauthkeys create --expiration 99y --reusable --user default +# User-ID ermitteln +sudo headscale users list +# Preauth-Key erstellen (User-ID von "default" einsetzen) +sudo headscale preauthkeys create --expiration 99y --reusable --user ``` **Ausgabe notieren!** (z.B. `tskey-preauth-abc123...`) diff --git a/docs/services/headplane.md b/docs/services/headplane.md index e6c807f..458d3fc 100644 --- a/docs/services/headplane.md +++ b/docs/services/headplane.md @@ -25,9 +25,10 @@ nix-shell -p openssl --run 'openssl rand -hex 16' ```bash # First, create a dedicated user sudo headscale users create headplane-agent - -# Then create a reusable pre-auth key -sudo headscale preauthkeys create --expiration 99y --reusable --user headplane-agent +# Find the user ID +sudo headscale users list +# Then create a reusable pre-auth key (use the ID of headplane-agent) +sudo headscale preauthkeys create --expiration 99y --reusable --user ``` ### Add to Secrets @@ -101,7 +102,7 @@ sudo journalctl -u headplane -f Verify the agent pre-auth key is valid: ```bash -sudo headscale preauthkeys list --user headplane-agent +sudo headscale preauthkeys list --user ``` If expired, create a new one and update the secrets file. diff --git a/docs/services/sops.md b/docs/services/sops.md index cf1a044..0f297a7 100644 --- a/docs/services/sops.md +++ b/docs/services/sops.md @@ -153,7 +153,7 @@ netdata: | Mailserver password | `nix-shell -p mkpasswd --run 'mkpasswd -sm bcrypt'` | | Random hex token | `nix-shell -p openssl --run 'openssl rand -hex 16'` | | UUID | `uuidgen` | -| Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user default` | +| Tailscale preauth | `sudo headscale preauthkeys create --expiration 99y --reusable --user ` | ## Updating Keys diff --git a/docs/services/tailscale.md b/docs/services/tailscale.md index 8b0b54c..768a21c 100644 --- a/docs/services/tailscale.md +++ b/docs/services/tailscale.md @@ -14,7 +14,10 @@ Tailscale clients connect to the self-hosted Headscale server to join the mesh V On the Headscale server (cryodev-main): ```bash -sudo headscale preauthkeys create --expiration 99y --reusable --user default +# User-ID ermitteln +sudo headscale users list +# Preauth-Key erstellen (User-ID von "default" einsetzen) +sudo headscale preauthkeys create --expiration 99y --reusable --user ``` ### Add to Secrets @@ -111,7 +114,7 @@ Check the auth key is valid: ```bash # On Headscale server -sudo headscale preauthkeys list --user default +sudo headscale preauthkeys list --user ``` Verify the login server URL is correct in the client configuration. From 7e31405f9188ade602bdfe8bc5a58207a012b3c1 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:33:09 +0100 Subject: [PATCH 03/10] rename admin_key to steffen_key, add forgejo admin account step - Rename SOPS key alias from generic admin_key to steffen_key in .sops.yaml and all docs (keys should identify the person, not a role) - Add step 3.6 to first-install docs: create Forgejo admin account via CLI (required since DISABLE_REGISTRATION is enabled) - Fix cryodev-pi_key comment naming in .sops.yaml --- .sops.yaml | 8 ++++---- docs/getting-started/first-install.md | 23 ++++++++++++++++++++--- docs/getting-started/new-client.md | 8 ++++---- docs/services/sops.md | 12 ++++++------ hosts/cryodev-main/secrets.yaml | 8 ++++---- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/.sops.yaml b/.sops.yaml index 4931118..6c3ae99 100644 --- a/.sops.yaml +++ b/.sops.yaml @@ -1,14 +1,14 @@ keys: - - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t + - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) - &cryodev-main_key age1y6hushuapy0k04mrvvpev0t8lq44w904r596jus44nhkflky0yhqgq2xx6 creation_rules: - path_regex: hosts/cryodev-main/secrets.yaml$ key_groups: - age: - - *admin_key + - *steffen_key - *cryodev-main_key - path_regex: hosts/cryodev-pi/secrets.yaml$ key_groups: - age: - - *admin_key - # - *pi_key # Add pi key here once obtained + - *steffen_key + # - *cryodev-pi_key # Add after Pi installation diff --git a/docs/getting-started/first-install.md b/docs/getting-started/first-install.md index 9878a3e..765c16e 100644 --- a/docs/getting-started/first-install.md +++ b/docs/getting-started/first-install.md @@ -166,14 +166,14 @@ Auf dem **Entwicklungsrechner** den neuen Host-Key in `.sops.yaml` eintragen: ```yaml keys: - - &admin_key age1e8p... # Dein lokaler Admin-Key - - &hostname_key age1abc... # Key von Schritt 3.1 + - &steffen_key age1e8p... # steffen (lokal) + - &hostname_key age1abc... # Key von Schritt 3.1 creation_rules: - path_regex: hosts//secrets.yaml$ key_groups: - age: - - *admin_key + - *steffen_key - *hostname_key ``` @@ -265,6 +265,23 @@ NIX_SSHOPTS="-p 2299" nixos-rebuild switch --flake .# \ Nach diesem Deploy laufen Headscale, Forgejo, Mailserver und Nginx. +### 3.6 Forgejo Admin-Account erstellen + +Beim ersten Start hat Forgejo noch keine Benutzer. Admin-Account per CLI anlegen +(auf dem **Server**): + +```bash +sudo -u forgejo forgejo --config /var/lib/forgejo/custom/conf/app.ini \ + admin user create \ + --username \ + --email @ \ + --password \ + --admin +``` + +> **Hinweis:** Da `DISABLE_REGISTRATION = true` gesetzt ist, koennen neue Accounts +> nur per CLI erstellt werden. + ## Schritt 4: Restliche Secrets generieren und alle Services aktivieren Nachdem der Server mit Headscale und Forgejo laeuft: diff --git a/docs/getting-started/new-client.md b/docs/getting-started/new-client.md index cc2ec9e..8cb29a8 100644 --- a/docs/getting-started/new-client.md +++ b/docs/getting-started/new-client.md @@ -198,17 +198,17 @@ Auf dem Entwicklungsrechner: ```yaml keys: - - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t + - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) - &neuer_pi_key age1xyz... # Der neue Key creation_rules: # ... bestehende Regeln ... - + - path_regex: hosts/neuer-pi/secrets.yaml$ key_groups: - age: - - *admin_key - - *neuer_pi_key + - *steffen_key + - *neuer_pi_key ``` ### 6.5 Secrets erstellen diff --git a/docs/services/sops.md b/docs/services/sops.md index 0f297a7..6a2def2 100644 --- a/docs/services/sops.md +++ b/docs/services/sops.md @@ -31,7 +31,7 @@ Add the host key to `.sops.yaml`: ```yaml keys: - - &admin_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t + - &steffen_key age1e8p35795htf7twrejyugpzw0qja2v33awcw76y4gp6acnxnkzq0s935t4t # steffen (local) - &main_key age1... # cryodev-main - &pi_key age1... # cryodev-pi @@ -39,14 +39,14 @@ creation_rules: - path_regex: hosts/cryodev-main/secrets.yaml$ key_groups: - age: - - *admin_key - - *main_key - + - *steffen_key + - *main_key + - path_regex: hosts/cryodev-pi/secrets.yaml$ key_groups: - age: - - *admin_key - - *pi_key + - *steffen_key + - *pi_key ``` ### 3. Create Secrets File diff --git a/hosts/cryodev-main/secrets.yaml b/hosts/cryodev-main/secrets.yaml index a36dda7..9792645 100644 --- a/hosts/cryodev-main/secrets.yaml +++ b/hosts/cryodev-main/secrets.yaml @@ -1,10 +1,10 @@ tailscale: - auth-key: ENC[AES256_GCM,data:APMZrLYEqywYTmc=,iv:KiFwgR3UXLXCdl9DlR5tJOr8XUyQEeDomPx9hOREhnw=,tag:32quLtu74EIxAgmjH3hvIw==,type:str] + auth-key: ENC[AES256_GCM,data:v5C3DqYJsDKq6oUa/3G6WKxyKeIK4EJLNxWMbKjSbwe5MPtS4sZjFszMviKcEVGW,iv:4G8irABGuVhOYnK15EjbpNQ4B9VY/NdwCrfz+YAMzvA=,tag:0Vhq/TJgx+48frRy30yKFg==,type:str] forgejo-runner: token: ENC[AES256_GCM,data:/i9KVMeEXYwQnn0=,iv:pILMNbhDviifDUFRINi6n9dtGSAeqxKMdBgjYwtXXEM=,tag:JCj5v5BZdZteo0MdTVKREw==,type:str] headplane: cookie_secret: ENC[AES256_GCM,data:HICF31i6yCLZGNeOFYTR3Bp0a7i0UKOvGAvx/pD3NB4=,iv:ZtK8r1YUWnf5Af0Ls341k0w1mZm+D5Rb0E1uS5z/Gdo=,tag:vwM9+4dpcmnjn/wR6Ty/MQ==,type:str] - agent_pre_authkey: ENC[AES256_GCM,data:aYkPZTR4fwArcKQ=,iv:+OhbIpwsyCJ4i4k8eyCKYAHE25F4iUHfdM+CG0+BQd8=,tag:BkT73WPjOv5Lu6dCFBXxWg==,type:str] + agent_pre_authkey: ENC[AES256_GCM,data:QvhPi2lhyP7w6HTeOSS8660NzIY9Q6AOhlOGQXnvz+qYu9vOAMQPOFMZfie5+e8g,iv:X60wVOEUIsTiMHrrd4lId0VpR7VfFDr74p8RGka3+18=,tag:kIvaHrOWIM+VQ+Qz1GiheQ==,type:str] mailserver: accounts: admin: ENC[AES256_GCM,data:gY2k3x3sA98yGNLcSWUr9aC0566MJM2UXhwLtWPUL3PRvxQt0XOzjeiC7ddgbqTAol4dBNeaV0zbFInD,iv:rxp0M9kHMgD73K+RDC562sUpXaJ067eU1CeciAke+LM=,tag:VKobduo/ZULAk17M9LD3bw==,type:str] @@ -31,7 +31,7 @@ sops: MEpGbGlQbVRsM1NxN1JxY2J1MVNTTE0KuIvuM2c1VIXKv0LGLb0NwqtSyBYcRcb1 uiIjNV0UzEt/WvnCeUTMPgIXBHk6jWcaKe13v6MHeha+/CVZ9Su/Lw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-03-14T10:28:25Z" - mac: ENC[AES256_GCM,data:oeT8I9gMIAPnm8wlNUFjn/0UT6qfTA//fLp3USO33FMsNIOWmqt3kB4NsozS+n6ZeMxBVWQZPss8t819DYqv0xQarzfOqQe1idCGCB+7NBFcFP2VLFzkIH+9Wei9AJSlR3BRnzyVaQDi797P6pEXFn/IoQWPWZ8sX8ZKugOfY0w=,iv:RjsKhPcVZBHHLs1W3PDhcseGLV4eawafg0is6KrzhtE=,tag:ifkobUteslEZ78OvkZw8JQ==,type:str] + lastmodified: "2026-03-14T11:30:38Z" + mac: ENC[AES256_GCM,data:CbK8Yd39gpxLd2m5O43UKOW3jU1h4d7NRyQd3IruxEsUgokt1v9W9aXTyXvyv4fnbOaYqGxw7e8a08MECS3GtUuFpXJFK4rWDET2mU2OweoG1h6uPejyg0ejPHa+PMI7dFcADTn6W//6WZcCbQhHrAuISrUG9/JZtOod28SZWp4=,iv:KtDNJnQwgNRETDA17v4jq0rESHADfaAH4cBeCUbeEv4=,tag:825/Y83J270NZ17mTmYMew==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 From 086e760b9ebda3fc5692ecc072612be47a572c93 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:36:13 +0100 Subject: [PATCH 04/10] fix forgejo admin create command to use shell alias --- docs/getting-started/first-install.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/first-install.md b/docs/getting-started/first-install.md index 765c16e..f50c51a 100644 --- a/docs/getting-started/first-install.md +++ b/docs/getting-started/first-install.md @@ -271,15 +271,18 @@ Beim ersten Start hat Forgejo noch keine Benutzer. Admin-Account per CLI anlegen (auf dem **Server**): ```bash -sudo -u forgejo forgejo --config /var/lib/forgejo/custom/conf/app.ini \ - admin user create \ +forgejo admin user create \ --username \ --email @ \ --password \ --admin ``` -> **Hinweis:** Da `DISABLE_REGISTRATION = true` gesetzt ist, koennen neue Accounts +> **Hinweis:** Das `forgejo` Shell-Alias wird vom Modul bereitgestellt und fuehrt +> automatisch den Befehl als `forgejo`-User mit der richtigen Config aus. +> Falls der Alias nicht verfuegbar ist, neue Shell starten (`bash` oder `zsh`). +> +> Da `DISABLE_REGISTRATION = true` gesetzt ist, koennen neue Accounts > nur per CLI erstellt werden. ## Schritt 4: Restliche Secrets generieren und alle Services aktivieren From 82f3be3b9d92efd7aa76d80e2a9270c04a3341d1 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:43:07 +0100 Subject: [PATCH 05/10] fix forgejo-runner token: use SOPS template with TOKEN= prefix The gitea-actions-runner NixOS module expects tokenFile to be an EnvironmentFile containing TOKEN=, but sops-nix writes only the raw secret value. Use a sops template to prepend TOKEN= prefix. --- hosts/cryodev-main/secrets.yaml | 6 +++--- hosts/cryodev-main/services/default.nix | 6 +++--- hosts/cryodev-main/services/forgejo-runner.nix | 8 +++++++- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/hosts/cryodev-main/secrets.yaml b/hosts/cryodev-main/secrets.yaml index 9792645..4e2dda6 100644 --- a/hosts/cryodev-main/secrets.yaml +++ b/hosts/cryodev-main/secrets.yaml @@ -1,7 +1,7 @@ tailscale: auth-key: ENC[AES256_GCM,data:v5C3DqYJsDKq6oUa/3G6WKxyKeIK4EJLNxWMbKjSbwe5MPtS4sZjFszMviKcEVGW,iv:4G8irABGuVhOYnK15EjbpNQ4B9VY/NdwCrfz+YAMzvA=,tag:0Vhq/TJgx+48frRy30yKFg==,type:str] forgejo-runner: - token: ENC[AES256_GCM,data:/i9KVMeEXYwQnn0=,iv:pILMNbhDviifDUFRINi6n9dtGSAeqxKMdBgjYwtXXEM=,tag:JCj5v5BZdZteo0MdTVKREw==,type:str] + token: ENC[AES256_GCM,data:sdnJcyRiTLxXoZDNbEzJAjpiK+iSUH0gV0XwbEQf94IE/6IZz5/zHw==,iv:py+qqp3VAwBGEpYiQwft3jnQS943JaBlrcckColv4f8=,tag:rtmRwW8rpXB6Pv+LSkp+Fw==,type:str] headplane: cookie_secret: ENC[AES256_GCM,data:HICF31i6yCLZGNeOFYTR3Bp0a7i0UKOvGAvx/pD3NB4=,iv:ZtK8r1YUWnf5Af0Ls341k0w1mZm+D5Rb0E1uS5z/Gdo=,tag:vwM9+4dpcmnjn/wR6Ty/MQ==,type:str] agent_pre_authkey: ENC[AES256_GCM,data:QvhPi2lhyP7w6HTeOSS8660NzIY9Q6AOhlOGQXnvz+qYu9vOAMQPOFMZfie5+e8g,iv:X60wVOEUIsTiMHrrd4lId0VpR7VfFDr74p8RGka3+18=,tag:kIvaHrOWIM+VQ+Qz1GiheQ==,type:str] @@ -31,7 +31,7 @@ sops: MEpGbGlQbVRsM1NxN1JxY2J1MVNTTE0KuIvuM2c1VIXKv0LGLb0NwqtSyBYcRcb1 uiIjNV0UzEt/WvnCeUTMPgIXBHk6jWcaKe13v6MHeha+/CVZ9Su/Lw== -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-03-14T11:30:38Z" - mac: ENC[AES256_GCM,data:CbK8Yd39gpxLd2m5O43UKOW3jU1h4d7NRyQd3IruxEsUgokt1v9W9aXTyXvyv4fnbOaYqGxw7e8a08MECS3GtUuFpXJFK4rWDET2mU2OweoG1h6uPejyg0ejPHa+PMI7dFcADTn6W//6WZcCbQhHrAuISrUG9/JZtOod28SZWp4=,iv:KtDNJnQwgNRETDA17v4jq0rESHADfaAH4cBeCUbeEv4=,tag:825/Y83J270NZ17mTmYMew==,type:str] + lastmodified: "2026-03-14T11:38:57Z" + mac: ENC[AES256_GCM,data:gmxyp3XaHeU/CT2lgo14wIbJsKs/JrZmUPhgHwo1XRN5Sf/Su6lHOpVlQS1M6R3+ZlBnS/oEur+y0gydCCqhJK1C3Y5YuUfPlOWOeQWMVxQBqxWkyemvz5KgGseDc9nG09FpoGEYa4sSeuD1J6vRsGcZiOStaA6s8NICWivdWcQ=,iv:cYILLrScr7cFiLx5INbc9z3BT7LaCjLnCH0wdn3lZ1k=,tag:IIRb/Tu8YqWNiHXH7CSOfQ==,type:str] unencrypted_suffix: _unencrypted version: 3.11.0 diff --git a/hosts/cryodev-main/services/default.nix b/hosts/cryodev-main/services/default.nix index 485b746..4b447cd 100644 --- a/hosts/cryodev-main/services/default.nix +++ b/hosts/cryodev-main/services/default.nix @@ -10,8 +10,8 @@ ./sops.nix # Stufe 2: Erst aktivieren wenn Headscale/Forgejo laufen und echte Secrets existieren - # ./forgejo-runner.nix # braucht: forgejo-runner/token (Forgejo) - # ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) - # ./tailscale.nix # braucht: tailscale/auth-key (Headscale) + ./forgejo-runner.nix # braucht: forgejo-runner/token (Forgejo) + ./headplane.nix # braucht: headplane/agent_pre_authkey (Headscale) + ./tailscale.nix # braucht: tailscale/auth-key (Headscale) ]; } diff --git a/hosts/cryodev-main/services/forgejo-runner.nix b/hosts/cryodev-main/services/forgejo-runner.nix index 6c8362f..57d86f2 100644 --- a/hosts/cryodev-main/services/forgejo-runner.nix +++ b/hosts/cryodev-main/services/forgejo-runner.nix @@ -13,10 +13,16 @@ services.forgejo-runner = { enable = true; url = "https://${constants.services.forgejo.fqdn}"; - tokenFile = config.sops.secrets."forgejo-runner/token".path; + tokenFile = config.sops.templates."forgejo-runner-token".path; }; sops.secrets."forgejo-runner/token" = { mode = "0400"; }; + + sops.templates."forgejo-runner-token" = { + content = '' + TOKEN=${config.sops.placeholder."forgejo-runner/token"} + ''; + }; } From da219a52ec8cf562ea69dbd0f519a26869e720eb Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:52:46 +0100 Subject: [PATCH 06/10] fix forgejo-runner: use local URL instead of public FQDN Runner on the same host cannot reach Forgejo via the public HTTPS URL during boot (ACME certs not ready, nginx not fully up). Use the local HTTP endpoint instead. --- hosts/cryodev-main/services/forgejo-runner.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosts/cryodev-main/services/forgejo-runner.nix b/hosts/cryodev-main/services/forgejo-runner.nix index 57d86f2..e2a5c1c 100644 --- a/hosts/cryodev-main/services/forgejo-runner.nix +++ b/hosts/cryodev-main/services/forgejo-runner.nix @@ -12,7 +12,7 @@ services.forgejo-runner = { enable = true; - url = "https://${constants.services.forgejo.fqdn}"; + url = "http://127.0.0.1:${toString constants.services.forgejo.port}"; tokenFile = config.sops.templates."forgejo-runner-token".path; }; From 9f4f8b9c9716b54030e0e8528c3a1a3c851bd5a8 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 12:58:15 +0100 Subject: [PATCH 07/10] complete DNS docs: add www, PTR record, DKIM checklist, fix SSH port --- docs/deployment/dns.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/docs/deployment/dns.md b/docs/deployment/dns.md index 0e00b35..0c72bbc 100644 --- a/docs/deployment/dns.md +++ b/docs/deployment/dns.md @@ -10,6 +10,8 @@ Required DNS records for the cryodev infrastructure. |----------|------|-------|---------| | `@` | A | `` | Main server | | `@` | AAAA | `` | Main server (IPv6) | +| `www` | A | `` | www redirect | +| `www` | AAAA | `` | www redirect (IPv6) | | `mail` | A | `` | Mail server | | `mail` | AAAA | `` | Mail server (IPv6) | @@ -29,7 +31,20 @@ Required DNS records for the cryodev infrastructure. | `@` | MX | `10 mail.cryodev.xyz.` | Mail delivery | | `@` | TXT | `"v=spf1 mx ~all"` | SPF | | `_dmarc` | TXT | `"v=DMARC1; p=none"` | DMARC | -| `mail._domainkey` | TXT | `"v=DKIM1; k=rsa; p=..."` | DKIM | +| `mail._domainkey` | TXT | *(siehe unten)* | DKIM | + +### Reverse DNS (PTR) + +Fuer zuverlaessige Mail-Zustellung muss ein **PTR Record** beim Hosting-Provider +konfiguriert werden (nicht im DNS-Panel der Domain): + +| IP | PTR Value | +|----|-----------| +| `` | `mail.cryodev.xyz` | +| `` | `mail.cryodev.xyz` | + +> **Hinweis:** PTR Records werden beim Hosting-Provider (z.B. Hetzner Robot) +> konfiguriert, nicht im Domain-DNS-Panel. ## Getting the DKIM Key @@ -41,6 +56,18 @@ sudo cat /var/dkim/cryodev.xyz.mail.txt Add this as a TXT record for `mail._domainkey.cryodev.xyz`. +## Complete Checklist + +- [ ] A/AAAA fuer `@` (Root-Domain) +- [ ] A/AAAA fuer `www` +- [ ] A/AAAA fuer `mail` +- [ ] CNAME fuer `git`, `headscale`, `headplane`, `netdata` +- [ ] MX Record +- [ ] TXT fuer SPF (`v=spf1 mx ~all`) +- [ ] TXT fuer DMARC (`v=DMARC1; p=none`) +- [ ] TXT fuer DKIM (`mail._domainkey` -- nach erstem Deploy) +- [ ] PTR Record beim Hosting-Provider (Reverse DNS) + ## Verification ### Check DNS Propagation @@ -60,6 +87,9 @@ dig TXT mail._domainkey.cryodev.xyz # DMARC dig TXT _dmarc.cryodev.xyz + +# Reverse DNS +dig -x ``` ### Online Tools @@ -74,7 +104,7 @@ For initial setup, use low TTLs (300 seconds) to allow quick changes. After verification, increase to: - A/AAAA records: 3600 (1 hour) -- CNAME records: 3600 (1 hour) +- CNAME records: 3600 (1 hour) - MX records: 3600 (1 hour) - TXT records: 3600 (1 hour) @@ -84,7 +114,7 @@ Ensure these ports are open on `cryodev-main`: | Port | Protocol | Service | |------|----------|---------| -| 22 | TCP | SSH | +| 2299 | TCP | SSH | | 80 | TCP | HTTP (ACME/redirect) | | 443 | TCP | HTTPS | | 25 | TCP | SMTP | From bd165bc59232ee7ebb0f96c23563fa697d012102 Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 13:05:14 +0100 Subject: [PATCH 08/10] docs: add Hetzner PTR record setup instructions --- docs/deployment/dns.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/deployment/dns.md b/docs/deployment/dns.md index 0c72bbc..8d0f99b 100644 --- a/docs/deployment/dns.md +++ b/docs/deployment/dns.md @@ -43,8 +43,20 @@ konfiguriert werden (nicht im DNS-Panel der Domain): | `` | `mail.cryodev.xyz` | | `` | `mail.cryodev.xyz` | -> **Hinweis:** PTR Records werden beim Hosting-Provider (z.B. Hetzner Robot) -> konfiguriert, nicht im Domain-DNS-Panel. +#### Hetzner Robot (Dedicated Server) + +1. [robot.hetzner.com](https://robot.hetzner.com) > **Server** > Server auswaehlen +2. **IPs** Tab +3. Bei der IPv4-Adresse auf das **Stift-Symbol** klicken +4. `mail.cryodev.xyz` eintragen und speichern +5. Fuer IPv6: Unter **Subnets** dasselbe fuer die primaere IPv6-Adresse + +#### Hetzner Cloud + +1. [cloud.hetzner.com](https://cloud.hetzner.com) > Server auswaehlen +2. **Networking** Tab +3. Bei "Primary IP" auf die IP klicken > **Reverse DNS** +4. `mail.cryodev.xyz` eintragen (fuer IPv4 und IPv6) ## Getting the DKIM Key From 5225974c2a8b422ad114b644203d87e2db98154d Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 13:25:59 +0100 Subject: [PATCH 09/10] fix headplane: disable strict config validation NixOS headscale module generates a minimal config.yaml with only explicitly set values. Headplane with config_strict=true rejects this because fields like database, derp, dns, listen_addr are missing (headscale fills these with internal defaults). Setting config_strict to false makes headplane tolerate the incomplete config. --- hosts/cryodev-main/services/headplane.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/hosts/cryodev-main/services/headplane.nix b/hosts/cryodev-main/services/headplane.nix index fbc21bb..fc39494 100644 --- a/hosts/cryodev-main/services/headplane.nix +++ b/hosts/cryodev-main/services/headplane.nix @@ -16,6 +16,7 @@ headscale = { url = "http://127.0.0.1:${toString constants.services.headscale.port}"; public_url = "https://${constants.services.headscale.fqdn}"; + config_strict = false; }; }; }; From d623a01ebd50d227f1297acb129f852993cd111d Mon Sep 17 00:00:00 2001 From: steffen Date: Sat, 14 Mar 2026 13:45:08 +0100 Subject: [PATCH 10/10] fix ACME: set default group to nginx for webroot permissions The ACME challenge directory was created with group 'acme' but nginx needs read access to serve challenge responses. Setting defaults.group to 'nginx' ensures all ACME directories are accessible by nginx. --- modules/nixos/nginx/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/nixos/nginx/default.nix b/modules/nixos/nginx/default.nix index d28d7a9..3b2bef1 100644 --- a/modules/nixos/nginx/default.nix +++ b/modules/nixos/nginx/default.nix @@ -62,6 +62,7 @@ in acceptTerms = true; defaults.email = mkDefault "postmaster@${config.networking.domain}"; defaults.webroot = mkDefault "/var/lib/acme/acme-challenge"; + defaults.group = mkDefault "nginx"; }; security.dhparams = mkIf cfg.forceSSL {