feat(openbao): SSH client CA roles (SSH CA phase 1) #1466

Merged
k.renner merged 1 commits from ssh-ca-phase1 into main 2026-10-27 08:42:25 +00:00
Member

Phase 1 of the central SSH CA. The ssh-client-signer mount and its ed25519 CA were created by hand with the root token; this PR records the role definitions and the claude-bot signing policy (house pattern: reference files, NOT applied by Flux).

Changes

  • openbao/ssh-ca/role-owner.json: principal k.renner, TTL 4380h (6 months). The mount was enabled with -max-lease-ttl=4380h; the 768h system default would silently cap it.
  • openbao/ssh-ca/role-claude.json: principal claude-bot, TTL 1h, permit-pty only.
  • openbao/policies/ssh-sign-claude.hcl: update on ssh-client-signer/sign/claude, with the commands to attach it to userpass claude-bot next to its existing claude-bot-write (checked live: the only policy it has today).

Verified

  • YubiKey sk-ssh-ed25519 key signed via OIDC login on role owner → sk-ssh-ed25519-cert-v01@openssh.com, principal k.renner, valid 2026-10-27 → 2027-04-27.
  • CA public key served unauthenticated at /v1/ssh-client-signer/public_key, fingerprint SHA256:xEhuqxqFVZ/ig6KpAtHOhLbwsIhyWpzXhvDcjIfM/Js (matches the cert's signing CA).

After merge (manual, root token)

bao write ssh-client-signer/roles/claude @kubernetes/infrastructure/openbao/ssh-ca/role-claude.json
bao policy write ssh-sign-claude kubernetes/infrastructure/openbao/policies/ssh-sign-claude.hcl
bao write auth/userpass/users/claude-bot token_policies=claude-bot-write,ssh-sign-claude

🤖 Generated with Claude Code

Phase 1 of the central SSH CA. The `ssh-client-signer` mount and its ed25519 CA were created by hand with the root token; this PR records the role definitions and the claude-bot signing policy (house pattern: reference files, NOT applied by Flux). ## Changes - `openbao/ssh-ca/role-owner.json`: principal `k.renner`, TTL 4380h (6 months). The mount was enabled with `-max-lease-ttl=4380h`; the 768h system default would silently cap it. - `openbao/ssh-ca/role-claude.json`: principal `claude-bot`, TTL 1h, `permit-pty` only. - `openbao/policies/ssh-sign-claude.hcl`: `update` on `ssh-client-signer/sign/claude`, with the commands to attach it to userpass `claude-bot` next to its existing `claude-bot-write` (checked live: the only policy it has today). ## Verified - YubiKey `sk-ssh-ed25519` key signed via OIDC login on role `owner` → `sk-ssh-ed25519-cert-v01@openssh.com`, principal `k.renner`, valid 2026-10-27 → 2027-04-27. - CA public key served unauthenticated at `/v1/ssh-client-signer/public_key`, fingerprint `SHA256:xEhuqxqFVZ/ig6KpAtHOhLbwsIhyWpzXhvDcjIfM/Js` (matches the cert's signing CA). ## After merge (manual, root token) ``` bao write ssh-client-signer/roles/claude @kubernetes/infrastructure/openbao/ssh-ca/role-claude.json bao policy write ssh-sign-claude kubernetes/infrastructure/openbao/policies/ssh-sign-claude.hcl bao write auth/userpass/users/claude-bot token_policies=claude-bot-write,ssh-sign-claude ``` 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-10-27 08:39:31 +00:00
feat(openbao): SSH client CA roles for owner and claude-bot
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
2e412e6448
Phase 1 of the SSH CA. The ssh-client-signer mount and its ed25519 CA
were set up by hand (root token); this records the roles and the
claude-bot signing policy.

- role owner: principal k.renner, 6-month certs. The mount was enabled
  with -max-lease-ttl=4380h; the 768h system default would cap it.
- role claude: principal claude-bot, 1h certs, pty only.
- policy ssh-sign-claude for the claude-bot userpass user, next to its
  existing claude-bot-write.

Verified: a YubiKey sk-ssh-ed25519 key signed on role owner yields a
sk-ssh-ed25519-cert-v01 cert, principal k.renner, valid to 2027-04-27.
CA fingerprint SHA256:xEhuqxqFVZ/ig6KpAtHOhLbwsIhyWpzXhvDcjIfM/Js,
served unauthenticated at /v1/ssh-client-signer/public_key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-opus-5-5, commit 2e412e6448f7cbbd1817fb059eb2aa04ad029201.

Automated review by the review-bot: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review.


Summary

This PR adds three reference files that Flux does not apply. Two are SSH secrets-engine role definitions under kubernetes/infrastructure/openbao/ssh-ca/, one for owner (principal k.renner, 4380h) and one for claude (principal claude-bot, 1h, pty only). The third is policy ssh-sign-claude, which grants update on the claude sign path. The PR is mergeable and is based on the current main tip (7f61ad0c). None of the three files exist on main, no other PR covers them, and the PR links no issues. Only phase 0 (#1463, which added ssh-sign-owner.hcl) came before it. I found nothing blocking.

🟡 Suggestions

  • The owner role and the mount/CA setup are not written down anywhere (kubernetes/infrastructure/openbao/ssh-ca/role-owner.json). Every other policy file in policies/ keeps the exact bao commands that apply it in its header. ssh-sign-claude.hcl:4-6 does this for the claude role. Nothing in the repo does it for role-owner.json: ssh-sign-owner.hcl names sign/owner but has no bao write ssh-client-signer/roles/owner @…. The same goes for the hand-run mount and CA setup that role-owner.json depends on: secrets enable -path=ssh-client-signer -max-lease-ttl=4380h ssh and config/ca … key_type=ed25519. The PR body itself says the 4380h TTL only works because of that mount tuning. JSON can't hold comments, so add the commands either to the header of ssh-sign-owner.hcl or to a short ssh-ca/README.md. Without them, someone rebuilding the mount (instead of restoring a snapshot) would hit the 768h cap without being told why.

🟢 Nits

  • role-owner.json:6: permit-agent-forwarding is allowed but left out of default_extensions, so the owner has to ask for it per signing. That looks deliberate; a one-line note in the README suggested above would make that clear.
  • Owner certs last 6 months and the CA cannot revoke a single cert. The hardware-backed sk- key limits the damage if one leaks. Plan host-side RevokedKeys when hosts start trusting the CA (phase 2).

Tests

The repo has no automated tests for these reference files. The PR reports a manual check that matters: an owner signing produced an sk-ssh-ed25519-cert-v01 cert with the expected principal and validity, and the CA fingerprint matched. After merge, run the same check for the claude role: log in as userpass claude-bot, sign on ssh-client-signer/sign/claude, and confirm that the cert expires after 1h and carries only permit-pty.

Recommendation

Looks reasonable — no blocking issues; comments above are optional.

🤖 **Automated review** — model `claude-opus-5-5`, commit `2e412e6448f7cbbd1817fb059eb2aa04ad029201`. > Automated review by the `review-bot`: it approves only when it found nothing red or yellow, and a human still merges. Push new commits to get a fresh review. --- ## Summary This PR adds three reference files that Flux does not apply. Two are SSH secrets-engine role definitions under `kubernetes/infrastructure/openbao/ssh-ca/`, one for `owner` (principal `k.renner`, 4380h) and one for `claude` (principal `claude-bot`, 1h, pty only). The third is policy `ssh-sign-claude`, which grants `update` on the claude sign path. The PR is mergeable and is based on the current `main` tip (`7f61ad0c`). None of the three files exist on `main`, no other PR covers them, and the PR links no issues. Only phase 0 (#1463, which added `ssh-sign-owner.hcl`) came before it. I found nothing blocking. ## 🟡 Suggestions - **The owner role and the mount/CA setup are not written down anywhere** (`kubernetes/infrastructure/openbao/ssh-ca/role-owner.json`). Every other policy file in `policies/` keeps the exact `bao` commands that apply it in its header. `ssh-sign-claude.hcl:4-6` does this for the claude role. Nothing in the repo does it for `role-owner.json`: `ssh-sign-owner.hcl` names `sign/owner` but has no `bao write ssh-client-signer/roles/owner @…`. The same goes for the hand-run mount and CA setup that `role-owner.json` depends on: `secrets enable -path=ssh-client-signer -max-lease-ttl=4380h ssh` and `config/ca … key_type=ed25519`. The PR body itself says the 4380h TTL only works because of that mount tuning. JSON can't hold comments, so add the commands either to the header of `ssh-sign-owner.hcl` or to a short `ssh-ca/README.md`. Without them, someone rebuilding the mount (instead of restoring a snapshot) would hit the 768h cap without being told why. ## 🟢 Nits - `role-owner.json:6`: `permit-agent-forwarding` is allowed but left out of `default_extensions`, so the owner has to ask for it per signing. That looks deliberate; a one-line note in the README suggested above would make that clear. - Owner certs last 6 months and the CA cannot revoke a single cert. The hardware-backed `sk-` key limits the damage if one leaks. Plan host-side `RevokedKeys` when hosts start trusting the CA (phase 2). ## Tests The repo has no automated tests for these reference files. The PR reports a manual check that matters: an `owner` signing produced an `sk-ssh-ed25519-cert-v01` cert with the expected principal and validity, and the CA fingerprint matched. After merge, run the same check for the `claude` role: log in as userpass `claude-bot`, sign on `ssh-client-signer/sign/claude`, and confirm that the cert expires after 1h and carries only `permit-pty`. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. <!-- pr-review:sha=2e412e6448f7cbbd1817fb059eb2aa04ad029201 -->
k.renner merged commit 2b309dec92 into main 2026-10-27 08:42:25 +00:00
Sign in to join this conversation.