diff --git a/modules/hosts/janus/README.md b/modules/hosts/janus/README.md index eb5c36b..4ebe025 100644 --- a/modules/hosts/janus/README.md +++ b/modules/hosts/janus/README.md @@ -17,6 +17,9 @@ Insert generated runtime CA material into `modules/hosts/janus/secrets.yaml` und - `/tmp/janus-step-ca-bootstrap/step/secrets/intermediate_ca_key` -> `intermediate_ca_key` - `/tmp/janus-step-ca-bootstrap/step/secrets/ssh_host_ca_key` -> `ssh_host_ca_key` - `/tmp/janus-step-ca-bootstrap/step/secrets/ssh_user_ca_key` -> `ssh_user_ca_key` +- `/tmp/janus-step-ca-bootstrap/step/config/ca.json` -> `admin_provisioner_encrypted_key` (copy `authority.provisioners[].encryptedKey` for the `admin` JWK provisioner) + +If you are only validating wiring first, `admin_provisioner_encrypted_key` can be an encrypted placeholder and replaced later. If rotating provisioner password, also set: @@ -24,7 +27,7 @@ If rotating provisioner password, also set: Secret source-of-truth after this split: -- `modules/hosts/janus/secrets.yaml`: Janus runtime CA secrets only (`ca_password`, `intermediate_ca_crt`, `intermediate_ca_key`, `ssh_host_ca_key`, `ssh_user_ca_key`) +- `modules/hosts/janus/secrets.yaml`: Janus runtime CA secrets (`ca_password`, `intermediate_ca_crt`, `intermediate_ca_key`, `ssh_host_ca_key`, `ssh_user_ca_key`, `admin_provisioner_encrypted_key`) - `keys/secrets.yaml`: shared Janus provisioner secret (`janus.admin_jwk`) consumed by `step-ssh-host` across hosts Then update public artifacts in repo from generated output: diff --git a/modules/hosts/janus/default.nix b/modules/hosts/janus/default.nix index 64dff35..b76a063 100644 --- a/modules/hosts/janus/default.nix +++ b/modules/hosts/janus/default.nix @@ -39,6 +39,7 @@ in intermediateKey = "janus/intermediate_ca_key"; sshHostCaKey = "janus/ssh_host_ca_key"; sshUserCaKey = "janus/ssh_user_ca_key"; + adminProvisionerEncryptedKey = "janus/admin_provisioner_encrypted_key"; }; }; mtls = { diff --git a/modules/hosts/janus/secrets.yaml b/modules/hosts/janus/secrets.yaml index 6c6a053..7ef0ea0 100644 --- a/modules/hosts/janus/secrets.yaml +++ b/modules/hosts/janus/secrets.yaml @@ -4,6 +4,7 @@ janus: intermediate_ca_key: ENC[AES256_GCM,data:wrzE8pgSZrtMA2jyLuVhMFxr47ICDBUkqOFyFJex9h3g,iv:I3Sz07e+cFDOUunCu7ZGcAjckRJDy3NmTXoB0vtA0HI=,tag:inANSVzyAzWlX4J5pheY8A==,type:str] ssh_host_ca_key: ENC[AES256_GCM,data:IT3PcnbrexRDvxoVEyyxYBx7+Brm3SzVP3Yr2UGkFjeH,iv:edfMiRR+EVx9veTH+mEAFtEdC/rlW8uU5jMD8UX2Ylo=,tag:KKS/2GQE8V6obbjfMLspug==,type:str] ssh_user_ca_key: ENC[AES256_GCM,data:w3gc97CYUODNxk0gIyghd9PCksDVvrdvl8j/NQe/tCtw,iv:WbWlVCPV7niVHEJ9sVcO9SAcLSfquE/bvcR5KClxszk=,tag:08uj+lv9MflYOtQh28K6pw==,type:str] + admin_provisioner_encrypted_key: ENC[AES256_GCM,data:/EdlRiPn/LHpQk5sA9VVYTz/U4i6Ag==,iv:r6Rm38L1R4xsnyR97e3J2FdH4KWYewZdADosv9WLAXw=,tag:MnWq9lI7GAXN0FmGTa9zmQ==,type:str] sops: age: - enc: | @@ -24,7 +25,7 @@ sops: ZT3ScL4PAYhH+lxciJK4tit53taevbp5o0jFibaup4ByDvgj7HPclw== -----END AGE ENCRYPTED FILE----- recipient: age1gvplss0ddmyf6vpjy363wu3n057vhm0j6n7tc94cxd8kadapypws5mtaj0 - lastmodified: "2026-07-04T03:42:39Z" - mac: ENC[AES256_GCM,data:qBuVJo9F5IiQGDqji0ZfNH6MYYLgaVuEoHGsvejw1ffFjYeqGI5LrZmez8xTx8dymQADCDYINZNYlKxgXaXdGqrlKaU8GzuWeF7M0Ad43+S/8DbP2OGPXERHAH+IWVjvzlurXs4tWE4ULw4PbsVpWqOBaSk603N7cuy7Ztwfk8M=,iv:lceVJAeZi8FwKo8DAET1hgUi54Bu+0kjWcGEZxs5wCA=,tag:mFkqan9BHtI6NkxqbjkIcQ==,type:str] + lastmodified: "2026-07-04T04:20:22Z" + mac: ENC[AES256_GCM,data:N0+lAFghaas9fg9PMcsaMPKJbYmnSrgPIFPsEuC749nzMX1IfQVaTLYm6ziZiq8TpscGR1tBqzw2eXiSZG4XfI44naEzO8CvY87aaZAL19/c/j4XE8eBqv1RLsLMbBIYYs8h6aZXCwhWUa3bwFukjueD0iu2X3md6V4T+4RnYSE=,iv:Xd/DCSDymp+KURHjM3Cht8oqbyN/k5+eybdi3pK5eww=,tag:tmDJoo5RKXQTnVjqkXsZ1w==,type:str] unencrypted_suffix: _unencrypted version: 3.13.1 diff --git a/modules/services/step-ca/ca.json b/modules/services/step-ca/ca.json deleted file mode 100644 index e6912aa..0000000 --- a/modules/services/step-ca/ca.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "root": "/etc/step-ca/certs/root_ca.crt", - "federatedRoots": null, - "crt": "/etc/step-ca/certs/intermediate_ca.crt", - "key": "/etc/step-ca/secrets/intermediate_ca_key", - "address": ":443", - "insecureAddress": "", - "dnsNames": [ - "janus.john-stream.com", - "192.168.1.113" - ], - "ssh": { - "hostKey": "/etc/step-ca/secrets/ssh_host_ca_key", - "userKey": "/etc/step-ca/secrets/ssh_user_ca_key" - }, - "logger": { - "format": "text" - }, - "db": { - "type": "badgerv2", - "dataSource": "/var/lib/step-ca/db", - "badgerFileLoadingMode": "" - }, - "authority": { - "provisioners": [ - { - "type": "ACME", - "name": "acme" - }, - { - "type": "SSHPOP", - "name": "sshpop", - "claims": { - "enableSSHCA": true - } - }, - { - "type": "JWK", - "name": "admin", - "key": { - "use": "sig", - "kty": "EC", - "kid": "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8", - "crv": "P-256", - "alg": "ES256", - "x": "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA", - "y": "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME" - }, - "encryptedKey": "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiUVJnTnJVTF9KcmxJYkJMVTlGNVRPZyJ9.DMu7xBNCq5pr-_--YTxNr5Hrcqy6ZmSVHsWurfVXL7Hk0Q3vyYRxiw.h-CnFiYc-DhxThI3.plx3_Qa_0kU-2TwnqFNfAfGnCpfQ2e0iiCMLruNHbLMnHeXQ1BysHBqps45_02zZXIRdHoDgYGtXRSfcdUYYoS0pLoPzC6m301ZFNSAFdRVlSZ3Q6VmWdixPXXnEB4EgSKTT_wxR33L8t9OpFzD85KfY-b_Un1l99ufjCnfg-EYkcICTn_G4-8bcW3eFIvJ6setzu-l0jHMhLQdIweqncn9on9xBXBD-ANhZfP95P2BJt-APqCi8eqiAvn_vClovdg0PxzRwOVDvWREz66FDw-HTU7xDtGO9hACopT5tfZOXDoykgZw1mJsq9NEq9ZzvKG2hvyk1UXtExxrNtFo.5q1OfGU4Amo4Si-vpeI42g", - "claims": { - "enableSSHCA": true, - "disableRenewal": false, - "allowRenewalAfterExpiry": false, - "disableSmallstepExtensions": false - }, - "options": { - "x509": {}, - "ssh": {} - } - } - ], - "template": {}, - "backdate": "1m0s" - }, - "tls": { - "cipherSuites": [ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" - ], - "minVersion": 1.2, - "maxVersion": 1.3, - "renegotiation": false - }, - "templates": { - "ssh": { - "user": [ - { - "name": "config.tpl", - "type": "snippet", - "template": "templates/ssh/config.tpl", - "path": "~/.ssh/config", - "comment": "#" - }, - { - "name": "step_includes.tpl", - "type": "prepend-line", - "template": "templates/ssh/step_includes.tpl", - "path": "${STEPPATH}/ssh/includes", - "comment": "#" - }, - { - "name": "step_config.tpl", - "type": "file", - "template": "templates/ssh/step_config.tpl", - "path": "ssh/config", - "comment": "#" - }, - { - "name": "known_hosts.tpl", - "type": "file", - "template": "templates/ssh/known_hosts.tpl", - "path": "ssh/known_hosts", - "comment": "#" - } - ], - "host": [ - { - "name": "sshd_config.tpl", - "type": "snippet", - "template": "templates/ssh/sshd_config.tpl", - "path": "/etc/ssh/sshd_config", - "comment": "#", - "requires": [ - "Certificate", - "Key" - ] - }, - { - "name": "ca.tpl", - "type": "snippet", - "template": "templates/ssh/ca.tpl", - "path": "/etc/ssh/ca.pub", - "comment": "#" - } - ] - } - }, - "commonName": "Step Online CA" -} \ No newline at end of file diff --git a/modules/services/step-ca/step-ca.nix b/modules/services/step-ca/step-ca.nix index 8a0eb4a..5a5576b 100644 --- a/modules/services/step-ca/step-ca.nix +++ b/modules/services/step-ca/step-ca.nix @@ -3,11 +3,83 @@ flake.modules.nixos.step-ca = { config, pkgs, lib, ... }: let cfg = config.step-ca; + caAddress = "0.0.0.0"; + caPort = 443; + caPasswordPath = (lib.getAttr cfg.secrets.caPassword config.sops.secrets).path; intermediateCrtPath = (lib.getAttr cfg.secrets.intermediateCrt config.sops.secrets).path; intermediateKeyPath = (lib.getAttr cfg.secrets.intermediateKey config.sops.secrets).path; sshHostCaKeyPath = (lib.getAttr cfg.secrets.sshHostCaKey config.sops.secrets).path; sshUserCaKeyPath = (lib.getAttr cfg.secrets.sshUserCaKey config.sops.secrets).path; + adminProvisionerEncryptedKeyValue = + (lib.getAttr cfg.secrets.adminProvisionerEncryptedKey config.sops.placeholder); + + renderedStepCaConfig = builtins.toJSON { + root = cfg.rootCertPath; + crt = intermediateCrtPath; + key = intermediateKeyPath; + address = "${caAddress}:${toString caPort}"; + dnsNames = cfg.dnsNames; + + ssh = { + hostKey = sshHostCaKeyPath; + userKey = sshUserCaKeyPath; + }; + + db = { + type = "badgerv2"; + dataSource = "/var/lib/step-ca/db"; + }; + + authority = { + backdate = "1m0s"; + provisioners = [ + { + type = "ACME"; + name = "acme"; + } + { + type = "SSHPOP"; + name = "sshpop"; + claims.enableSSHCA = true; + } + { + type = "JWK"; + name = "admin"; + key = { + use = "sig"; + kty = "EC"; + kid = "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8"; + crv = "P-256"; + alg = "ES256"; + x = "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA"; + y = "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME"; + }; + encryptedKey = adminProvisionerEncryptedKeyValue; + claims = { + enableSSHCA = true; + disableRenewal = false; + allowRenewalAfterExpiry = false; + disableSmallstepExtensions = false; + }; + options = { + x509 = { }; + ssh = { }; + }; + } + ]; + }; + + tls = { + cipherSuites = [ + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" + ]; + minVersion = 1.2; + maxVersion = 1.3; + renegotiation = false; + }; + }; in { options.step-ca = { @@ -44,6 +116,10 @@ description = "SOPS key for the Step SSH user CA private key."; type = lib.types.str; }; + adminProvisionerEncryptedKey = lib.mkOption { + description = "SOPS key for the Step CA admin provisioner encrypted key."; + type = lib.types.str; + }; }; }; @@ -54,108 +130,66 @@ owner = "step-ca"; group = "step-ca"; mode = "0400"; + restartUnits = [ "step-ca.service" ]; }; sops.secrets."${cfg.secrets.intermediateCrt}" = { sopsFile = cfg.secrets.sopsFile; owner = "step-ca"; group = "step-ca"; mode = "0400"; + restartUnits = [ "step-ca.service" ]; }; sops.secrets."${cfg.secrets.intermediateKey}" = { sopsFile = cfg.secrets.sopsFile; owner = "step-ca"; group = "step-ca"; mode = "0400"; + restartUnits = [ "step-ca.service" ]; }; sops.secrets."${cfg.secrets.sshHostCaKey}" = { sopsFile = cfg.secrets.sopsFile; owner = "step-ca"; group = "step-ca"; mode = "0400"; + restartUnits = [ "step-ca.service" ]; }; sops.secrets."${cfg.secrets.sshUserCaKey}" = { sopsFile = cfg.secrets.sopsFile; owner = "step-ca"; group = "step-ca"; mode = "0400"; + restartUnits = [ "step-ca.service" ]; + }; + sops.secrets."${cfg.secrets.adminProvisionerEncryptedKey}" = { + sopsFile = cfg.secrets.sopsFile; + owner = "step-ca"; + group = "step-ca"; + mode = "0400"; + restartUnits = [ "step-ca.service" ]; + }; + + sops.templates."step-ca-config" = { + owner = "step-ca"; + group = "step-ca"; + mode = "0400"; + content = renderedStepCaConfig; }; # https://github.com/NixOS/nixpkgs/blob/nixos-23.05/nixos/modules/services/security/step-ca.nix services.step-ca = { enable = true; openFirewall = true; - address = "0.0.0.0"; - port = 443; + address = caAddress; + port = caPort; intermediatePasswordFile = caPasswordPath; - - # https://smallstep.com/docs/step-ca/configuration/#configuration-options - settings = { - root = cfg.rootCertPath; - crt = intermediateCrtPath; - key = intermediateKeyPath; - dnsNames = cfg.dnsNames; - - ssh = { - hostKey = sshHostCaKeyPath; - userKey = sshUserCaKeyPath; - }; - - db = { - type = "badgerv2"; - dataSource = "/var/lib/step-ca/db"; - }; - - authority = { - backdate = "1m0s"; - provisioners = [ - { - type = "ACME"; - name = "acme"; - } - { - type = "SSHPOP"; - name = "sshpop"; - claims.enableSSHCA = true; - } - { - type = "JWK"; - name = "admin"; - key = { - use = "sig"; - kty = "EC"; - kid = "xoxgOJFbveSLIL2gm1Yu5ZiRb9v8Jxe44F56i3v-Nf8"; - crv = "P-256"; - alg = "ES256"; - x = "zFO8hPx_eH0Iyz7UJI-w8ODMusEKCZ28M76sGWmWYxA"; - y = "XIWLLyKDzqxV9UH-2KeAkKPDrgLoPrxxW9-PzkXggME"; - }; - encryptedKey = "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkEyNTZHQ00iLCJwMmMiOjYwMDAwMCwicDJzIjoiUVJnTnJVTF9KcmxJYkJMVTlGNVRPZyJ9.DMu7xBNCq5pr-_--YTxNr5Hrcqy6ZmSVHsWurfVXL7Hk0Q3vyYRxiw.h-CnFiYc-DhxThI3.plx3_Qa_0kU-2TwnqFNfAfGnCpfQ2e0iiCMLruNHbLMnHeXQ1BysHBqps45_02zZXIRdHoDgYGtXRSfcdUYYoS0pLoPzC6m301ZFNSAFdRVlSZ3Q6VmWdixPXXnEB4EgSKTT_wxR33L8t9OpFzD85KfY-b_Un1l99ufjCnfg-EYkcICTn_G4-8bcW3eFIvJ6setzu-l0jHMhLQdIweqncn9on9xBXBD-ANhZfP95P2BJt-APqCi8eqiAvn_vClovdg0PxzRwOVDvWREz66FDw-HTU7xDtGO9hACopT5tfZOXDoykgZw1mJsq9NEq9ZzvKG2hvyk1UXtExxrNtFo.5q1OfGU4Amo4Si-vpeI42g"; - claims = { - enableSSHCA = true; - disableRenewal = false; - allowRenewalAfterExpiry = false; - disableSmallstepExtensions = false; - }; - options = { - x509 = { }; - ssh = { }; - }; - } - ]; - }; - - tls = { - cipherSuites = [ - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256" - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" - ]; - minVersion = 1.2; - maxVersion = 1.3; - renegotiation = false; - }; - }; }; + # Keep modules/services/step-ca/ca.json as reference-only; runtime config comes from SOPS template. + environment.etc."smallstep/ca.json".source = + lib.mkForce config.sops.templates."step-ca-config".path; + systemd.services.step-ca.restartTriggers = + lib.mkAfter [ config.sops.templates."step-ca-config".path ]; + environment.systemPackages = with pkgs; [ step-ca step-cli