Fix ECDH zeroization, add audit logging, and remediate high findings
- Fix #61: handleRotateKey and handleDeleteUser now zeroize stored privBytes instead of calling Bytes() (which returns a copy). New state populates privBytes; old references nil'd for GC. - Add audit logging subsystem (internal/audit) with structured event recording for cryptographic operations. - Add audit log engine spec (engines/auditlog.md). - Add ValidateName checks across all engines for path traversal (#48). - Update AUDIT.md: all High findings resolved (0 open). - Add REMEDIATION.md with detailed remediation tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -596,6 +596,9 @@ func (e *CAEngine) handleGetChain(_ context.Context, req *engine.Request) (*engi
|
||||
if issuerName == "" {
|
||||
issuerName = req.Path
|
||||
}
|
||||
if err := engine.ValidateName(issuerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chain, err := e.GetChainPEM(issuerName)
|
||||
if err != nil {
|
||||
@@ -610,6 +613,9 @@ func (e *CAEngine) handleGetChain(_ context.Context, req *engine.Request) (*engi
|
||||
|
||||
func (e *CAEngine) handleGetIssuer(_ context.Context, req *engine.Request) (*engine.Response, error) {
|
||||
name := req.Path
|
||||
if err := engine.ValidateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
certPEM, err := e.GetIssuerCertPEM(name)
|
||||
if err != nil {
|
||||
@@ -698,6 +704,7 @@ func (e *CAEngine) handleCreateIssuer(ctx context.Context, req *engine.Request)
|
||||
Expiry: expiry,
|
||||
}
|
||||
|
||||
e.setProfileAIA(&profile)
|
||||
issuerCert, err := profile.SignRequest(e.rootCert, csr, e.rootKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ca: sign issuer cert: %w", err)
|
||||
@@ -757,6 +764,9 @@ func (e *CAEngine) handleDeleteIssuer(ctx context.Context, req *engine.Request)
|
||||
if name == "" {
|
||||
name = req.Path
|
||||
}
|
||||
if err := engine.ValidateName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
@@ -830,6 +840,9 @@ func (e *CAEngine) handleIssue(ctx context.Context, req *engine.Request) (*engin
|
||||
if issuerName == "" {
|
||||
return nil, fmt.Errorf("ca: issuer name is required")
|
||||
}
|
||||
if err := engine.ValidateName(issuerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
profileName, _ := req.Data["profile"].(string)
|
||||
if profileName == "" {
|
||||
@@ -922,6 +935,7 @@ func (e *CAEngine) handleIssue(ctx context.Context, req *engine.Request) (*engin
|
||||
return nil, fmt.Errorf("ca: create leaf CSR: %w", err)
|
||||
}
|
||||
|
||||
e.setProfileAIA(&profile)
|
||||
leafCert, err := profile.SignRequest(is.cert, csr, is.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ca: sign leaf cert: %w", err)
|
||||
@@ -1171,6 +1185,7 @@ func (e *CAEngine) handleRenew(ctx context.Context, req *engine.Request) (*engin
|
||||
return nil, fmt.Errorf("ca: create renewal CSR: %w", err)
|
||||
}
|
||||
|
||||
e.setProfileAIA(&profile)
|
||||
newCert, err := profile.SignRequest(is.cert, csr, is.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ca: sign renewal cert: %w", err)
|
||||
@@ -1238,6 +1253,9 @@ func (e *CAEngine) handleSignCSR(ctx context.Context, req *engine.Request) (*eng
|
||||
if issuerName == "" {
|
||||
return nil, fmt.Errorf("ca: issuer name is required")
|
||||
}
|
||||
if err := engine.ValidateName(issuerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
csrPEM, _ := req.Data["csr_pem"].(string)
|
||||
if csrPEM == "" {
|
||||
@@ -1293,6 +1311,7 @@ func (e *CAEngine) handleSignCSR(ctx context.Context, req *engine.Request) (*eng
|
||||
}
|
||||
}
|
||||
|
||||
e.setProfileAIA(&profile)
|
||||
leafCert, err := profile.SignRequest(is.cert, csr, is.key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ca: sign CSR: %w", err)
|
||||
@@ -1436,6 +1455,20 @@ func (e *CAEngine) handleDeleteCert(ctx context.Context, req *engine.Request) (*
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
// setProfileAIA populates the AIA (Authority Information Access) extension
|
||||
// URLs on the profile if external_url is configured. This allows clients
|
||||
// to discover the issuing CA certificate for chain building.
|
||||
func (e *CAEngine) setProfileAIA(profile *certgen.Profile) {
|
||||
if e.config.ExternalURL == "" {
|
||||
return
|
||||
}
|
||||
base := strings.TrimSuffix(e.config.ExternalURL, "/")
|
||||
mount := e.mountName()
|
||||
profile.IssuingCertificateURL = []string{
|
||||
base + "/v1/pki/" + mount + "/ca/chain",
|
||||
}
|
||||
}
|
||||
|
||||
func defaultCAConfig() *CAConfig {
|
||||
return &CAConfig{
|
||||
Organization: "Metacircular",
|
||||
@@ -1461,6 +1494,9 @@ func mapToCAConfig(m map[string]interface{}, cfg *CAConfig) error {
|
||||
if v, ok := m["root_expiry"].(string); ok {
|
||||
cfg.RootExpiry = v
|
||||
}
|
||||
if v, ok := m["external_url"].(string); ok {
|
||||
cfg.ExternalURL = v
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,13 @@ func GetProfile(name string) (certgen.Profile, bool) {
|
||||
}
|
||||
// Return a copy so callers can modify.
|
||||
cp := certgen.Profile{
|
||||
IsCA: p.IsCA,
|
||||
PathLen: p.PathLen,
|
||||
Expiry: p.Expiry,
|
||||
KeyUse: make([]string, len(p.KeyUse)),
|
||||
ExtKeyUsages: make([]string, len(p.ExtKeyUsages)),
|
||||
IsCA: p.IsCA,
|
||||
PathLen: p.PathLen,
|
||||
Expiry: p.Expiry,
|
||||
KeyUse: make([]string, len(p.KeyUse)),
|
||||
ExtKeyUsages: make([]string, len(p.ExtKeyUsages)),
|
||||
OCSPServer: append([]string(nil), p.OCSPServer...),
|
||||
IssuingCertificateURL: append([]string(nil), p.IssuingCertificateURL...),
|
||||
}
|
||||
copy(cp.KeyUse, p.KeyUse)
|
||||
copy(cp.ExtKeyUsages, p.ExtKeyUsages)
|
||||
|
||||
@@ -10,6 +10,7 @@ type CAConfig struct {
|
||||
Country string `json:"country,omitempty"`
|
||||
KeyAlgorithm string `json:"key_algorithm"`
|
||||
RootExpiry string `json:"root_expiry"`
|
||||
ExternalURL string `json:"external_url,omitempty"`
|
||||
KeySize int `json:"key_size"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user