Add certificate issuance, CSR signing, and cert listing to web UI

- Add SignCSR RPC to v2 CA proto and regenerate; implement handleSignCSR
  in CA engine and caServer gRPC layer; add SignCSR client method and
  POST /pki/sign-csr web route with result display in pki.html
- Fix issuer detail cert listing: template was using map-style index on
  CertSummary structs; switch to struct field access and populate
  IssuedBy/IssuedAt fields from proto response
- Add certificate detail view (cert_detail.html) with GET /cert/{serial}
  and GET /cert/{serial}/download routes
- Update Makefile proto target to generate both v1 and v2 protos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-15 13:21:13 -07:00
parent 65c92fe5ec
commit b4dbc088cb
12 changed files with 785 additions and 82 deletions

View File

@@ -47,6 +47,11 @@ service CAService {
// RenewCert renews an existing certificate, generating a new key and serial.
// Auth required.
rpc RenewCert(RenewCertRequest) returns (RenewCertResponse);
// SignCSR signs an externally generated CSR with a named issuer. All Subject
// 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);
}
// --- ImportRoot ---
@@ -217,6 +222,34 @@ message RenewCertResponse {
bytes chain_pem = 8;
}
// --- SignCSR ---
message SignCSRRequest {
string mount = 1;
// issuer is the name of the issuer to sign with.
string issuer = 2;
// csr_pem is the PEM-encoded PKCS#10 certificate signing request.
bytes csr_pem = 3;
// profile selects key usage defaults (e.g. "server", "client", "peer").
// Defaults to "server" if empty.
string profile = 4;
// ttl overrides the profile's default validity period (e.g. "8760h").
string ttl = 5;
}
message SignCSRResponse {
string serial = 1;
string common_name = 2;
repeated string sans = 3;
string issued_by = 4;
google.protobuf.Timestamp expires_at = 5;
// cert_pem is the signed leaf certificate. No private key is returned
// because the caller already holds it.
bytes cert_pem = 6;
// chain_pem contains the full chain: leaf + issuer + root, PEM-concatenated.
bytes chain_pem = 7;
}
// --- Shared message types ---
// CertRecord is the full certificate record including the PEM-encoded cert.