Add ACME (RFC 8555) server and Go client library

Implements full ACME protocol support in Metacrypt:

- internal/acme: core types, JWS verification (ES256/384/512 + RS256),
  nonce store, per-mount handler, all RFC 8555 protocol endpoints,
  HTTP-01 and DNS-01 challenge validation, EAB management
- internal/server/acme.go: management REST routes (EAB create, config,
  list accounts/orders) + ACME protocol route dispatch
- proto/metacrypt/v1/acme.proto: ACMEService (CreateEAB, SetConfig,
  ListAccounts, ListOrders) — protocol endpoints are HTTP-only per RFC
- clients/go: new Go module with MCIAS-auth bootstrap, ACME account
  registration, certificate issuance/renewal, HTTP-01 and DNS-01
  challenge providers
- .claude/launch.json: dev server configuration

EAB is required for all account creation; MCIAS-authenticated users
obtain a single-use KID + HMAC-SHA256 key via POST /v1/acme/{mount}/eab.
This commit is contained in:
2026-03-15 01:31:52 -07:00
parent aa9a378685
commit 167db48eb4
19 changed files with 2743 additions and 5 deletions

View File

@@ -0,0 +1,83 @@
syntax = "proto3";
package metacrypt.v1;
option go_package = "git.wntrmute.dev/kyle/metacrypt/gen/metacrypt/v1;metacryptv1";
// ACMEService provides authenticated management of ACME state.
// These RPCs correspond to the REST management endpoints at /v1/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(SetACMEConfigRequest) returns (SetACMEConfigResponse);
// ListAccounts returns all ACME accounts for a CA mount. Admin only.
rpc ListAccounts(ListACMEAccountsRequest) returns (ListACMEAccountsResponse);
// ListOrders returns all ACME orders for a CA mount. Admin only.
rpc ListOrders(ListACMEOrdersRequest) returns (ListACMEOrdersResponse);
}
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 SetACMEConfigRequest {
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 SetACMEConfigResponse {
bool ok = 1;
}
message ListACMEAccountsRequest {
string mount = 1;
}
message ListACMEAccountsResponse {
repeated ACMEAccount accounts = 1;
}
message ACMEAccount {
string id = 1;
string status = 2;
repeated string contact = 3;
string mcias_username = 4;
string created_at = 5;
}
message ListACMEOrdersRequest {
string mount = 1;
}
message ListACMEOrdersResponse {
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;
string created_at = 5;
string expires_at = 6;
}