// Package config provides KMS configuration types shared across KMS providers. package config import ( "fmt" "time" ) // ProviderType identifies the KMS provider implementation. type ProviderType string const ( ProviderTypeVault ProviderType = "vault" ProviderTypeAWS ProviderType = "aws" ProviderTypeMemory ProviderType = "memory" // Development only ) // IsValid returns true if the provider type is valid. func (t ProviderType) IsValid() bool { switch t { case ProviderTypeVault, ProviderTypeAWS, ProviderTypeMemory: return true } return false } // Config holds KMS provider configuration. type Config struct { Provider ProviderType `yaml:"provider"` Vault VaultConfig `yaml:"vault,omitempty"` AWS AWSConfig `yaml:"aws,omitempty"` Cache CacheConfig `yaml:"cache,omitempty"` } // VaultConfig holds HashiCorp Vault-specific configuration. type VaultConfig struct { Address string `yaml:"address"` AuthMethod string `yaml:"auth_method"` RoleID string `yaml:"role_id"` SecretID string `yaml:"secret_id"` Token string `yaml:"token"` TransitMount string `yaml:"transit_mount"` KeyPrefix string `yaml:"key_prefix"` Region string `yaml:"region"` Timeout time.Duration `yaml:"timeout"` } // AWSConfig holds AWS KMS-specific configuration. type AWSConfig struct { Region string `yaml:"region"` KeyAliasPrefix string `yaml:"key_alias_prefix"` RoleARN string `yaml:"role_arn,omitempty"` Endpoint string `yaml:"endpoint,omitempty"` } // CacheConfig holds DEK cache configuration per ADR-012. type CacheConfig struct { TTL time.Duration `yaml:"ttl_minutes"` MaxEntries int `yaml:"max_entries"` GraceWindow time.Duration `yaml:"grace_window_minutes"` } // DefaultCacheConfig returns the default cache configuration per ADR-012/013. func DefaultCacheConfig() CacheConfig { return CacheConfig{ TTL: 15 * time.Minute, MaxEntries: 1000, GraceWindow: 1 * time.Hour, } } // Validate checks the configuration for errors. func (c *Config) Validate() error { if !c.Provider.IsValid() { return fmt.Errorf("invalid KMS provider: %s", c.Provider) } switch c.Provider { case ProviderTypeVault: if err := c.Vault.Validate(); err != nil { return fmt.Errorf("vault config: %w", err) } case ProviderTypeAWS: if err := c.AWS.Validate(); err != nil { return fmt.Errorf("aws config: %w", err) } } // Apply defaults for cache config if c.Cache.TTL == 0 { c.Cache.TTL = DefaultCacheConfig().TTL } if c.Cache.MaxEntries == 0 { c.Cache.MaxEntries = DefaultCacheConfig().MaxEntries } if c.Cache.GraceWindow == 0 { c.Cache.GraceWindow = DefaultCacheConfig().GraceWindow } return nil } // Validate checks Vault configuration. func (v *VaultConfig) Validate() error { if v.Address == "" { return fmt.Errorf("vault address is required") } switch v.AuthMethod { case "approle": if v.RoleID == "" || v.SecretID == "" { return fmt.Errorf("approle auth requires role_id and secret_id") } case "kubernetes": // Kubernetes auth uses service account token case "token": if v.Token == "" { return fmt.Errorf("token auth requires token") } default: return fmt.Errorf("invalid auth_method: %s", v.AuthMethod) } // Apply defaults if v.TransitMount == "" { v.TransitMount = "transit" } if v.KeyPrefix == "" { v.KeyPrefix = "fetchml-tenant" } if v.Timeout == 0 { v.Timeout = 30 * time.Second } return nil } // Validate checks AWS configuration. func (a *AWSConfig) Validate() error { if a.Region == "" { return fmt.Errorf("AWS region is required") } // Apply defaults if a.KeyAliasPrefix == "" { a.KeyAliasPrefix = "alias/fetchml" } return nil } // KeyIDForTenant generates a KMS key ID for a tenant based on the provider config. func (c *Config) KeyIDForTenant(tenantID string) string { switch c.Provider { case ProviderTypeVault: if c.Vault.Region != "" { return fmt.Sprintf("%s-%s-%s", c.Vault.KeyPrefix, c.Vault.Region, tenantID) } return fmt.Sprintf("%s-%s", c.Vault.KeyPrefix, tenantID) case ProviderTypeAWS: if c.AWS.Region != "" { return fmt.Sprintf("%s-%s-%s", c.AWS.KeyAliasPrefix, c.AWS.Region, tenantID) } return fmt.Sprintf("%s-%s", c.AWS.KeyAliasPrefix, tenantID) default: return tenantID } }