Skip to content

chore(operator-ca): OpenSSL operator CA, CRL and OCSP responder for the lab Fleet Manager - #9

Merged
Bugs5382 merged 1 commit into
mainfrom
chore/8-operator-ca
Sep 30, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
chore/8-operator-ca

Conversation

@Bugs5382

Copy link
Copy Markdown
Contributor

Summary

The Fleet Manager's operator CA is always external and never a CryptOS node, and operator_ca_node is about to become a startup error. The lab Fleet Manager still trusted the workload Intermediate as its operator CA through operator_ca_node, so it has to move first. This adds tooling under operator-ca/ for an external OpenSSL operator CA with a CRL and a delegated OCSP responder, and documents the Fleet Manager values that use it through the file source (operatorCA.configMap, no operatorCANode).

Closes #8
Refs CryptOS-PKI/cryptos-manager#78

Changes

  • task opca:init (operator-ca/ca.sh init): the CA in .state/operator-ca/ (mode 700, gitignored, untouched by k8s:down). ECDSA P-384, CA:TRUE, pathlen:0, KU keyCertSign, cRLSign, SKI, SHA-384. It renders operator-ca.cnf with copy_extensions = none, the CN-only policy, 7-day CRLs, and the expanded op_admin, op_operator and op_viewer sections (OpenSSL has no section include). Each level section carries 1.3.6.1.4.1.59999.1.1 non-critical, with the DER cmd/opext prints, plus an authorityInfoAccess OCSP URI. It also makes a delegated OCSP signer (EKU OCSPSigning, noCheck, 30 days) and the first CRL, and prints the CA SHA-256. It refuses to overwrite an existing CA.
  • task opca:issue LEVEL= EMAIL=: P-384 key and CSR (CN=<email>, lower-cased), openssl ca -extensions op_<level>, then a check against the Fleet Manager's rules (level extension present, non-critical and exact; EKU exactly clientAuth; KU critical digitalSignature; critical CA:FALSE; verifies to the CA). Then a PKCS#12 with the CA cert. The passphrase comes from LAB_OPCA_P12_PASS_FILE or LAB_OPCA_P12_PASS (18 bytes or more), never from the command line, and is never logged.
  • task opca:revoke, opca:crl and opca:ocsp-signer cover the CA's upkeep.
  • task opca:publish: the CA cert goes into the chart's fm-operator-ca ConfigMap (key operator-ca.pem) and the CRL into fm-operator-crl. It also runs openssl ocsp as a lab-grade responder (operator-ocsp Deployment and Service, alpine/openssl pinned by digest in k8s/versions.env, nonroot, read-only root filesystem, -nmin 60). The responder's Secret holds only the signer cert and key, the CA cert and index.txt, never the CA key. A checksum annotation restarts the responder whenever its data changes. The Secret is applied without a diff, so the signer key never reaches a log.
  • The responder probes send a real OCSP request. A bare TCP connect (a tcpSocket probe) wedges openssl ocsp for good: reproduced with OpenSSL 3.5 and 3.6, and -timeout doesn't recover it. A liveness probe restarts a wedged responder.
  • ci-operator-ca.yml: task opca:check on every non-draft PR that touches operator-ca/, the shared k8s helpers or pins, or the Taskfile.
  • README: a "Fleet Manager operator CA" section covering the CA, issue and revoke, the responder, and the Fleet Manager values. Until the manager takes a CRL and OCSP settings, running without operatorCANode means no operator revocation source and MCP stays off.

Testing

  • task opca:check: 147 checks against a throwaway CA built by the real scripts. They cover shellcheck; the CA profile and file modes; the cnf (no .include_section, each level section, the DER); the refusal to re-init; the OCSP signer (EKU, noCheck, 30 days, chains to the CA); the CRL (verifies, nextUpdate, CRL number, AKI); and issue for all three levels. For each issued cert the checks cover the level extension (not critical, DER equal to cmd/opext), subject, key, EKU, KU, basic constraints, AIA, the PKCS#12 contents and passphrase, and that the passphrase is written nowhere. They also cover the input refusals, check refusing a critical level extension, revoke plus the new CRL, and a local openssl ocsp responder answering good (verified, delegated signer, nextUpdate) and then revoked. Finally they run the signer renewal and the publish step in dry-run mode (pinned image, no private key in the manifest, CA key absent from the Secret, probes, kubeconform -strict).
  • task k8s:check still passes (60).
  • cmd/opext on manager main prints 130561646D696E, 13086F70657261746F72 and 1306766965776572 for admin, operator and viewer, the values the cnf carries.
  • On a kind cluster on the lab box, with the Fleet Manager from manager main through chart/fleet-manager and Postgres:
    • opca:publish rolled out the responder, and OCSP through the AIA URL from inside the cluster, and through a port-forward, answered good with "Response verify OK".
    • With operatorCA.configMap: fm-operator-ca and no operatorCANode, the manager started on operatorCAPath. An admin PKCS#12 from opca:issue signed in: WhoAmI returned level admin and ListNodes listed the nodes.
    • A client cert from the old node-hosted operator CA failed the handshake (unknown ca), and a request without a cert got 401.
    • A viewer cert revoked with opca:revoke, then published, showed as revoked in the CRL and in OCSP, but the manager still accepted it. That is expected: current main has no revocation source without operatorCANode, and the manager's CRL and OCSP support will close the gap.

Docs

The README is updated. No docs-site change: lab tooling isn't part of the product docs.

…he lab Fleet Manager

The operator CA is always external, never a CryptOS node, so the lab Fleet
Manager can no longer trust the workload Intermediate through
operator_ca_node. operator-ca/ builds the external CA instead:

- ca.sh init: P-384 CA (pathlen:0, keyCertSign and cRLSign), an
  operator-ca.cnf with the expanded op_admin, op_operator and op_viewer
  sections (level extension non-critical, OCSP AIA), a delegated OCSP
  signer (OCSPSigning, noCheck, 30 days) and a first CRL (7 days)
- ca.sh issue: key, CSR, cert and PKCS#12, checked against the Fleet
  Manager's rules; the passphrase comes from a file or the environment
- ca.sh revoke, crl and ocsp-signer for the CA's upkeep
- publish.sh: the CA ConfigMap for the chart, the CRL ConfigMap and an
  openssl ocsp responder in kind, with the signer key only; the probes
  send a real OCSP request because a bare TCP connect wedges the responder

task opca:check runs the offline checks, and a new workflow runs it on PRs.

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382 Bugs5382 added this to the v0.1.0 milestone Sep 30, 2026
@Bugs5382 Bugs5382 added the enhancement New feature or request label Sep 30, 2026
@Bugs5382 Bugs5382 self-assigned this Sep 30, 2026
@Bugs5382
Bugs5382 marked this pull request as ready for review September 30, 2026 17:11
@Bugs5382

Copy link
Copy Markdown
Contributor Author

Merging for the v0.1.0 test run: CI green and mergeable at merge time.

@Bugs5382
Bugs5382 merged commit 56be75f into main Sep 30, 2026
6 checks passed
@Bugs5382
Bugs5382 deleted the chore/8-operator-ca branch September 30, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(operator-ca): move the lab Fleet Manager to an external OpenSSL operator CA with a CRL and OCSP

1 participant