Phase C: Automated TLS cert provisioning for L7 routes
Add CertProvisioner that requests TLS certificates from Metacrypt's CA API during deploy. When a service has L7 routes, the agent checks for an existing cert, re-issues if missing or within 30 days of expiry, and writes chain+key to mc-proxy's cert directory before registering routes. - Add MetacryptConfig to agent config (server_url, ca_cert, mount, issuer, token_path) with defaults and env overrides - Add CertProvisioner (internal/agent/certs.go): REST client for Metacrypt IssueCert, atomic file writes, cert expiry checking - Wire into Agent struct and deploy flow (before route registration) - Add hasL7Routes/l7Hostnames helpers in deploy.go - Fix pre-existing lint issues: unreachable code in portalloc.go, gofmt in servicedef.go, gosec suppressions, golangci v2 config - Update vendored mc-proxy to fix protobuf init panic - 10 new tests, make all passes with 0 issues Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,6 +163,14 @@ func TestLoadAgentConfig(t *testing.T) {
|
||||
if cfg.Log.Level != "debug" {
|
||||
t.Fatalf("log.level: got %q", cfg.Log.Level)
|
||||
}
|
||||
|
||||
// Metacrypt defaults when section is omitted.
|
||||
if cfg.Metacrypt.Mount != "pki" {
|
||||
t.Fatalf("metacrypt.mount default: got %q, want pki", cfg.Metacrypt.Mount)
|
||||
}
|
||||
if cfg.Metacrypt.Issuer != "infra" {
|
||||
t.Fatalf("metacrypt.issuer default: got %q, want infra", cfg.Metacrypt.Issuer)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCLIConfigValidation(t *testing.T) {
|
||||
@@ -439,6 +447,80 @@ level = "info"
|
||||
})
|
||||
}
|
||||
|
||||
func TestAgentConfigMetacrypt(t *testing.T) {
|
||||
cfgStr := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
[metacrypt]
|
||||
server_url = "https://metacrypt.metacircular.net:8443"
|
||||
ca_cert = "/etc/mcp/metacircular-ca.pem"
|
||||
mount = "custom-pki"
|
||||
issuer = "custom-issuer"
|
||||
token_path = "/srv/mcp/metacrypt-token"
|
||||
`
|
||||
path := writeTempConfig(t, cfgStr)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Metacrypt.ServerURL != "https://metacrypt.metacircular.net:8443" {
|
||||
t.Fatalf("metacrypt.server_url: got %q", cfg.Metacrypt.ServerURL)
|
||||
}
|
||||
if cfg.Metacrypt.CACert != "/etc/mcp/metacircular-ca.pem" {
|
||||
t.Fatalf("metacrypt.ca_cert: got %q", cfg.Metacrypt.CACert)
|
||||
}
|
||||
if cfg.Metacrypt.Mount != "custom-pki" {
|
||||
t.Fatalf("metacrypt.mount: got %q", cfg.Metacrypt.Mount)
|
||||
}
|
||||
if cfg.Metacrypt.Issuer != "custom-issuer" {
|
||||
t.Fatalf("metacrypt.issuer: got %q", cfg.Metacrypt.Issuer)
|
||||
}
|
||||
if cfg.Metacrypt.TokenPath != "/srv/mcp/metacrypt-token" {
|
||||
t.Fatalf("metacrypt.token_path: got %q", cfg.Metacrypt.TokenPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentConfigMetacryptEnvOverrides(t *testing.T) {
|
||||
minimal := `
|
||||
[server]
|
||||
grpc_addr = "0.0.0.0:9444"
|
||||
tls_cert = "/srv/mcp/cert.pem"
|
||||
tls_key = "/srv/mcp/key.pem"
|
||||
[database]
|
||||
path = "/srv/mcp/mcp.db"
|
||||
[mcias]
|
||||
server_url = "https://mcias.metacircular.net:8443"
|
||||
service_name = "mcp-agent"
|
||||
[agent]
|
||||
node_name = "rift"
|
||||
`
|
||||
t.Setenv("MCP_AGENT_METACRYPT_SERVER_URL", "https://override.metacrypt:8443")
|
||||
t.Setenv("MCP_AGENT_METACRYPT_TOKEN_PATH", "/override/token")
|
||||
|
||||
path := writeTempConfig(t, minimal)
|
||||
cfg, err := LoadAgentConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Metacrypt.ServerURL != "https://override.metacrypt:8443" {
|
||||
t.Fatalf("metacrypt.server_url: got %q", cfg.Metacrypt.ServerURL)
|
||||
}
|
||||
if cfg.Metacrypt.TokenPath != "/override/token" {
|
||||
t.Fatalf("metacrypt.token_path: got %q", cfg.Metacrypt.TokenPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDurationParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
|
||||
Reference in New Issue
Block a user