feat(M34): dynamic issuer configuration with encrypted config storage

Replace static env-var-based issuer wiring with GUI-driven dynamic
configuration stored encrypted in PostgreSQL. Operators can now
configure, test, enable/disable, and manage issuers from the dashboard
without restarting the server.

Key changes:
- AES-256-GCM encryption for sensitive issuer config at rest (PBKDF2
  key derivation with 100k iterations)
- Dynamic IssuerRegistry with sync.RWMutex replacing static map
- Connector factory pattern (issuerfactory.NewFromConfig) replacing
  140 lines of static wiring in main.go
- Migration 000009: encrypted_config, last_tested_at, test_status,
  source columns on issuers table
- Env var seeding on first boot with ON CONFLICT DO NOTHING
- Registry Rebuild() for atomic map swap after CRUD operations
- Issuer type validation against domain constants on Create
- Audit trail for test connection results
- Conditional seeding for step-ca/OpenSSL (only when env vars set)
- GUI: source badge, connection test status on issuer detail page

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shankar0123
2026-04-04 00:20:13 -04:00
parent 9954fd1100
commit 995b72df05
36 changed files with 1859 additions and 361 deletions
+8 -9
View File
@@ -2,6 +2,7 @@ package service
import (
"context"
"log/slog"
"testing"
"time"
@@ -21,15 +22,13 @@ func newRevocationTestService() (*CertificateService, *mockCertRepo, *mockRevoca
// Create RevocationSvc
revSvc := NewRevocationSvc(certRepo, revocationRepo, auditService)
revSvc.SetIssuerRegistry(map[string]IssuerConnector{
"iss-local": &mockIssuerConnector{},
})
registry := NewIssuerRegistry(slog.Default())
registry.Set("iss-local", &mockIssuerConnector{})
revSvc.SetIssuerRegistry(registry)
// Create CAOperationsSvc
caSvc := NewCAOperationsSvc(revocationRepo, certRepo, profileRepo)
caSvc.SetIssuerRegistry(map[string]IssuerConnector{
"iss-local": &mockIssuerConnector{},
})
caSvc.SetIssuerRegistry(registry)
certService := NewCertificateService(certRepo, policyService, auditService)
certService.SetRevocationSvc(revSvc)
@@ -243,9 +242,9 @@ func TestRevokeCertificate_WithIssuerNotification(t *testing.T) {
// Wire up issuer registry on RevocationSvc with mock
mockIssuer := &mockIssuerConnector{}
svc.revSvc.SetIssuerRegistry(map[string]IssuerConnector{
"iss-local": mockIssuer,
})
registry := NewIssuerRegistry(slog.Default())
registry.Set("iss-local", mockIssuer)
svc.revSvc.SetIssuerRegistry(registry)
cert := &domain.ManagedCertificate{
ID: "cert-7",