Checkpoint: grpc auth fix, issuer list/detail, v2 protos, architecture docs

Co-authored-by: Junie <junie@jetbrains.com>
This commit is contained in:
2026-03-15 11:39:13 -07:00
parent d0b1875dbb
commit ad167aed9b
41 changed files with 1080 additions and 219 deletions

View File

@@ -0,0 +1,83 @@
syntax = "proto3";
package metacrypt.v2;
import "google/protobuf/timestamp.proto";
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// ACMEService provides authenticated management of ACME state.
// These RPCs correspond to the REST management endpoints at /v2/acme/{mount}/.
// The ACME protocol endpoints themselves (/acme/{mount}/...) are HTTP-only
// per RFC 8555 and have no gRPC equivalents.
service ACMEService {
// CreateEAB creates External Account Binding credentials for the
// authenticated MCIAS user. The returned kid and hmac_key are used
// with any RFC 8555-compliant ACME client to register an account.
rpc CreateEAB(CreateEABRequest) returns (CreateEABResponse);
// SetConfig sets the ACME configuration for a CA mount.
// Currently configures the default issuer used for ACME certificate issuance.
rpc SetConfig(SetConfigRequest) returns (SetConfigResponse);
// ListAccounts returns all ACME accounts for a CA mount. Admin only.
rpc ListAccounts(ListAccountsRequest) returns (ListAccountsResponse);
// ListOrders returns all ACME orders for a CA mount. Admin only.
rpc ListOrders(ListOrdersRequest) returns (ListOrdersResponse);
}
message CreateEABRequest {
string mount = 1;
}
message CreateEABResponse {
// kid is the key identifier to pass to the ACME client.
string kid = 1;
// hmac_key is the raw 32-byte HMAC-SHA256 key.
// Base64url-encode this value when configuring an ACME client.
bytes hmac_key = 2;
}
message SetConfigRequest {
string mount = 1;
// default_issuer is the name of the CA issuer to use for ACME certificates.
// The issuer must already exist on the CA mount.
string default_issuer = 2;
}
message SetConfigResponse {}
message ListAccountsRequest {
string mount = 1;
}
message ListAccountsResponse {
repeated ACMEAccount accounts = 1;
}
message ACMEAccount {
string id = 1;
string status = 2;
repeated string contact = 3;
string mcias_username = 4;
google.protobuf.Timestamp created_at = 5;
}
message ListOrdersRequest {
string mount = 1;
}
message ListOrdersResponse {
repeated ACMEOrder orders = 1;
}
message ACMEOrder {
string id = 1;
string account_id = 2;
string status = 3;
// identifiers are in "type:value" format, e.g. "dns:example.com".
repeated string identifiers = 4;
google.protobuf.Timestamp created_at = 5;
google.protobuf.Timestamp expires_at = 6;
}

View File

@@ -0,0 +1,34 @@
syntax = "proto3";
package metacrypt.v2;
import "google/protobuf/timestamp.proto";
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
rpc Logout(LogoutRequest) returns (LogoutResponse);
rpc TokenInfo(TokenInfoRequest) returns (TokenInfoResponse);
}
message LoginRequest {
string username = 1;
string password = 2;
string totp_code = 3;
}
message LoginResponse {
string token = 1;
google.protobuf.Timestamp expires_at = 2;
}
message LogoutRequest {}
message LogoutResponse {}
message TokenInfoRequest {}
message TokenInfoResponse {
string username = 1;
repeated string roles = 2;
bool is_admin = 3;
}

246
proto/metacrypt/v2/ca.proto Normal file
View File

@@ -0,0 +1,246 @@
syntax = "proto3";
package metacrypt.v2;
import "google/protobuf/timestamp.proto";
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// 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.
service CAService {
// ImportRoot imports an existing root CA certificate and private key.
// Admin only. Only allowed when no valid root exists.
rpc ImportRoot(ImportRootRequest) returns (ImportRootResponse);
// GetRoot returns the root CA certificate for a mount.
rpc GetRoot(GetRootRequest) returns (GetRootResponse);
// CreateIssuer creates a new intermediate CA issuer signed by the root.
// Admin only.
rpc CreateIssuer(CreateIssuerRequest) returns (CreateIssuerResponse);
// DeleteIssuer removes an issuer and its key material. Admin only.
rpc DeleteIssuer(DeleteIssuerRequest) returns (DeleteIssuerResponse);
// ListIssuers returns the names of all configured issuers. Auth required.
rpc ListIssuers(ListIssuersRequest) returns (ListIssuersResponse);
// GetIssuer returns the PEM-encoded certificate for a named issuer.
rpc GetIssuer(GetIssuerRequest) returns (GetIssuerResponse);
// GetChain returns the full PEM certificate chain for a named issuer.
rpc GetChain(CAServiceGetChainRequest) returns (CAServiceGetChainResponse);
// IssueCert issues a new leaf certificate from a named issuer. Auth required.
rpc IssueCert(IssueCertRequest) returns (IssueCertResponse);
// GetCert retrieves a certificate record by serial number. Auth required.
rpc GetCert(GetCertRequest) returns (GetCertResponse);
// ListCerts lists all certificate records for a mount. Auth required.
rpc ListCerts(ListCertsRequest) returns (ListCertsResponse);
// RenewCert renews an existing certificate, generating a new key and serial.
// Auth required.
rpc RenewCert(RenewCertRequest) returns (RenewCertResponse);
}
// --- ImportRoot ---
message ImportRootRequest {
string mount = 1;
// cert_pem is the PEM-encoded root CA certificate.
bytes cert_pem = 2;
// key_pem is the PEM-encoded private key for the root CA.
bytes key_pem = 3;
}
message ImportRootResponse {
string common_name = 1;
google.protobuf.Timestamp expires_at = 2;
}
// --- GetRoot ---
message GetRootRequest {
string mount = 1;
}
message GetRootResponse {
bytes cert_pem = 1;
}
// --- CreateIssuer ---
message CreateIssuerRequest {
string mount = 1;
// name is the unique identifier for this issuer within the mount.
string name = 2;
// key_algorithm overrides the mount-level default (e.g. "ecdsa", "rsa").
string key_algorithm = 3;
// key_size overrides the mount-level default (e.g. 256, 2048).
int32 key_size = 4;
// expiry is the lifetime of the issuer certificate (e.g. "26280h").
// Defaults to 3 years if empty.
string expiry = 5;
// max_ttl is the maximum TTL for leaf certificates issued by this issuer
// (e.g. "2160h"). Defaults to 90 days if empty.
string max_ttl = 6;
}
message CreateIssuerResponse {
string name = 1;
// cert_pem is the PEM-encoded issuer certificate.
bytes cert_pem = 2;
}
// --- DeleteIssuer ---
message DeleteIssuerRequest {
string mount = 1;
string name = 2;
}
message DeleteIssuerResponse {}
// --- ListIssuers ---
message ListIssuersRequest {
string mount = 1;
}
message ListIssuersResponse {
repeated string issuers = 1;
}
// --- GetIssuer ---
message GetIssuerRequest {
string mount = 1;
string name = 2;
}
message GetIssuerResponse {
bytes cert_pem = 1;
}
// --- GetChain ---
message CAServiceGetChainRequest {
string mount = 1;
string issuer = 2;
}
message CAServiceGetChainResponse {
// chain_pem contains the issuer certificate followed by the root certificate,
// PEM-encoded and concatenated.
bytes chain_pem = 1;
}
// --- IssueCert ---
message IssueCertRequest {
string mount = 1;
// issuer is the name of the issuer to sign the certificate.
string issuer = 2;
// profile selects the certificate profile (e.g. "server", "client").
// Defaults to "server" if empty.
string profile = 3;
string common_name = 4;
repeated string dns_names = 5;
repeated string ip_addresses = 6;
// ttl overrides the profile's default validity period (e.g. "8760h").
string ttl = 7;
// key_algorithm overrides the issuer-level default.
string key_algorithm = 8;
// key_size overrides the issuer-level default.
int32 key_size = 9;
repeated string key_usages = 10;
repeated string ext_key_usages = 11;
}
message IssueCertResponse {
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 PEM-encoded leaf certificate.
bytes cert_pem = 6;
// key_pem is the PEM-encoded private key for the leaf certificate.
// This is the only time the private key is returned; it is not stored.
bytes key_pem = 7;
// chain_pem contains the full chain: leaf + issuer + root, PEM-concatenated.
bytes chain_pem = 8;
}
// --- GetCert ---
message GetCertRequest {
string mount = 1;
string serial = 2;
}
message GetCertResponse {
CertRecord cert = 1;
}
// --- ListCerts ---
message ListCertsRequest {
string mount = 1;
}
message ListCertsResponse {
repeated CertSummary certs = 1;
}
// --- RenewCert ---
message RenewCertRequest {
string mount = 1;
string serial = 2;
}
message RenewCertResponse {
string serial = 1;
string common_name = 2;
repeated string sans = 3;
string issued_by = 4;
google.protobuf.Timestamp expires_at = 5;
bytes cert_pem = 6;
bytes key_pem = 7;
bytes chain_pem = 8;
}
// --- Shared message types ---
// CertRecord is the full certificate record including the PEM-encoded cert.
message CertRecord {
string serial = 1;
string issuer = 2;
string common_name = 3;
repeated string sans = 4;
string profile = 5;
string issued_by = 6;
google.protobuf.Timestamp issued_at = 7;
google.protobuf.Timestamp expires_at = 8;
// cert_pem is the PEM-encoded certificate.
bytes cert_pem = 9;
}
// CertSummary is a lightweight certificate record without the PEM data,
// suitable for list responses.
message CertSummary {
string serial = 1;
string issuer = 2;
string common_name = 3;
string profile = 4;
string issued_by = 5;
google.protobuf.Timestamp issued_at = 6;
google.protobuf.Timestamp expires_at = 7;
}

View File

@@ -0,0 +1,8 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// Common message types shared across metacrypt.v2 services.
// Currently empty; reserved for future shared types.

View File

@@ -0,0 +1,38 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// EngineService manages the lifecycle of engine mounts.
// In v2, typed service RPCs (e.g. CAService) replace the generic Execute RPC
// for all engine operations.
service EngineService {
rpc Mount(MountRequest) returns (MountResponse);
rpc Unmount(UnmountRequest) returns (UnmountResponse);
rpc ListMounts(ListMountsRequest) returns (ListMountsResponse);
}
message MountRequest {
string name = 1;
string type = 2;
// config holds engine-specific configuration as key-value string pairs.
map<string, string> config = 3;
}
message MountResponse {}
message UnmountRequest {
string name = 1;
}
message UnmountResponse {}
message ListMountsRequest {}
message ListMountsResponse {
repeated MountInfo mounts = 1;
}
message MountInfo {
string name = 1;
string type = 2;
string mount_path = 3;
}

View File

@@ -0,0 +1,36 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
// PKIService provides unauthenticated access to public CA certificates.
// These endpoints only require the service to be unsealed.
service PKIService {
rpc GetRootCert(GetRootCertRequest) returns (GetRootCertResponse);
rpc GetChain(GetChainRequest) returns (GetChainResponse);
rpc GetIssuerCert(GetIssuerCertRequest) returns (GetIssuerCertResponse);
}
message GetRootCertRequest {
string mount = 1;
}
message GetRootCertResponse {
bytes cert_pem = 1;
}
message GetChainRequest {
string mount = 1;
string issuer = 2;
}
message GetChainResponse {
bytes chain_pem = 1;
}
message GetIssuerCertRequest {
string mount = 1;
string issuer = 2;
}
message GetIssuerCertResponse {
bytes cert_pem = 1;
}

View File

@@ -0,0 +1,49 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
service PolicyService {
rpc CreatePolicy(CreatePolicyRequest) returns (CreatePolicyResponse);
rpc ListPolicies(ListPoliciesRequest) returns (ListPoliciesResponse);
rpc GetPolicy(GetPolicyRequest) returns (GetPolicyResponse);
rpc DeletePolicy(DeletePolicyRequest) returns (DeletePolicyResponse);
}
message PolicyRule {
string id = 1;
int32 priority = 2;
string effect = 3;
repeated string usernames = 4;
repeated string roles = 5;
repeated string resources = 6;
repeated string actions = 7;
}
message CreatePolicyRequest {
PolicyRule rule = 1;
}
message CreatePolicyResponse {
PolicyRule rule = 1;
}
message ListPoliciesRequest {}
message ListPoliciesResponse {
repeated PolicyRule rules = 1;
}
message GetPolicyRequest {
string id = 1;
}
message GetPolicyResponse {
PolicyRule rule = 1;
}
message DeletePolicyRequest {
string id = 1;
}
message DeletePolicyResponse {}

View File

@@ -0,0 +1,36 @@
syntax = "proto3";
package metacrypt.v2;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v2;metacryptv2";
service SystemService {
rpc Status(StatusRequest) returns (StatusResponse);
rpc Init(InitRequest) returns (InitResponse);
rpc Unseal(UnsealRequest) returns (UnsealResponse);
rpc Seal(SealRequest) returns (SealResponse);
}
message StatusRequest {}
message StatusResponse {
string state = 1;
}
message InitRequest {
string password = 1;
}
message InitResponse {
string state = 1;
}
message UnsealRequest {
string password = 1;
}
message UnsealResponse {
string state = 1;
}
message SealRequest {}
message SealResponse {
string state = 1;
}