Add KMSProvider interface for external key management systems: - Encrypt/Decrypt operations for DEK wrapping - Key lifecycle management (Create, Disable, ScheduleDeletion, Enable) - HealthCheck and Close methods Implement MemoryProvider for development/testing: - XOR encryption with HMAC-SHA256 authentication - Secure random key generation using crypto/rand - MAC verification to detect wrong keys Implement DEKCache per ADR-012: - 15-minute TTL with configurable grace window (1 hour) - LRU eviction with 1000 entry limit - Cache key includes (tenantID, artifactID, kmsKeyID) for isolation - Thread-safe operations with RWMutex - Secure memory wiping on eviction/cleanup Add config package with types: - ProviderType enum (vault, aws, memory) - VaultConfig with AppRole/Kubernetes/Token auth - AWSConfig with region and alias prefix - CacheConfig with TTL, MaxEntries, GraceWindow - Validation methods for all config types
168 lines
4.3 KiB
Go
168 lines
4.3 KiB
Go
// 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
|
|
}
|
|
}
|