feat(security): trusted-services surface + auth mgmt client (M2 §3)

Maestro is the UI/CA, not the enforcement: the auth service owns its
authorized-keys trust store. This is Maestro's side of the flow.

- lib/authmgmt: mTLS management client (Maestro's own identity cert from
  the instance CA) for /internal/{attestation,authorizations,revoke,
  pending}; RevokeServiceBestEffort for uninstall.
- handlers/peers: GET /api/peers, /api/peers/attestation, POST
  approve/revoke.
- uninstall: revoke the service's authorization in the auth trust store
  alongside the CRL identity revocation.
- frontend: TrustedServices panel (pending first-contact authorizations +
  approved keys with authorize/ignore/revoke actions).
- spec.yaml: document the new endpoints and schemas.
- housekeeping: untrack pki test log artifacts, gofmt fixes in pki.
This commit is contained in:
2026-08-15 23:27:21 +01:00
parent dc79629c7e
commit 950b6e5827
16 changed files with 839 additions and 28 deletions
+7 -7
View File
@@ -31,10 +31,10 @@ import (
// RevokedCert is the persisted record of a revoked certificate (CRL source).
type RevokedCert struct {
Serial string `json:"serial"`
Service string `json:"service"`
RevokedAt time.Time `json:"revoked_at"`
Reason string `json:"reason,omitempty"`
Serial string `json:"serial"`
Service string `json:"service"`
RevokedAt time.Time `json:"revoked_at"`
Reason string `json:"reason,omitempty"`
}
// RevokedSerialsDir returns the directory holding revoked certificates.
@@ -123,9 +123,9 @@ func regenerateCRL(ca *CA) error {
}
template := &x509.RevocationList{
Number: mustSerial(),
ThisUpdate: now(),
NextUpdate: now().Add(24 * time.Hour),
Number: mustSerial(),
ThisUpdate: now(),
NextUpdate: now().Add(24 * time.Hour),
RevokedCertificates: revokedEntries(serials),
}