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:
262
proto/metacrypt/v2/sshca.proto
Normal file
262
proto/metacrypt/v2/sshca.proto
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user