# Runbook: Docker TLS Rotation Rotate on a schedule (recommend yearly, well before the ~2.25-year cert lifetime `generate-docker-tls.sh` defaults to) or immediately if a client cert may have leaked. ```bash # 1. Generate a fresh bundle (new CA too — simplest, avoids any question about whether the old # CA's private key was ever exposed) cd infra/scripts/docker-tls ./generate-docker-tls.sh --out-dir ./out-$(date +%Y%m%d) --lab-host-ip --lab-host-dns rn-lab-host-01.internal ./verify-docker-tls.sh ./out-$(date +%Y%m%d) # 2. Redeploy — this restarts dockerd on rn-lab-host-01 (brief interruption to any in-flight # Docker API call from the Provisioner, NOT to already-running student Labs — their # containers keep running through a dockerd restart) cd ../../ansible ROOTNODE_TLS_BUNDLE_DIR=../scripts/docker-tls/out-$(date +%Y%m%d) \ ansible-playbook site.yml -i inventory/production.ini --tags docker-tls --ask-vault-pass # 3. Verify the Provisioner can still reach rn-lab-host-01 curl -s http://localhost:4100/health # from rn-platform-01 ``` **Troubleshooting a failed rotation:** - `x509: certificate signed by unknown authority` — the new ca.pem wasn't deployed to both sides, or an old one is cached; re-run the docker-tls role on BOTH hosts. - Provisioner logs `ECONNREFUSED` on port 2376 — dockerd didn't actually restart with the new daemon.json; check `systemctl status docker` on rn-lab-host-01 and the ExecStart override (`infra/ansible/roles/docker-tls/templates/docker-override.conf.j2`) actually applied. - Firewall — confirm `infra/firewall/rn-lab-host-01.rules`' 2376 rule still matches rn-platform-01's actual IP (didn't change as part of this rotation, but worth a sanity check).