config.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. package kms
  2. import (
  3. "context"
  4. "fmt"
  5. "sync"
  6. "time"
  7. "github.com/seaweedfs/seaweedfs/weed/glog"
  8. "github.com/seaweedfs/seaweedfs/weed/util"
  9. )
  10. // KMSManager manages KMS provider instances and configurations
  11. type KMSManager struct {
  12. mu sync.RWMutex
  13. providers map[string]KMSProvider // provider name -> provider instance
  14. configs map[string]*KMSConfig // provider name -> configuration
  15. bucketKMS map[string]string // bucket name -> provider name
  16. defaultKMS string // default KMS provider name
  17. }
  18. // KMSConfig represents a complete KMS provider configuration
  19. type KMSConfig struct {
  20. Provider string `json:"provider"` // Provider type (aws, azure, gcp, local)
  21. Config map[string]interface{} `json:"config"` // Provider-specific configuration
  22. CacheEnabled bool `json:"cache_enabled"` // Enable data key caching
  23. CacheTTL time.Duration `json:"cache_ttl"` // Cache TTL (default: 1 hour)
  24. MaxCacheSize int `json:"max_cache_size"` // Maximum cached keys (default: 1000)
  25. }
  26. // BucketKMSConfig represents KMS configuration for a specific bucket
  27. type BucketKMSConfig struct {
  28. Provider string `json:"provider"` // KMS provider to use
  29. KeyID string `json:"key_id"` // Default KMS key ID for this bucket
  30. BucketKey bool `json:"bucket_key"` // Enable S3 Bucket Keys optimization
  31. Context map[string]string `json:"context"` // Additional encryption context
  32. Enabled bool `json:"enabled"` // Whether KMS encryption is enabled
  33. }
  34. // configAdapter adapts KMSConfig.Config to util.Configuration interface
  35. type configAdapter struct {
  36. config map[string]interface{}
  37. }
  38. // GetConfigMap returns the underlying configuration map for direct access
  39. func (c *configAdapter) GetConfigMap() map[string]interface{} {
  40. return c.config
  41. }
  42. func (c *configAdapter) GetString(key string) string {
  43. if val, ok := c.config[key]; ok {
  44. if str, ok := val.(string); ok {
  45. return str
  46. }
  47. }
  48. return ""
  49. }
  50. func (c *configAdapter) GetBool(key string) bool {
  51. if val, ok := c.config[key]; ok {
  52. if b, ok := val.(bool); ok {
  53. return b
  54. }
  55. }
  56. return false
  57. }
  58. func (c *configAdapter) GetInt(key string) int {
  59. if val, ok := c.config[key]; ok {
  60. if i, ok := val.(int); ok {
  61. return i
  62. }
  63. if f, ok := val.(float64); ok {
  64. return int(f)
  65. }
  66. }
  67. return 0
  68. }
  69. func (c *configAdapter) GetStringSlice(key string) []string {
  70. if val, ok := c.config[key]; ok {
  71. if slice, ok := val.([]string); ok {
  72. return slice
  73. }
  74. if interfaceSlice, ok := val.([]interface{}); ok {
  75. result := make([]string, len(interfaceSlice))
  76. for i, v := range interfaceSlice {
  77. if str, ok := v.(string); ok {
  78. result[i] = str
  79. }
  80. }
  81. return result
  82. }
  83. }
  84. return nil
  85. }
  86. func (c *configAdapter) SetDefault(key string, value interface{}) {
  87. if c.config == nil {
  88. c.config = make(map[string]interface{})
  89. }
  90. if _, exists := c.config[key]; !exists {
  91. c.config[key] = value
  92. }
  93. }
  94. var (
  95. globalKMSManager *KMSManager
  96. globalKMSMutex sync.RWMutex
  97. // Global KMS provider for legacy compatibility
  98. globalKMSProvider KMSProvider
  99. )
  100. // InitializeGlobalKMS initializes the global KMS provider
  101. func InitializeGlobalKMS(config *KMSConfig) error {
  102. if config == nil || config.Provider == "" {
  103. return fmt.Errorf("KMS configuration is required")
  104. }
  105. // Adapt the config to util.Configuration interface
  106. var providerConfig util.Configuration
  107. if config.Config != nil {
  108. providerConfig = &configAdapter{config: config.Config}
  109. }
  110. provider, err := GetProvider(config.Provider, providerConfig)
  111. if err != nil {
  112. return err
  113. }
  114. globalKMSMutex.Lock()
  115. defer globalKMSMutex.Unlock()
  116. // Close existing provider if any
  117. if globalKMSProvider != nil {
  118. globalKMSProvider.Close()
  119. }
  120. globalKMSProvider = provider
  121. return nil
  122. }
  123. // GetGlobalKMS returns the global KMS provider
  124. func GetGlobalKMS() KMSProvider {
  125. globalKMSMutex.RLock()
  126. defer globalKMSMutex.RUnlock()
  127. return globalKMSProvider
  128. }
  129. // IsKMSEnabled returns true if KMS is enabled globally
  130. func IsKMSEnabled() bool {
  131. return GetGlobalKMS() != nil
  132. }
  133. // SetGlobalKMSProvider sets the global KMS provider.
  134. // This is mainly for backward compatibility.
  135. func SetGlobalKMSProvider(provider KMSProvider) {
  136. globalKMSMutex.Lock()
  137. defer globalKMSMutex.Unlock()
  138. // Close existing provider if any
  139. if globalKMSProvider != nil {
  140. globalKMSProvider.Close()
  141. }
  142. globalKMSProvider = provider
  143. }
  144. // InitializeKMSManager initializes the global KMS manager
  145. func InitializeKMSManager() *KMSManager {
  146. globalKMSMutex.Lock()
  147. defer globalKMSMutex.Unlock()
  148. if globalKMSManager == nil {
  149. globalKMSManager = &KMSManager{
  150. providers: make(map[string]KMSProvider),
  151. configs: make(map[string]*KMSConfig),
  152. bucketKMS: make(map[string]string),
  153. }
  154. glog.V(1).Infof("KMS Manager initialized")
  155. }
  156. return globalKMSManager
  157. }
  158. // GetKMSManager returns the global KMS manager
  159. func GetKMSManager() *KMSManager {
  160. globalKMSMutex.RLock()
  161. manager := globalKMSManager
  162. globalKMSMutex.RUnlock()
  163. if manager == nil {
  164. return InitializeKMSManager()
  165. }
  166. return manager
  167. }
  168. // AddKMSProvider adds a KMS provider configuration
  169. func (km *KMSManager) AddKMSProvider(name string, config *KMSConfig) error {
  170. if name == "" {
  171. return fmt.Errorf("provider name cannot be empty")
  172. }
  173. if config == nil {
  174. return fmt.Errorf("KMS configuration cannot be nil")
  175. }
  176. km.mu.Lock()
  177. defer km.mu.Unlock()
  178. // Close existing provider if it exists
  179. if existingProvider, exists := km.providers[name]; exists {
  180. if err := existingProvider.Close(); err != nil {
  181. glog.Errorf("Failed to close existing KMS provider %s: %v", name, err)
  182. }
  183. }
  184. // Create new provider instance
  185. configAdapter := &configAdapter{config: config.Config}
  186. provider, err := GetProvider(config.Provider, configAdapter)
  187. if err != nil {
  188. return fmt.Errorf("failed to create KMS provider %s: %w", name, err)
  189. }
  190. // Store provider and configuration
  191. km.providers[name] = provider
  192. km.configs[name] = config
  193. glog.V(1).Infof("Added KMS provider %s (type: %s)", name, config.Provider)
  194. return nil
  195. }
  196. // SetDefaultKMSProvider sets the default KMS provider
  197. func (km *KMSManager) SetDefaultKMSProvider(name string) error {
  198. km.mu.RLock()
  199. _, exists := km.providers[name]
  200. km.mu.RUnlock()
  201. if !exists {
  202. return fmt.Errorf("KMS provider %s does not exist", name)
  203. }
  204. km.mu.Lock()
  205. km.defaultKMS = name
  206. km.mu.Unlock()
  207. glog.V(1).Infof("Set default KMS provider to %s", name)
  208. return nil
  209. }
  210. // SetBucketKMSProvider sets the KMS provider for a specific bucket
  211. func (km *KMSManager) SetBucketKMSProvider(bucket, providerName string) error {
  212. if bucket == "" {
  213. return fmt.Errorf("bucket name cannot be empty")
  214. }
  215. km.mu.RLock()
  216. _, exists := km.providers[providerName]
  217. km.mu.RUnlock()
  218. if !exists {
  219. return fmt.Errorf("KMS provider %s does not exist", providerName)
  220. }
  221. km.mu.Lock()
  222. km.bucketKMS[bucket] = providerName
  223. km.mu.Unlock()
  224. glog.V(2).Infof("Set KMS provider for bucket %s to %s", bucket, providerName)
  225. return nil
  226. }
  227. // GetKMSProvider returns the KMS provider for a bucket (or default if not configured)
  228. func (km *KMSManager) GetKMSProvider(bucket string) (KMSProvider, error) {
  229. km.mu.RLock()
  230. defer km.mu.RUnlock()
  231. // Try bucket-specific provider first
  232. if bucket != "" {
  233. if providerName, exists := km.bucketKMS[bucket]; exists {
  234. if provider, exists := km.providers[providerName]; exists {
  235. return provider, nil
  236. }
  237. }
  238. }
  239. // Fall back to default provider
  240. if km.defaultKMS != "" {
  241. if provider, exists := km.providers[km.defaultKMS]; exists {
  242. return provider, nil
  243. }
  244. }
  245. // No provider configured
  246. return nil, fmt.Errorf("no KMS provider configured for bucket %s", bucket)
  247. }
  248. // GetKMSProviderByName returns a specific KMS provider by name
  249. func (km *KMSManager) GetKMSProviderByName(name string) (KMSProvider, error) {
  250. km.mu.RLock()
  251. defer km.mu.RUnlock()
  252. provider, exists := km.providers[name]
  253. if !exists {
  254. return nil, fmt.Errorf("KMS provider %s not found", name)
  255. }
  256. return provider, nil
  257. }
  258. // ListKMSProviders returns all configured KMS provider names
  259. func (km *KMSManager) ListKMSProviders() []string {
  260. km.mu.RLock()
  261. defer km.mu.RUnlock()
  262. names := make([]string, 0, len(km.providers))
  263. for name := range km.providers {
  264. names = append(names, name)
  265. }
  266. return names
  267. }
  268. // GetBucketKMSProvider returns the KMS provider name for a bucket
  269. func (km *KMSManager) GetBucketKMSProvider(bucket string) string {
  270. km.mu.RLock()
  271. defer km.mu.RUnlock()
  272. if providerName, exists := km.bucketKMS[bucket]; exists {
  273. return providerName
  274. }
  275. return km.defaultKMS
  276. }
  277. // RemoveKMSProvider removes a KMS provider
  278. func (km *KMSManager) RemoveKMSProvider(name string) error {
  279. km.mu.Lock()
  280. defer km.mu.Unlock()
  281. provider, exists := km.providers[name]
  282. if !exists {
  283. return fmt.Errorf("KMS provider %s does not exist", name)
  284. }
  285. // Close the provider
  286. if err := provider.Close(); err != nil {
  287. glog.Errorf("Failed to close KMS provider %s: %v", name, err)
  288. }
  289. // Remove from maps
  290. delete(km.providers, name)
  291. delete(km.configs, name)
  292. // Remove from bucket associations
  293. for bucket, providerName := range km.bucketKMS {
  294. if providerName == name {
  295. delete(km.bucketKMS, bucket)
  296. }
  297. }
  298. // Clear default if it was this provider
  299. if km.defaultKMS == name {
  300. km.defaultKMS = ""
  301. }
  302. glog.V(1).Infof("Removed KMS provider %s", name)
  303. return nil
  304. }
  305. // Close closes all KMS providers and cleans up resources
  306. func (km *KMSManager) Close() error {
  307. km.mu.Lock()
  308. defer km.mu.Unlock()
  309. var allErrors []error
  310. for name, provider := range km.providers {
  311. if err := provider.Close(); err != nil {
  312. allErrors = append(allErrors, fmt.Errorf("failed to close KMS provider %s: %w", name, err))
  313. }
  314. }
  315. // Clear all maps
  316. km.providers = make(map[string]KMSProvider)
  317. km.configs = make(map[string]*KMSConfig)
  318. km.bucketKMS = make(map[string]string)
  319. km.defaultKMS = ""
  320. if len(allErrors) > 0 {
  321. return fmt.Errorf("errors closing KMS providers: %v", allErrors)
  322. }
  323. glog.V(1).Infof("KMS Manager closed")
  324. return nil
  325. }
  326. // GenerateDataKeyForBucket generates a data key using the appropriate KMS provider for a bucket
  327. func (km *KMSManager) GenerateDataKeyForBucket(ctx context.Context, bucket, keyID string, keySpec KeySpec, encryptionContext map[string]string) (*GenerateDataKeyResponse, error) {
  328. provider, err := km.GetKMSProvider(bucket)
  329. if err != nil {
  330. return nil, fmt.Errorf("failed to get KMS provider for bucket %s: %w", bucket, err)
  331. }
  332. req := &GenerateDataKeyRequest{
  333. KeyID: keyID,
  334. KeySpec: keySpec,
  335. EncryptionContext: encryptionContext,
  336. }
  337. return provider.GenerateDataKey(ctx, req)
  338. }
  339. // DecryptForBucket decrypts a data key using the appropriate KMS provider for a bucket
  340. func (km *KMSManager) DecryptForBucket(ctx context.Context, bucket string, ciphertextBlob []byte, encryptionContext map[string]string) (*DecryptResponse, error) {
  341. provider, err := km.GetKMSProvider(bucket)
  342. if err != nil {
  343. return nil, fmt.Errorf("failed to get KMS provider for bucket %s: %w", bucket, err)
  344. }
  345. req := &DecryptRequest{
  346. CiphertextBlob: ciphertextBlob,
  347. EncryptionContext: encryptionContext,
  348. }
  349. return provider.Decrypt(ctx, req)
  350. }
  351. // ValidateKeyForBucket validates that a KMS key exists and is usable for a bucket
  352. func (km *KMSManager) ValidateKeyForBucket(ctx context.Context, bucket, keyID string) error {
  353. provider, err := km.GetKMSProvider(bucket)
  354. if err != nil {
  355. return fmt.Errorf("failed to get KMS provider for bucket %s: %w", bucket, err)
  356. }
  357. req := &DescribeKeyRequest{KeyID: keyID}
  358. resp, err := provider.DescribeKey(ctx, req)
  359. if err != nil {
  360. return fmt.Errorf("failed to validate key %s for bucket %s: %w", keyID, bucket, err)
  361. }
  362. // Check key state
  363. if resp.KeyState != KeyStateEnabled {
  364. return fmt.Errorf("key %s is not enabled (state: %s)", keyID, resp.KeyState)
  365. }
  366. // Check key usage
  367. if resp.KeyUsage != KeyUsageEncryptDecrypt && resp.KeyUsage != KeyUsageGenerateDataKey {
  368. return fmt.Errorf("key %s cannot be used for encryption (usage: %s)", keyID, resp.KeyUsage)
  369. }
  370. return nil
  371. }
  372. // GetKMSHealth returns health status of all KMS providers
  373. func (km *KMSManager) GetKMSHealth(ctx context.Context) map[string]error {
  374. km.mu.RLock()
  375. defer km.mu.RUnlock()
  376. health := make(map[string]error)
  377. for name, provider := range km.providers {
  378. // Try to perform a basic operation to check health
  379. // We'll use DescribeKey with a dummy key - the error will tell us if KMS is reachable
  380. req := &DescribeKeyRequest{KeyID: "health-check-dummy-key"}
  381. _, err := provider.DescribeKey(ctx, req)
  382. // If it's a "not found" error, KMS is healthy but key doesn't exist (expected)
  383. if kmsErr, ok := err.(*KMSError); ok && kmsErr.Code == ErrCodeNotFoundException {
  384. health[name] = nil // Healthy
  385. } else if err != nil {
  386. health[name] = err // Unhealthy
  387. } else {
  388. health[name] = nil // Healthy (shouldn't happen with dummy key, but just in case)
  389. }
  390. }
  391. return health
  392. }