9.5 KiB
9.5 KiB
description, name, applyTo
| description | name | applyTo |
|---|---|---|
| Use when modifying the Janus host, Janus step-ca deployment, Step SSH CA, mTLS issuance, CA bootstrap artifacts, or Janus SOPS secrets. Covers idiomatic Nix, secret placement, and troubleshooting. | Janus Host Instructions | modules/hosts/janus/**, modules/services/step-ca/step-ca.nix, modules/features/step-client.nix |
Janus Host Instructions
Use this instruction when changing Janus host wiring, Janus CA material, or the shared Step CA module that Janus consumes.
Host Intent And Boundaries
- Treat Janus as the homelab certificate authority host. It runs
step-cafor X.509 issuance, SSH user and host certificates, and mTLS certificates. - Assume Janus CA endpoints are private/LAN-only unless the task explicitly changes the exposure model.
- Keep Janus as a concrete host under
modules/hosts/janus; keep reusable behavior in shared modules such asmodules/services/step-ca/step-ca.nix,modules/services/step-ca/ssh-host.nix,modules/features/step-client.nix, andmodules/features/mtls.nix. - Preserve host identity unless the task explicitly renames the deployed CA:
hostname = "janus",flake.nixosConfigurations."${hostname}", andnetworking.hostName = hostname. - Keep Janus-specific paths, DNS names, IP SANs, and CA artifacts in the Janus host area or in Janus-specific option values. Do not move them into generic shared modules unless they become a deliberate module interface.
Required Module Composition
When editing modules/hosts/janus/default.nix, preserve this composition unless the task explicitly changes Janus architecture:
nixos.lxcfor the container host shape.nixos.mysopswithsops.defaultSopsFile = ./secrets.yamlfor host-local encrypted CA runtime material.nixos.step-cato run the CA daemon and render/etc/smallstep/ca.jsonfrom Nix plus SOPS paths.nixos.step-clientto install Step trust bootstrap defaults and root CA material.nixos.ssh-certsso Janus itself participates in SSH host certificate flow.nixos.mtlsso Janus can issue and renew its own mTLS certificate bundle.
Step CA Deployment Pattern
- Prefer structured Nix values rendered with
builtins.toJSONover hand-written JSON strings forca.json. - Prefer the NixOS
services.step-ca.settingsinterface for normalca.jsonsettings. Use asops.templates-rendered config only when the rendered JSON needs sops-nix placeholders or runtime secret paths. - Keep secret-bearing
ca.jsoncontent rendered throughsops.templateswhen it includes values fromconfig.sops.placeholder; do not put decrypted private keys, passwords, or provisioner encrypted private-key values directly in the Nix store. - Continue using
services.step-cafor the daemon contract:enable,settings,address,port,openFirewall,package, andintermediatePasswordFile. - Remember that the NixOS module overrides
services.step-ca.settings.addresswithservices.step-ca.addressplusservices.step-ca.port. Change the high-level address/port options when changing the listener. - Keep the intermediate password as a runtime string path from
config.sops.secrets.<name>.path; do not use a Nix path literal or the password file can be copied to the globally readable Nix store. Do not pass passwords through command-line arguments or environment variables. - Account for upstream NixOS service behavior when debugging: the module writes
/etc/smallstep/ca.json, startsstep-ca.serviceasType=notify, setsDynamicUser = true, usesStateDirectory = "step-ca", and passesintermediatePasswordFilethrough systemdLoadCredential. - If
step-cacannot read key files referenced byca.json, check sops-nix owner/group/mode and systemd credential handling before loosening permissions. The password file path and CA signing key paths are separate contracts. - Keep Step CA runtime state in
/var/lib/step-ca/dbunless the task explicitly migrates storage. If moving away from embedded Badger, document database backup and HA implications. - Keep
root,crt,key,ssh.hostKey, andssh.userKeyinca.jsonpointing at public cert paths or sops-nix materialized secret paths as appropriate. - Use an ACME provisioner for automated X.509 issuance, an
SSHPOPprovisioner for SSH certificate renewal/rekey behavior, and theadminJWK provisioner for explicit administrative or scripted issuance. Ensure JWK claims includeenableSSHCA = truewhen it is expected to sign SSH certificates. - Treat Nix and SOPS as the source of truth. If
step ca provisioner add,remove, orupdateis used for investigation, copy the intended result back into Nix/SOPS and reload or restartstep-ca; do not leave liveca.jsondrift on the host. - Use
authority.claimsor provisioner-level claims for certificate lifetimes, renewal behavior, and SSH CA enablement. AvoidallowRenewalAfterExpiryunless the task explicitly accepts the added risk for intermittently connected clients. - Add
authority.policywhen constraining which X.509 SANs or SSH principals Janus may issue. This is mandatory for any move away from private/LAN-only operation.
CA Material And Secret Placement
Store each kind of material in the narrowest place that matches who needs it:
modules/hosts/janus/secrets.yaml: Janus runtime CA secrets used bystep-ca, includingjanus/ca_password,janus/intermediate_ca_key,janus/ssh_host_ca_key,janus/ssh_user_ca_key, andjanus/admin_provisioner_encrypted_key.keys/secrets.yaml: shared provisioner credentialjanus/admin_jwk, because client and host certificate modules outside Janus consume it to request certs.modules/hosts/janus/root_ca.crt,intermediate_ca.crt,fingerprint,ssh_user_ca_key.pub, andssh_host_ca_key.pub: public trust artifacts that may be committed and reviewed.- Offline secret storage only:
root_ca_keyand the root key password. The running CA should not need the root private key for normal operation.
When editing secrets:
- Use SOPS commands (
sops,edit-secrets, orsops set) rather than editingENC[...]payloads. - Use YAML literal blocks for PEM or OpenSSH private keys so newlines are preserved.
- After changing Janus recipient rules, run
sops --config .sops.yaml updatekeys -y modules/hosts/janus/secrets.yamlso the host-local file is actually rewrapped. - Keep
.sops.yamlscoped: Janus host runtime secrets should matchmodules/hosts/janus/secrets.yaml; shared credentials should matchkeys/secrets.yaml.
Public Trust Artifacts
- If
root_ca.crtchanges, updatefingerprintfromstep certificate fingerprint <root_ca.crt>and updatemodules/features/step-client.nixdefaults in the same change. - If
intermediate_ca.crtchanges, confirm it still chains to the committed root and that the running CA uses the matchingintermediate_ca_keyfrom SOPS. - If SSH CA keys rotate, update the matching public key files and every SSH trust consumer, including
modules/services/ssh.nixknown-host CA values andTrustedUserCAKeysbehavior. - Treat
README.mdbootstrap commands as part of the operational contract. Keep them aligned with secret names, public artifact paths, CA URL, DNS names, and IP SANs.
mTLS And Client Trust
- Janus mTLS values should stay aligned with Step CA identity: update
step-ca.dnsNames,mtls.subject,mtls.san, and Step client defaults together when CA names or addresses change. modules/features/step-client.nixis the shared bootstrap surface. Its default CA URL, root certificate, and fingerprint should describe Janus unless the repo intentionally migrates to another CA.- mTLS certificate files are runtime outputs under the configured cert directory, not tracked source files. Do not store mTLS private keys in Git or the Nix store.
Step/Nix Troubleshooting Checklist
Start with the smallest check that matches the change:
- Host evaluation:
nix eval .#nixosConfigurations.janus.config.system.build.toplevel.drvPath - Build planning:
nix build .#nixosConfigurations.janus.config.system.build.toplevel --no-link --dry-run - Secret contract: confirm every
step-ca.secrets.*value has a matchingsops.secretsdeclaration and a matching key inmodules/hosts/janus/secrets.yaml. - Rendered config: on the host, inspect
/etc/smallstep/ca.jsonor/etc/step-capaths without copying secret content into logs. - Service health: check
systemctl status step-ca.service,journalctl -u step-ca.service, andstep ca health --ca-url https://janus.john-stream.comafter trust is bootstrapped. - Provisioners: use
step ca provisioner listto confirm ACME, SSHPOP, and admin JWK availability. - SSH cert flow: use
ssh-host-cert-check,systemctl status ssh-certs-renew.service, andsystemctl status ssh-certs-renew.timer. - mTLS flow: use
mtls-check,mtls-generate, andsystemctl status mtls-renew.timerfor host certificate renewal issues.
Change Safety Rules
- Do not deploy a root private key to Janus unless the task explicitly changes the trust model.
- Do not rename
janus/admin_jwkwithout updating all consumers in the same change. - Do not expose ACME or
/provisionersendpoints publicly without an explicit migration away from the private/LAN-only model plus policy and endpoint exposure review. - Do not use certificate templates casually; if templates are added, keep them narrow and use
toJsonfor user-controlled values. - If changing certificate durations, prefer short-lived leaf certs with automated renewal over long-lived leaf certs.
- If changing service ports or proxying, account for Step renewal behavior: mTLS renewal is sensitive to layer-7 proxies unless configured for token-based renewal.