Add SSH CA engine with host/user cert signing, profiles, and KRL

Implement the complete SSH CA engine following the CA engine pattern:
- Engine core (initialize, unseal, seal, HandleRequest) with ed25519/ecdsa key support
- Host and user certificate signing with TTL enforcement and policy checks
- Signing profiles with extensions, critical options, and principal restrictions
- Certificate CRUD (list, get, revoke, delete) with proper auth enforcement
- OpenSSH KRL generation rebuilt on revoke/delete operations
- gRPC service (SSHCAService) with all RPCs and interceptor registration
- REST routes for public endpoints (CA pubkey, KRL) and authenticated operations
- Comprehensive test suite (15 tests covering lifecycle, signing, profiles, KRL, auth)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-16 19:43:32 -07:00
parent 64d921827e
commit 5ae37da300
10 changed files with 6007 additions and 20 deletions

View File

@@ -0,0 +1,262 @@
syntax = "proto3";
package metacrypt.v2;
import "google/protobuf/timestamp.proto";
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// SSHCAService provides typed, authenticated access to SSH CA engine operations.
// All RPCs require the service to be unsealed unless noted. Write operations
// require authentication. Admin-only operations additionally require admin
// privileges.
service SSHCAService {
// GetCAPublicKey returns the SSH CA public key for a mount. No auth required.
rpc GetCAPublicKey(SSHGetCAPublicKeyRequest) returns (SSHGetCAPublicKeyResponse);
// SignHost signs an SSH host certificate. Auth required (user+policy).
rpc SignHost(SSHSignHostRequest) returns (SSHSignHostResponse);
// SignUser signs an SSH user certificate. Auth required (user+policy).
rpc SignUser(SSHSignUserRequest) returns (SSHSignUserResponse);
// CreateProfile creates a new signing profile. Admin only.
rpc CreateProfile(SSHCreateProfileRequest) returns (SSHCreateProfileResponse);
// UpdateProfile updates an existing signing profile. Admin only.
rpc UpdateProfile(SSHUpdateProfileRequest) returns (SSHUpdateProfileResponse);
// GetProfile retrieves a signing profile by name. Auth required.
rpc GetProfile(SSHGetProfileRequest) returns (SSHGetProfileResponse);
// ListProfiles lists all signing profiles. Auth required.
rpc ListProfiles(SSHListProfilesRequest) returns (SSHListProfilesResponse);
// DeleteProfile removes a signing profile. Admin only.
rpc DeleteProfile(SSHDeleteProfileRequest) returns (SSHDeleteProfileResponse);
// GetCert retrieves an SSH certificate record by serial. Auth required.
rpc GetCert(SSHGetCertRequest) returns (SSHGetCertResponse);
// ListCerts lists all SSH certificate records for a mount. Auth required.
rpc ListCerts(SSHListCertsRequest) returns (SSHListCertsResponse);
// RevokeCert marks an SSH certificate as revoked by serial. Admin only.
rpc RevokeCert(SSHRevokeCertRequest) returns (SSHRevokeCertResponse);
// DeleteCert permanently removes an SSH certificate record. Admin only.
rpc DeleteCert(SSHDeleteCertRequest) returns (SSHDeleteCertResponse);
// GetKRL returns the current Key Revocation List in OpenSSH KRL format.
// No auth required.
rpc GetKRL(SSHGetKRLRequest) returns (SSHGetKRLResponse);
}
// --- GetCAPublicKey ---
message SSHGetCAPublicKeyRequest {
string mount = 1;
}
message SSHGetCAPublicKeyResponse {
// public_key is the SSH CA public key in authorized_keys format.
string public_key = 1;
}
// --- SignHost ---
message SSHSignHostRequest {
string mount = 1;
// public_key is the host's SSH public key in authorized_keys format.
string public_key = 2;
// hostname is the principal to embed in the host certificate.
string hostname = 3;
// ttl overrides the default certificate validity (e.g. "720h").
string ttl = 4;
}
message SSHSignHostResponse {
string serial = 1;
string cert_type = 2;
repeated string principals = 3;
string cert_data = 4;
string key_id = 5;
string issued_by = 6;
google.protobuf.Timestamp issued_at = 7;
google.protobuf.Timestamp expires_at = 8;
}
// --- SignUser ---
message SSHSignUserRequest {
string mount = 1;
// public_key is the user's SSH public key in authorized_keys format.
string public_key = 2;
// principals are the usernames to embed in the certificate.
// Defaults to the caller's own username if empty.
repeated string principals = 3;
// profile selects a signing profile for extensions and constraints.
string profile = 4;
// ttl overrides the default certificate validity (e.g. "24h").
string ttl = 5;
}
message SSHSignUserResponse {
string serial = 1;
string cert_type = 2;
repeated string principals = 3;
string cert_data = 4;
string key_id = 5;
string profile = 6;
string issued_by = 7;
google.protobuf.Timestamp issued_at = 8;
google.protobuf.Timestamp expires_at = 9;
}
// --- CreateProfile ---
message SSHCreateProfileRequest {
string mount = 1;
string name = 2;
map<string, string> critical_options = 3;
map<string, string> extensions = 4;
string max_ttl = 5;
repeated string allowed_principals = 6;
}
message SSHCreateProfileResponse {
string name = 1;
}
// --- UpdateProfile ---
message SSHUpdateProfileRequest {
string mount = 1;
string name = 2;
map<string, string> critical_options = 3;
map<string, string> extensions = 4;
string max_ttl = 5;
repeated string allowed_principals = 6;
}
message SSHUpdateProfileResponse {
string name = 1;
}
// --- GetProfile ---
message SSHGetProfileRequest {
string mount = 1;
string name = 2;
}
message SSHGetProfileResponse {
string name = 1;
map<string, string> critical_options = 2;
map<string, string> extensions = 3;
string max_ttl = 4;
repeated string allowed_principals = 5;
}
// --- ListProfiles ---
message SSHListProfilesRequest {
string mount = 1;
}
message SSHListProfilesResponse {
repeated string profiles = 1;
}
// --- DeleteProfile ---
message SSHDeleteProfileRequest {
string mount = 1;
string name = 2;
}
message SSHDeleteProfileResponse {}
// --- GetCert ---
message SSHGetCertRequest {
string mount = 1;
string serial = 2;
}
message SSHGetCertResponse {
SSHCertRecord cert = 1;
}
// --- ListCerts ---
message SSHListCertsRequest {
string mount = 1;
}
message SSHListCertsResponse {
repeated SSHCertSummary certs = 1;
}
// --- RevokeCert ---
message SSHRevokeCertRequest {
string mount = 1;
string serial = 2;
}
message SSHRevokeCertResponse {
string serial = 1;
google.protobuf.Timestamp revoked_at = 2;
}
// --- DeleteCert ---
message SSHDeleteCertRequest {
string mount = 1;
string serial = 2;
}
message SSHDeleteCertResponse {}
// --- GetKRL ---
message SSHGetKRLRequest {
string mount = 1;
}
message SSHGetKRLResponse {
// krl is the binary KRL data in OpenSSH KRL format.
bytes krl = 1;
}
// --- Shared message types ---
// SSHCertRecord is the full SSH certificate record.
message SSHCertRecord {
string serial = 1;
string cert_type = 2;
repeated string principals = 3;
string cert_data = 4;
string key_id = 5;
string profile = 6;
string issued_by = 7;
google.protobuf.Timestamp issued_at = 8;
google.protobuf.Timestamp expires_at = 9;
bool revoked = 10;
google.protobuf.Timestamp revoked_at = 11;
string revoked_by = 12;
}
// SSHCertSummary is a lightweight SSH certificate record for list responses.
message SSHCertSummary {
string serial = 1;
string cert_type = 2;
repeated string principals = 3;
string key_id = 4;
string profile = 5;
string issued_by = 6;
google.protobuf.Timestamp issued_at = 7;
google.protobuf.Timestamp expires_at = 8;
bool revoked = 9;
}