Add certificate revocation, deletion, and retrieval

Admins can now revoke or delete certificate records from the cert detail
page in the web UI. Revoked certificates display a [REVOKED] badge and
show revocation metadata (time and actor). Deletion redirects to the
issuer page.

The REST API gains three new authenticated endpoints that mirror the
gRPC surface:
  GET    /v1/ca/{mount}/cert/{serial}         (auth required)
  POST   /v1/ca/{mount}/cert/{serial}/revoke  (admin only)
  DELETE /v1/ca/{mount}/cert/{serial}         (admin only)

The CA engine stores revocation state (revoked, revoked_at, revoked_by)
directly in the existing CertRecord barrier entry. The proto CertRecord
message is extended with the same three fields (field numbers 10–12).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 13:37:54 -07:00
parent 74e35ce63e
commit d574685b99
27 changed files with 839 additions and 91 deletions

View File

@@ -9,8 +9,8 @@ option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv
// CAService provides typed, authenticated access to CA engine operations.
// All RPCs require the service to be unsealed. Write operations (CreateIssuer,
// DeleteIssuer, ImportRoot, IssueCert, RenewCert) require authentication.
// Admin-only operations (CreateIssuer, DeleteIssuer, ImportRoot) additionally
// require the caller to have admin privileges.
// Admin-only operations (CreateIssuer, DeleteIssuer, ImportRoot, RevokeCert,
// DeleteCert) additionally require the caller to have admin privileges.
service CAService {
// ImportRoot imports an existing root CA certificate and private key.
// Admin only. Only allowed when no valid root exists.
@@ -52,6 +52,13 @@ service CAService {
// and SAN fields from the CSR are preserved exactly; profile defaults supply
// key usages and validity if not overridden. Auth required.
rpc SignCSR(SignCSRRequest) returns (SignCSRResponse);
// RevokeCert marks a certificate as revoked by serial number. Admin only.
rpc RevokeCert(RevokeCertRequest) returns (RevokeCertResponse);
// DeleteCert permanently removes a certificate record by serial number.
// Admin only.
rpc DeleteCert(DeleteCertRequest) returns (DeleteCertResponse);
}
// --- ImportRoot ---
@@ -252,6 +259,27 @@ message SignCSRResponse {
// --- Shared message types ---
// --- RevokeCert ---
message RevokeCertRequest {
string mount = 1;
string serial = 2;
}
message RevokeCertResponse {
string serial = 1;
google.protobuf.Timestamp revoked_at = 2;
}
// --- DeleteCert ---
message DeleteCertRequest {
string mount = 1;
string serial = 2;
}
message DeleteCertResponse {}
// CertRecord is the full certificate record including the PEM-encoded cert.
message CertRecord {
string serial = 1;
@@ -264,6 +292,12 @@ message CertRecord {
google.protobuf.Timestamp expires_at = 8;
// cert_pem is the PEM-encoded certificate.
bytes cert_pem = 9;
// revoked indicates whether the certificate has been revoked.
bool revoked = 10;
// revoked_at is the time the certificate was revoked, if applicable.
google.protobuf.Timestamp revoked_at = 11;
// revoked_by is the username of the admin who revoked the certificate.
string revoked_by = 12;
}
// CertSummary is a lightweight certificate record without the PEM data,