- Network-policy SPIRE plugin extension - Governance event notification with merkle anchoring - Shellstream specs for consent channels + HFL embedded ABI - All 17 audit findings from AUDIT.md remediated - SSH credential composer + substrate key manager updates - Test coverage for config + sshcert packages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
93 lines
3.1 KiB
Go
93 lines
3.1 KiB
Go
// Package config provides configuration loading for SPIRE plugins.
|
|
// SPIRE plugins receive configuration via HCL in the SPIRE server/agent config file.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/hcl/v2/hclsimple"
|
|
)
|
|
|
|
const (
|
|
// DefaultGovernanceEpochSeconds is used when governance_epoch_seconds is not set.
|
|
DefaultGovernanceEpochSeconds = 300
|
|
|
|
// MinGovernanceEpochSeconds is the minimum allowed epoch duration.
|
|
MinGovernanceEpochSeconds = 10
|
|
|
|
// MaxGovernanceEpochSeconds is the maximum allowed epoch duration.
|
|
MaxGovernanceEpochSeconds = 3600
|
|
)
|
|
|
|
// PluginConfig holds common configuration fields shared by all Guildhouse SPIRE plugins.
|
|
type PluginConfig struct {
|
|
// GovernanceAddr is the gRPC address of the GovernanceService.
|
|
GovernanceAddr string `hcl:"governance_addr"`
|
|
|
|
// CeremonyAddr is the gRPC address of the CeremonyService.
|
|
CeremonyAddr string `hcl:"ceremony_addr"`
|
|
|
|
// NotaryAddr is the gRPC address of the NotaryService.
|
|
NotaryAddr string `hcl:"notary_addr"`
|
|
|
|
// TrustDomain is the SPIFFE trust domain.
|
|
TrustDomain string `hcl:"trust_domain"`
|
|
|
|
// ClusterID identifies this cluster for notary anchoring.
|
|
ClusterID string `hcl:"cluster_id"`
|
|
|
|
// GovernanceEpochSeconds is the duration of a governance epoch in seconds.
|
|
// Controls how frequently merkle anchors are created. Must not exceed 256
|
|
// credential events per epoch (see shellstream merkle-proof depth limit).
|
|
// Default: 300 (5 minutes).
|
|
GovernanceEpochSeconds int `hcl:"governance_epoch_seconds"`
|
|
}
|
|
|
|
// Validate checks that required fields are present and within valid ranges.
|
|
func (c *PluginConfig) Validate() error {
|
|
if c.TrustDomain == "" {
|
|
return fmt.Errorf("config: trust_domain is required")
|
|
}
|
|
if c.GovernanceAddr == "" {
|
|
return fmt.Errorf("config: governance_addr is required")
|
|
}
|
|
if c.ClusterID == "" {
|
|
return fmt.Errorf("config: cluster_id is required")
|
|
}
|
|
|
|
// Apply default and validate range for epoch seconds.
|
|
if c.GovernanceEpochSeconds == 0 {
|
|
c.GovernanceEpochSeconds = DefaultGovernanceEpochSeconds
|
|
}
|
|
if c.GovernanceEpochSeconds < MinGovernanceEpochSeconds {
|
|
return fmt.Errorf("config: governance_epoch_seconds %d below minimum %d",
|
|
c.GovernanceEpochSeconds, MinGovernanceEpochSeconds)
|
|
}
|
|
if c.GovernanceEpochSeconds > MaxGovernanceEpochSeconds {
|
|
return fmt.Errorf("config: governance_epoch_seconds %d exceeds maximum %d",
|
|
c.GovernanceEpochSeconds, MaxGovernanceEpochSeconds)
|
|
}
|
|
|
|
// CeremonyAddr and NotaryAddr are optional but must not be whitespace-only if set.
|
|
if c.CeremonyAddr != "" && strings.TrimSpace(c.CeremonyAddr) == "" {
|
|
return fmt.Errorf("config: ceremony_addr is set but empty")
|
|
}
|
|
if c.NotaryAddr != "" && strings.TrimSpace(c.NotaryAddr) == "" {
|
|
return fmt.Errorf("config: notary_addr is set but empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadFromHCL parses plugin configuration from HCL bytes.
|
|
func LoadFromHCL(data []byte) (*PluginConfig, error) {
|
|
cfg := &PluginConfig{}
|
|
if err := hclsimple.Decode("plugin.hcl", data, nil, cfg); err != nil {
|
|
return nil, fmt.Errorf("config: parse HCL: %w", err)
|
|
}
|
|
if err := cfg.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return cfg, nil
|
|
}
|