Files
Audiobuzz 1db3977238 fleet-manager: auto-renew the agent-plane server cert (step-ca sidecar + hot-reload)
The agent-plane server cert was issued by hand (server-pki.sh / docker exec
step ca certificate), which needs the step CLI on the host, re-specifies SANs
each time, and silently expires at the CA's 90-day cap — the exact failure that
just broke agent TLS (cert minted with a stale SAN, missing manager.fleet.lab).

Automate it:
- agent-cert-renewer sidecar (smallstep step-cli image, compose profile
  cert-auto): issues the cert once from the co-located fleet step-ca, then
  'step ca renew --daemon' rotates it before the cap. Renewal preserves the
  subject + SANs, so SAN drift cannot recur. Chowns to the manager's nonroot
  uid so the distroless manager reads the key. Opt-in + only valid where the CA
  is co-located; server-pki.sh stays as the manual/remote-CA path.
- manager hot-reloads via GetCertificate (certreload.go): a rotated cert on
  disk is picked up on the next handshake with no restart and no dropped agent
  streams; a half-written pair keeps the last good cert serving. Unit-tested.
- defaults.env documents the cert-auto knobs; DEPLOY.md §2a recommends the
  renewer and flags the mandatory manager.fleet.lab SAN.
2026-07-10 07:42:18 +00:00

92 lines
4.7 KiB
Bash

# defaults.env — the COMMITTED default configuration for the fleet-manager stack.
#
# Compose LAYERS this file, then .env (overrides), for both container env AND
# ${...} interpolation. Enable the layering once per shell (later file wins):
#
# export COMPOSE_ENV_FILES=defaults.env,.env
# # or per command: docker compose --env-file defaults.env --env-file .env <cmd>
#
# Then put ONLY overrides + real secrets in .env (gitignored). Changing a default
# here reaches every run without anyone re-copying it. The secret values below are
# PLACEHOLDERS — override them in .env. (.env must exist when COMPOSE_ENV_FILES
# lists it; create it with at least your secrets.)
# --- WebAuthn RP (admin passkeys are ORIGIN-BOUND — must be the UI's real URL) --
# The UI is fronted by Traefik on :443, so the origin is PORT-LESS.
FLEET_MGR_RP_ID=diskless-server.bitasylum.org
FLEET_MGR_RP_ORIGIN=https://diskless-server.bitasylum.org
FLEET_MGR_RP_NAME=Fleet Command
# --- admin plane (behind the Traefik ingress) ----------------------------------
# Traefik terminates the LE cert on :443 and forwards plaintext here; the manager
# serves passkey auth over plaintext on loopback (only Traefik reaches it). The
# manager never terminates TLS itself.
FLEET_MGR_ADMIN_ADDR=127.0.0.1:9500
# Dev-only escape hatch: plaintext, NO passkey auth. NEVER set true in prod.
FLEET_MGR_ADMIN_INSECURE=false
# --- Traefik ACME (Let's Encrypt DNS-01 via Route 53) --------------------------
# Traefik issues + auto-renews the :443 cert. Set the UI hostname in
# traefik/dynamic/fleet-manager.yml (== FLEET_MGR_RP_ID). Give it AWS creds scoped
# to the bitasylum.org hosted zone (IAM policy in DEPLOY.md §2c), or attach an
# instance role and leave them blank.
ACME_EMAIL=you@bitasylum.org
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_REGION=us-east-1
# Optional but recommended: pin the Route 53 zone (skip ListHostedZonesByName;
# disambiguate public/private zones). Traefik reads it from the env.
AWS_HOSTED_ZONE_ID=
# First dry-run vs LE staging: add the caserver flag to the traefik command (see
# docker-compose.yml) rather than an env var.
# --- agent plane (gRPC + mTLS, fleet CA) ---------------------------------------
# Routable mgmt address enrolled hosts dial (":9600" = all interfaces). The
# server cert is issued by the fleet CA (server-pki.sh, or the auto-renewer
# below); the client CA is the fleet root+intermediate so host TPM certs verify.
# Paths are fixed in docker-compose.yml.
FLEET_MGR_AGENT_ADDR=:9600
# --- agent-plane cert auto-renewer (compose profile: cert-auto) ----------------
# Only used when you run the `agent-cert-renewer` sidecar (step-ca co-located on
# this host). It issues the server cert once, then auto-renews before the CA cap,
# preserving the SANs. Defaults suit the bench; override host paths in .env if
# your step-ca lays out its PKI differently.
# AGENT_CERT_SANS all names hosts dial (first = CN). manager.fleet.lab
# is the fleet-DNS name; keep the host FQDN too.
# AGENT_CA_URL the co-located fleet step-ca (host-loopback).
# AGENT_CERT_NOT_AFTER first-issue lifetime (<= CA max, currently 2160h).
# AGENT_CERT_RENEW_EXPIRES_IN renew when less than this remains.
# FLEET_CA_ROOT / _INTERMEDIATE / _PROVISIONER_PW host paths to the CA material.
AGENT_CERT_SANS=manager.fleet.lab,diskless-server.bitasylum.org
AGENT_CA_URL=https://localhost:8443
AGENT_CERT_NOT_AFTER=2160h
AGENT_CERT_RENEW_EXPIRES_IN=720h
FLEET_CA_PROVISIONER=enrol
FLEET_CA_ROOT=../dot1x-bench/pki/root_ca.crt
FLEET_CA_INTERMEDIATE=../dot1x-bench/pki/intermediate_ca.crt
FLEET_CA_PROVISIONER_PW=../dot1x-bench/pki/stepca/secrets/password
# --- authorise pillar (enrol-broker admin plane over mTLS) ---------------------
# The broker runs network_mode:host on 127.0.0.1:9443. The manager's client cert
# is minted by ../enrol-broker/admin-pki.sh (admin CA), into ./pki/broker/ .
FLEET_MGR_BROKER_URL=https://127.0.0.1:9443
# --- InfluxDB (private; the manager is the ONLY reader/writer) -----------------
# The token is handed to BOTH influxdb (init admin token) and the manager, so a
# single value wires the two with no copy step. Treat as a secret.
FLEET_MGR_INFLUX_TOKEN=changeme-generate-a-long-random-token
FLEET_MGR_INFLUX_ORG=fleet
FLEET_MGR_INFLUX_BUCKET=fleet_health
INFLUX_ADMIN_USER=fleet-admin
INFLUX_ADMIN_PASSWORD=changeme-influx-admin-password
INFLUX_RETENTION=90d
# --- misc ----------------------------------------------------------------------
# CI image tag to run (from .gitea/workflows/fleet-manager-image.yaml): a release
# semver, main-<sha7>, or a <branch>-<sha7> dev build. Defaults to :latest.
FLEET_MANAGER_VERSION=latest
FLEET_MGR_SESSION_TTL_MIN=720
# Must match the enrol-broker's HOSTNQN_BASE.
HOSTNQN_BASE=nqn.2026-06.org.example.fleet:host-