You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							426 lines
						
					
					
						
							12 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							426 lines
						
					
					
						
							12 KiB
						
					
					
				| package kms | |
| 
 | |
| import ( | |
| 	"context" | |
| 	"fmt" | |
| 	"time" | |
| 
 | |
| 	"github.com/seaweedfs/seaweedfs/weed/glog" | |
| ) | |
| 
 | |
| // ViperConfig interface extends Configuration with additional methods needed for KMS configuration | |
| type ViperConfig interface { | |
| 	GetString(key string) string | |
| 	GetBool(key string) bool | |
| 	GetInt(key string) int | |
| 	GetStringSlice(key string) []string | |
| 	SetDefault(key string, value interface{}) | |
| 	GetStringMap(key string) map[string]interface{} | |
| 	IsSet(key string) bool | |
| } | |
| 
 | |
| // ConfigLoader handles loading KMS configurations from filer.toml | |
| type ConfigLoader struct { | |
| 	viper   ViperConfig | |
| 	manager *KMSManager | |
| } | |
| 
 | |
| // NewConfigLoader creates a new KMS configuration loader | |
| func NewConfigLoader(v ViperConfig) *ConfigLoader { | |
| 	return &ConfigLoader{ | |
| 		viper:   v, | |
| 		manager: GetKMSManager(), | |
| 	} | |
| } | |
| 
 | |
| // LoadConfigurations loads all KMS provider configurations from filer.toml | |
| func (loader *ConfigLoader) LoadConfigurations() error { | |
| 	// Check if KMS section exists | |
| 	if !loader.viper.IsSet("kms") { | |
| 		glog.V(1).Infof("No KMS configuration found in filer.toml") | |
| 		return nil | |
| 	} | |
| 
 | |
| 	// Get the KMS configuration section | |
| 	kmsConfig := loader.viper.GetStringMap("kms") | |
| 
 | |
| 	// Load global KMS settings | |
| 	if err := loader.loadGlobalKMSSettings(kmsConfig); err != nil { | |
| 		return fmt.Errorf("failed to load global KMS settings: %w", err) | |
| 	} | |
| 
 | |
| 	// Load KMS providers | |
| 	if providersConfig, exists := kmsConfig["providers"]; exists { | |
| 		if providers, ok := providersConfig.(map[string]interface{}); ok { | |
| 			if err := loader.loadKMSProviders(providers); err != nil { | |
| 				return fmt.Errorf("failed to load KMS providers: %w", err) | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	// Set default provider after all providers are loaded | |
| 	if err := loader.setDefaultProvider(); err != nil { | |
| 		return fmt.Errorf("failed to set default KMS provider: %w", err) | |
| 	} | |
| 
 | |
| 	// Initialize global KMS provider for backwards compatibility | |
| 	if err := loader.initializeGlobalKMSProvider(); err != nil { | |
| 		glog.Warningf("Failed to initialize global KMS provider: %v", err) | |
| 	} | |
| 
 | |
| 	// Load bucket-specific KMS configurations | |
| 	if bucketsConfig, exists := kmsConfig["buckets"]; exists { | |
| 		if buckets, ok := bucketsConfig.(map[string]interface{}); ok { | |
| 			if err := loader.loadBucketKMSConfigurations(buckets); err != nil { | |
| 				return fmt.Errorf("failed to load bucket KMS configurations: %w", err) | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	glog.V(1).Infof("KMS configuration loaded successfully") | |
| 	return nil | |
| } | |
| 
 | |
| // loadGlobalKMSSettings loads global KMS settings | |
| func (loader *ConfigLoader) loadGlobalKMSSettings(kmsConfig map[string]interface{}) error { | |
| 	// Set default KMS provider if specified | |
| 	if defaultProvider, exists := kmsConfig["default_provider"]; exists { | |
| 		if providerName, ok := defaultProvider.(string); ok { | |
| 			// We'll set this after providers are loaded | |
| 			glog.V(2).Infof("Default KMS provider will be set to: %s", providerName) | |
| 		} | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // loadKMSProviders loads individual KMS provider configurations | |
| func (loader *ConfigLoader) loadKMSProviders(providers map[string]interface{}) error { | |
| 	for providerName, providerConfigInterface := range providers { | |
| 		providerConfig, ok := providerConfigInterface.(map[string]interface{}) | |
| 		if !ok { | |
| 			glog.Warningf("Invalid configuration for KMS provider %s", providerName) | |
| 			continue | |
| 		} | |
| 
 | |
| 		if err := loader.loadSingleKMSProvider(providerName, providerConfig); err != nil { | |
| 			glog.Errorf("Failed to load KMS provider %s: %v", providerName, err) | |
| 			continue | |
| 		} | |
| 
 | |
| 		glog.V(1).Infof("Loaded KMS provider: %s", providerName) | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // loadSingleKMSProvider loads a single KMS provider configuration | |
| func (loader *ConfigLoader) loadSingleKMSProvider(providerName string, config map[string]interface{}) error { | |
| 	// Get provider type | |
| 	providerType, exists := config["type"] | |
| 	if !exists { | |
| 		return fmt.Errorf("provider type not specified for %s", providerName) | |
| 	} | |
| 
 | |
| 	providerTypeStr, ok := providerType.(string) | |
| 	if !ok { | |
| 		return fmt.Errorf("invalid provider type for %s", providerName) | |
| 	} | |
| 
 | |
| 	// Get provider-specific configuration | |
| 	providerConfig := make(map[string]interface{}) | |
| 	for key, value := range config { | |
| 		if key != "type" { | |
| 			providerConfig[key] = value | |
| 		} | |
| 	} | |
| 
 | |
| 	// Set default cache settings if not specified | |
| 	if _, exists := providerConfig["cache_enabled"]; !exists { | |
| 		providerConfig["cache_enabled"] = true | |
| 	} | |
| 
 | |
| 	if _, exists := providerConfig["cache_ttl"]; !exists { | |
| 		providerConfig["cache_ttl"] = "1h" | |
| 	} | |
| 
 | |
| 	if _, exists := providerConfig["max_cache_size"]; !exists { | |
| 		providerConfig["max_cache_size"] = 1000 | |
| 	} | |
| 
 | |
| 	// Parse cache TTL | |
| 	cacheTTL := time.Hour // default | |
| 	if ttlStr, exists := providerConfig["cache_ttl"]; exists { | |
| 		if ttlStrValue, ok := ttlStr.(string); ok { | |
| 			if parsed, err := time.ParseDuration(ttlStrValue); err == nil { | |
| 				cacheTTL = parsed | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	// Create KMS configuration | |
| 	kmsConfig := &KMSConfig{ | |
| 		Provider:     providerTypeStr, | |
| 		Config:       providerConfig, | |
| 		CacheEnabled: getBoolFromConfig(providerConfig, "cache_enabled", true), | |
| 		CacheTTL:     cacheTTL, | |
| 		MaxCacheSize: getIntFromConfig(providerConfig, "max_cache_size", 1000), | |
| 	} | |
| 
 | |
| 	// Add the provider to the KMS manager | |
| 	if err := loader.manager.AddKMSProvider(providerName, kmsConfig); err != nil { | |
| 		return err | |
| 	} | |
| 
 | |
| 	// Test the provider with a health check | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | |
| 	defer cancel() | |
| 
 | |
| 	health := loader.manager.GetKMSHealth(ctx) | |
| 	if providerHealth, exists := health[providerName]; exists && providerHealth != nil { | |
| 		glog.Warningf("KMS provider %s health check failed: %v", providerName, providerHealth) | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // loadBucketKMSConfigurations loads bucket-specific KMS configurations | |
| func (loader *ConfigLoader) loadBucketKMSConfigurations(buckets map[string]interface{}) error { | |
| 	for bucketName, bucketConfigInterface := range buckets { | |
| 		bucketConfig, ok := bucketConfigInterface.(map[string]interface{}) | |
| 		if !ok { | |
| 			glog.Warningf("Invalid KMS configuration for bucket %s", bucketName) | |
| 			continue | |
| 		} | |
| 
 | |
| 		// Get provider for this bucket | |
| 		if provider, exists := bucketConfig["provider"]; exists { | |
| 			if providerName, ok := provider.(string); ok { | |
| 				if err := loader.manager.SetBucketKMSProvider(bucketName, providerName); err != nil { | |
| 					glog.Errorf("Failed to set KMS provider for bucket %s: %v", bucketName, err) | |
| 					continue | |
| 				} | |
| 				glog.V(2).Infof("Set KMS provider for bucket %s to %s", bucketName, providerName) | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // setDefaultProvider sets the default KMS provider after all providers are loaded | |
| func (loader *ConfigLoader) setDefaultProvider() error { | |
| 	kmsConfig := loader.viper.GetStringMap("kms") | |
| 	if defaultProvider, exists := kmsConfig["default_provider"]; exists { | |
| 		if providerName, ok := defaultProvider.(string); ok { | |
| 			if err := loader.manager.SetDefaultKMSProvider(providerName); err != nil { | |
| 				return fmt.Errorf("failed to set default KMS provider: %w", err) | |
| 			} | |
| 			glog.V(1).Infof("Set default KMS provider to: %s", providerName) | |
| 		} | |
| 	} | |
| 	return nil | |
| } | |
| 
 | |
| // initializeGlobalKMSProvider initializes the global KMS provider for backwards compatibility | |
| func (loader *ConfigLoader) initializeGlobalKMSProvider() error { | |
| 	// Get the default provider from the manager | |
| 	defaultProviderName := "" | |
| 	kmsConfig := loader.viper.GetStringMap("kms") | |
| 	if defaultProvider, exists := kmsConfig["default_provider"]; exists { | |
| 		if providerName, ok := defaultProvider.(string); ok { | |
| 			defaultProviderName = providerName | |
| 		} | |
| 	} | |
| 
 | |
| 	if defaultProviderName == "" { | |
| 		// If no default provider, try to use the first available provider | |
| 		providers := loader.manager.ListKMSProviders() | |
| 		if len(providers) > 0 { | |
| 			defaultProviderName = providers[0] | |
| 		} | |
| 	} | |
| 
 | |
| 	if defaultProviderName == "" { | |
| 		glog.V(2).Infof("No KMS providers configured, skipping global KMS initialization") | |
| 		return nil | |
| 	} | |
| 
 | |
| 	// Get the provider from the manager | |
| 	provider, err := loader.manager.GetKMSProviderByName(defaultProviderName) | |
| 	if err != nil { | |
| 		return fmt.Errorf("failed to get KMS provider %s: %w", defaultProviderName, err) | |
| 	} | |
| 
 | |
| 	// Set as global KMS provider | |
| 	SetGlobalKMSProvider(provider) | |
| 	glog.V(1).Infof("Initialized global KMS provider: %s", defaultProviderName) | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // ValidateConfiguration validates the KMS configuration | |
| func (loader *ConfigLoader) ValidateConfiguration() error { | |
| 	providers := loader.manager.ListKMSProviders() | |
| 	if len(providers) == 0 { | |
| 		glog.V(1).Infof("No KMS providers configured") | |
| 		return nil | |
| 	} | |
| 
 | |
| 	// Test connectivity to all providers | |
| 	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) | |
| 	defer cancel() | |
| 
 | |
| 	health := loader.manager.GetKMSHealth(ctx) | |
| 	hasHealthyProvider := false | |
| 
 | |
| 	for providerName, err := range health { | |
| 		if err != nil { | |
| 			glog.Warningf("KMS provider %s is unhealthy: %v", providerName, err) | |
| 		} else { | |
| 			hasHealthyProvider = true | |
| 			glog.V(2).Infof("KMS provider %s is healthy", providerName) | |
| 		} | |
| 	} | |
| 
 | |
| 	if !hasHealthyProvider { | |
| 		glog.Warningf("No healthy KMS providers found") | |
| 	} | |
| 
 | |
| 	return nil | |
| } | |
| 
 | |
| // LoadKMSFromFilerToml is a convenience function to load KMS configuration from filer.toml | |
| func LoadKMSFromFilerToml(v ViperConfig) error { | |
| 	loader := NewConfigLoader(v) | |
| 	if err := loader.LoadConfigurations(); err != nil { | |
| 		return err | |
| 	} | |
| 	return loader.ValidateConfiguration() | |
| } | |
| 
 | |
| // LoadKMSFromConfig loads KMS configuration directly from parsed JSON data | |
| func LoadKMSFromConfig(kmsConfig interface{}) error { | |
| 	kmsMap, ok := kmsConfig.(map[string]interface{}) | |
| 	if !ok { | |
| 		return fmt.Errorf("invalid KMS configuration format") | |
| 	} | |
| 
 | |
| 	// Create a direct config adapter that doesn't use Viper | |
| 	// Wrap the KMS config under a "kms" key as expected by LoadConfigurations | |
| 	wrappedConfig := map[string]interface{}{ | |
| 		"kms": kmsMap, | |
| 	} | |
| 	adapter := &directConfigAdapter{config: wrappedConfig} | |
| 	loader := NewConfigLoader(adapter) | |
| 
 | |
| 	if err := loader.LoadConfigurations(); err != nil { | |
| 		return err | |
| 	} | |
| 
 | |
| 	return loader.ValidateConfiguration() | |
| } | |
| 
 | |
| // directConfigAdapter implements ViperConfig interface for direct map access | |
| type directConfigAdapter struct { | |
| 	config map[string]interface{} | |
| } | |
| 
 | |
| func (d *directConfigAdapter) GetStringMap(key string) map[string]interface{} { | |
| 	if val, exists := d.config[key]; exists { | |
| 		if mapVal, ok := val.(map[string]interface{}); ok { | |
| 			return mapVal | |
| 		} | |
| 	} | |
| 	return make(map[string]interface{}) | |
| } | |
| 
 | |
| func (d *directConfigAdapter) GetString(key string) string { | |
| 	if val, exists := d.config[key]; exists { | |
| 		if strVal, ok := val.(string); ok { | |
| 			return strVal | |
| 		} | |
| 	} | |
| 	return "" | |
| } | |
| 
 | |
| func (d *directConfigAdapter) GetBool(key string) bool { | |
| 	if val, exists := d.config[key]; exists { | |
| 		if boolVal, ok := val.(bool); ok { | |
| 			return boolVal | |
| 		} | |
| 	} | |
| 	return false | |
| } | |
| 
 | |
| func (d *directConfigAdapter) GetInt(key string) int { | |
| 	if val, exists := d.config[key]; exists { | |
| 		switch v := val.(type) { | |
| 		case int: | |
| 			return v | |
| 		case float64: | |
| 			return int(v) | |
| 		} | |
| 	} | |
| 	return 0 | |
| } | |
| 
 | |
| func (d *directConfigAdapter) GetStringSlice(key string) []string { | |
| 	if val, exists := d.config[key]; exists { | |
| 		if sliceVal, ok := val.([]interface{}); ok { | |
| 			result := make([]string, len(sliceVal)) | |
| 			for i, item := range sliceVal { | |
| 				if strItem, ok := item.(string); ok { | |
| 					result[i] = strItem | |
| 				} | |
| 			} | |
| 			return result | |
| 		} | |
| 		if strSlice, ok := val.([]string); ok { | |
| 			return strSlice | |
| 		} | |
| 	} | |
| 	return []string{} | |
| } | |
| 
 | |
| func (d *directConfigAdapter) SetDefault(key string, value interface{}) { | |
| 	// For direct config adapter, we don't need to set defaults | |
| 	// as the configuration is already parsed | |
| } | |
| 
 | |
| func (d *directConfigAdapter) IsSet(key string) bool { | |
| 	_, exists := d.config[key] | |
| 	return exists | |
| } | |
| 
 | |
| // Helper functions | |
|  | |
| func getBoolFromConfig(config map[string]interface{}, key string, defaultValue bool) bool { | |
| 	if value, exists := config[key]; exists { | |
| 		if boolValue, ok := value.(bool); ok { | |
| 			return boolValue | |
| 		} | |
| 	} | |
| 	return defaultValue | |
| } | |
| 
 | |
| func getIntFromConfig(config map[string]interface{}, key string, defaultValue int) int { | |
| 	if value, exists := config[key]; exists { | |
| 		if intValue, ok := value.(int); ok { | |
| 			return intValue | |
| 		} | |
| 		if floatValue, ok := value.(float64); ok { | |
| 			return int(floatValue) | |
| 		} | |
| 	} | |
| 	return defaultValue | |
| } | |
| 
 | |
| func getStringFromConfig(config map[string]interface{}, key string, defaultValue string) string { | |
| 	if value, exists := config[key]; exists { | |
| 		if stringValue, ok := value.(string); ok { | |
| 			return stringValue | |
| 		} | |
| 	} | |
| 	return defaultValue | |
| }
 |