mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-11 13:38:56 +00:00
Implement M8: agent-side key generation with ECDSA P-256
Private keys never leave agent infrastructure. Agents generate ECDSA P-256 key pairs locally, store them with 0600 permissions, and submit only the CSR (public key) to the control plane. New AwaitingCSR job state pauses renewal/issuance jobs until the agent submits its CSR. Server-side keygen retained behind CERTCTL_KEYGEN_MODE=server for demo/development. Key changes: - Dual keygen mode via CERTCTL_KEYGEN_MODE (agent default, server for demo) - AwaitingCSR job state with CommonName/SANs in work response - Agent ECDSA P-256 keygen, local key storage, CSR-only submission - CompleteAgentCSRRenewal server-side flow for agent-submitted CSRs - DeploymentRequest.KeyPEM for agent-provided keys during deployment - Dockerfile.agent creates /var/lib/certctl/keys with correct ownership Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,15 @@ type Config struct {
|
||||
Auth AuthConfig
|
||||
RateLimit RateLimitConfig
|
||||
CORS CORSConfig
|
||||
Keygen KeygenConfig
|
||||
}
|
||||
|
||||
// KeygenConfig controls where private keys are generated.
|
||||
type KeygenConfig struct {
|
||||
// Mode: "agent" (default, production) or "server" (demo only, Local CA).
|
||||
// In "agent" mode, renewal/issuance jobs enter AwaitingCSR state and agents generate keys locally.
|
||||
// In "server" mode, the control plane generates keys (private keys touch the server — demo only).
|
||||
Mode string
|
||||
}
|
||||
|
||||
// ServerConfig contains HTTP server configuration.
|
||||
@@ -101,6 +110,9 @@ func Load() (*Config, error) {
|
||||
CORS: CORSConfig{
|
||||
AllowedOrigins: getEnvList("CERTCTL_CORS_ORIGINS", nil),
|
||||
},
|
||||
Keygen: KeygenConfig{
|
||||
Mode: getEnv("CERTCTL_KEYGEN_MODE", "agent"),
|
||||
},
|
||||
}
|
||||
|
||||
if err := cfg.Validate(); err != nil {
|
||||
@@ -161,6 +173,15 @@ func (c *Config) Validate() error {
|
||||
return fmt.Errorf("auth secret is required for auth type %s", c.Auth.Type)
|
||||
}
|
||||
|
||||
// Validate keygen mode
|
||||
validKeygenModes := map[string]bool{
|
||||
"agent": true,
|
||||
"server": true,
|
||||
}
|
||||
if !validKeygenModes[c.Keygen.Mode] {
|
||||
return fmt.Errorf("invalid keygen mode: %s (must be 'agent' or 'server')", c.Keygen.Mode)
|
||||
}
|
||||
|
||||
// Validate scheduler intervals
|
||||
if c.Scheduler.RenewalCheckInterval < 1*time.Minute {
|
||||
return fmt.Errorf("renewal check interval must be at least 1 minute")
|
||||
|
||||
Reference in New Issue
Block a user