Files
certctl/internal/domain/profile.go
T
shankar0123 a579a84c7f feat: M11a — certificate profiles, crypto policy enforcement, short-lived cert expiry
Add certificate profiles as named enrollment templates that control allowed
key algorithms, max TTL, permitted EKUs, required SAN patterns, and optional
SPIFFE URI SANs. CSR submissions are validated against profile rules at
signing time (key type + minimum size). Short-lived certs (TTL < 1 hour)
auto-expire via a new scheduler loop — expiry acts as revocation, no
CRL/OCSP needed.

New files:
- Migration 000003: certificate_profiles table, FK columns on
  managed_certificates/renewal_policies, key metadata on certificate_versions
- domain/profile.go: CertificateProfile + KeyAlgorithmRule structs
- repository/postgres/profile.go: full CRUD with JSONB marshaling
- service/profile.go: ProfileService with validation + audit logging
- service/crypto_validation.go: CSR-against-profile validation (RSA/ECDSA/Ed25519)
- handler/profiles.go: 5 HTTP endpoints under /api/v1/profiles
- web/src/pages/ProfilesPage.tsx: profiles management page

Modified:
- renewal.go: CSR validation in CompleteAgentCSRRenewal, ExpireShortLivedCertificates
- scheduler.go: 30s short-lived expiry check loop
- certificate.go (repo): nullable profile FK, key metadata on versions
- main.go: profile repo/service/handler wiring, 8-param NewRenewalService
- router.go: 12-param RegisterHandlers with profile routes
- seed_demo.sql: 4 demo profiles (standard, mtls, short-lived, high-security)
- Frontend: types, API client, routing, sidebar nav

Tests: 40 new tests across handler (15), service (13), crypto validation (12)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 20:39:49 -04:00

72 lines
2.5 KiB
Go

package domain
import (
"time"
)
// CertificateProfile defines an enrollment profile that controls what kinds of
// certificates can be issued: allowed key algorithms, maximum TTL, permitted EKUs,
// required SAN patterns, and optional SPIFFE URI SANs for workload identity.
type CertificateProfile struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
AllowedKeyAlgorithms []KeyAlgorithmRule `json:"allowed_key_algorithms"`
MaxTTLSeconds int `json:"max_ttl_seconds"`
AllowedEKUs []string `json:"allowed_ekus"`
RequiredSANPatterns []string `json:"required_san_patterns"`
SPIFFEURIPattern string `json:"spiffe_uri_pattern"`
AllowShortLived bool `json:"allow_short_lived"`
Enabled bool `json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// KeyAlgorithmRule defines an allowed key algorithm and its minimum key size.
type KeyAlgorithmRule struct {
Algorithm string `json:"algorithm"` // "RSA", "ECDSA", "Ed25519"
MinSize int `json:"min_size"` // RSA: 2048/4096, ECDSA: 256/384, Ed25519: 0 (fixed)
}
// IsShortLived returns true if this profile's max TTL is under 1 hour (3600 seconds).
// Short-lived certs use expiry as revocation — no CRL/OCSP needed.
func (p *CertificateProfile) IsShortLived() bool {
return p.AllowShortLived && p.MaxTTLSeconds > 0 && p.MaxTTLSeconds < 3600
}
// DefaultKeyAlgorithms returns sensible defaults for profiles without explicit rules.
func DefaultKeyAlgorithms() []KeyAlgorithmRule {
return []KeyAlgorithmRule{
{Algorithm: "ECDSA", MinSize: 256},
{Algorithm: "RSA", MinSize: 2048},
}
}
// DefaultEKUs returns the default extended key usages.
func DefaultEKUs() []string {
return []string{"serverAuth"}
}
// Supported key algorithm constants for validation.
const (
KeyAlgorithmRSA = "RSA"
KeyAlgorithmECDSA = "ECDSA"
KeyAlgorithmEd25519 = "Ed25519"
)
// ValidKeyAlgorithms is the set of recognized key algorithm names.
var ValidKeyAlgorithms = map[string]bool{
KeyAlgorithmRSA: true,
KeyAlgorithmECDSA: true,
KeyAlgorithmEd25519: true,
}
// ValidEKUs is the set of recognized extended key usage names.
var ValidEKUs = map[string]bool{
"serverAuth": true,
"clientAuth": true,
"codeSigning": true,
"emailProtection": true,
"timeStamping": true,
}