github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/kms/kes.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package kms 19 20 import ( 21 "bytes" 22 "context" 23 "crypto/subtle" 24 "crypto/tls" 25 "crypto/x509" 26 "errors" 27 "fmt" 28 "strings" 29 "sync" 30 31 "github.com/minio/kms-go/kes" 32 "github.com/minio/pkg/v2/certs" 33 "github.com/minio/pkg/v2/env" 34 ) 35 36 const ( 37 tlsClientSessionCacheSize = 100 38 ) 39 40 // Config contains various KMS-related configuration 41 // parameters - like KMS endpoints or authentication 42 // credentials. 43 type Config struct { 44 // Endpoints contains a list of KMS server 45 // HTTP endpoints. 46 Endpoints []string 47 48 // DefaultKeyID is the key ID used when 49 // no explicit key ID is specified for 50 // a cryptographic operation. 51 DefaultKeyID string 52 53 // APIKey is an credential provided by env. var. 54 // to authenticate to a KES server. Either an 55 // API key or a client certificate must be specified. 56 APIKey kes.APIKey 57 58 // Certificate is the client TLS certificate 59 // to authenticate to KMS via mTLS. 60 Certificate *certs.Certificate 61 62 // ReloadCertEvents is an event channel that receives 63 // the reloaded client certificate. 64 ReloadCertEvents <-chan tls.Certificate 65 66 // RootCAs is a set of root CA certificates 67 // to verify the KMS server TLS certificate. 68 RootCAs *x509.CertPool 69 } 70 71 // NewWithConfig returns a new KMS using the given 72 // configuration. 73 func NewWithConfig(config Config) (KMS, error) { 74 if len(config.Endpoints) == 0 { 75 return nil, errors.New("kms: no server endpoints") 76 } 77 endpoints := make([]string, len(config.Endpoints)) // Copy => avoid being affect by any changes to the original slice 78 copy(endpoints, config.Endpoints) 79 80 var client *kes.Client 81 if config.APIKey != nil { 82 cert, err := kes.GenerateCertificate(config.APIKey) 83 if err != nil { 84 return nil, err 85 } 86 client = kes.NewClientWithConfig("", &tls.Config{ 87 MinVersion: tls.VersionTLS12, 88 Certificates: []tls.Certificate{cert}, 89 RootCAs: config.RootCAs, 90 ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), 91 }) 92 } else { 93 client = kes.NewClientWithConfig("", &tls.Config{ 94 MinVersion: tls.VersionTLS12, 95 Certificates: []tls.Certificate{config.Certificate.Get()}, 96 RootCAs: config.RootCAs, 97 ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), 98 }) 99 } 100 client.Endpoints = endpoints 101 102 c := &kesClient{ 103 client: client, 104 defaultKeyID: config.DefaultKeyID, 105 } 106 go func() { 107 if config.Certificate == nil || config.ReloadCertEvents == nil { 108 return 109 } 110 var prevCertificate tls.Certificate 111 for { 112 certificate, ok := <-config.ReloadCertEvents 113 if !ok { 114 return 115 } 116 sameCert := len(certificate.Certificate) == len(prevCertificate.Certificate) 117 for i, b := range certificate.Certificate { 118 if !sameCert { 119 break 120 } 121 sameCert = sameCert && bytes.Equal(b, prevCertificate.Certificate[i]) 122 } 123 // Do not reload if its the same cert as before. 124 if !sameCert { 125 client := kes.NewClientWithConfig("", &tls.Config{ 126 MinVersion: tls.VersionTLS12, 127 Certificates: []tls.Certificate{certificate}, 128 RootCAs: config.RootCAs, 129 ClientSessionCache: tls.NewLRUClientSessionCache(tlsClientSessionCacheSize), 130 }) 131 client.Endpoints = endpoints 132 133 c.lock.Lock() 134 c.client = client 135 c.lock.Unlock() 136 137 prevCertificate = certificate 138 } 139 } 140 }() 141 return c, nil 142 } 143 144 type kesClient struct { 145 lock sync.RWMutex 146 defaultKeyID string 147 client *kes.Client 148 } 149 150 var ( // compiler checks 151 _ KMS = (*kesClient)(nil) 152 _ KeyManager = (*kesClient)(nil) 153 _ IdentityManager = (*kesClient)(nil) 154 _ PolicyManager = (*kesClient)(nil) 155 ) 156 157 // Stat returns the current KES status containing a 158 // list of KES endpoints and the default key ID. 159 func (c *kesClient) Stat(ctx context.Context) (Status, error) { 160 c.lock.RLock() 161 defer c.lock.RUnlock() 162 163 st, err := c.client.Status(ctx) 164 if err != nil { 165 return Status{}, err 166 } 167 endpoints := make([]string, len(c.client.Endpoints)) 168 copy(endpoints, c.client.Endpoints) 169 return Status{ 170 Name: "KES", 171 Endpoints: endpoints, 172 DefaultKey: c.defaultKeyID, 173 Details: st, 174 }, nil 175 } 176 177 // IsLocal returns true if the KMS is a local implementation 178 func (c *kesClient) IsLocal() bool { 179 return env.IsSet(EnvKMSSecretKey) 180 } 181 182 // List returns an array of local KMS Names 183 func (c *kesClient) List() []kes.KeyInfo { 184 var kmsSecret []kes.KeyInfo 185 envKMSSecretKey := env.Get(EnvKMSSecretKey, "") 186 values := strings.SplitN(envKMSSecretKey, ":", 2) 187 if len(values) == 2 { 188 kmsSecret = []kes.KeyInfo{ 189 { 190 Name: values[0], 191 }, 192 } 193 } 194 return kmsSecret 195 } 196 197 // Metrics retrieves server metrics in the Prometheus exposition format. 198 func (c *kesClient) Metrics(ctx context.Context) (kes.Metric, error) { 199 c.lock.RLock() 200 defer c.lock.RUnlock() 201 202 return c.client.Metrics(ctx) 203 } 204 205 // Version retrieves version information 206 func (c *kesClient) Version(ctx context.Context) (string, error) { 207 c.lock.RLock() 208 defer c.lock.RUnlock() 209 210 return c.client.Version(ctx) 211 } 212 213 // APIs retrieves a list of supported API endpoints 214 func (c *kesClient) APIs(ctx context.Context) ([]kes.API, error) { 215 c.lock.RLock() 216 defer c.lock.RUnlock() 217 218 return c.client.APIs(ctx) 219 } 220 221 // CreateKey tries to create a new key at the KMS with the 222 // given key ID. 223 // 224 // If the a key with the same keyID already exists then 225 // CreateKey returns kes.ErrKeyExists. 226 func (c *kesClient) CreateKey(ctx context.Context, keyID string) error { 227 c.lock.RLock() 228 defer c.lock.RUnlock() 229 230 return c.client.CreateKey(ctx, keyID) 231 } 232 233 // DeleteKey deletes a key at the KMS with the given key ID. 234 // Please note that is a dangerous operation. 235 // Once a key has been deleted all data that has been encrypted with it cannot be decrypted 236 // anymore, and therefore, is lost. 237 func (c *kesClient) DeleteKey(ctx context.Context, keyID string) error { 238 c.lock.RLock() 239 defer c.lock.RUnlock() 240 241 return c.client.DeleteKey(ctx, keyID) 242 } 243 244 // ListKeys returns an iterator over all key names. 245 func (c *kesClient) ListKeys(ctx context.Context) (*kes.ListIter[string], error) { 246 c.lock.RLock() 247 defer c.lock.RUnlock() 248 249 return &kes.ListIter[string]{ 250 NextFunc: c.client.ListKeys, 251 }, nil 252 } 253 254 // GenerateKey generates a new data encryption key using 255 // the key at the KES server referenced by the key ID. 256 // 257 // The default key ID will be used if keyID is empty. 258 // 259 // The context is associated and tied to the generated DEK. 260 // The same context must be provided when the generated 261 // key should be decrypted. 262 func (c *kesClient) GenerateKey(ctx context.Context, keyID string, cryptoCtx Context) (DEK, error) { 263 c.lock.RLock() 264 defer c.lock.RUnlock() 265 266 if keyID == "" { 267 keyID = c.defaultKeyID 268 } 269 ctxBytes, err := cryptoCtx.MarshalText() 270 if err != nil { 271 return DEK{}, err 272 } 273 274 dek, err := c.client.GenerateKey(ctx, keyID, ctxBytes) 275 if err != nil { 276 return DEK{}, err 277 } 278 return DEK{ 279 KeyID: keyID, 280 Plaintext: dek.Plaintext, 281 Ciphertext: dek.Ciphertext, 282 }, nil 283 } 284 285 // ImportKey imports a cryptographic key into the KMS. 286 func (c *kesClient) ImportKey(ctx context.Context, keyID string, bytes []byte) error { 287 c.lock.RLock() 288 defer c.lock.RUnlock() 289 290 return c.client.ImportKey(ctx, keyID, &kes.ImportKeyRequest{ 291 Key: bytes, 292 }) 293 } 294 295 // EncryptKey Encrypts and authenticates a (small) plaintext with the cryptographic key 296 // The plaintext must not exceed 1 MB 297 func (c *kesClient) EncryptKey(keyID string, plaintext []byte, ctx Context) ([]byte, error) { 298 c.lock.RLock() 299 defer c.lock.RUnlock() 300 301 ctxBytes, err := ctx.MarshalText() 302 if err != nil { 303 return nil, err 304 } 305 return c.client.Encrypt(context.Background(), keyID, plaintext, ctxBytes) 306 } 307 308 // DecryptKey decrypts the ciphertext with the key at the KES 309 // server referenced by the key ID. The context must match the 310 // context value used to generate the ciphertext. 311 func (c *kesClient) DecryptKey(keyID string, ciphertext []byte, ctx Context) ([]byte, error) { 312 c.lock.RLock() 313 defer c.lock.RUnlock() 314 315 ctxBytes, err := ctx.MarshalText() 316 if err != nil { 317 return nil, err 318 } 319 return c.client.Decrypt(context.Background(), keyID, ciphertext, ctxBytes) 320 } 321 322 func (c *kesClient) DecryptAll(ctx context.Context, keyID string, ciphertexts [][]byte, contexts []Context) ([][]byte, error) { 323 c.lock.RLock() 324 defer c.lock.RUnlock() 325 326 plaintexts := make([][]byte, 0, len(ciphertexts)) 327 for i := range ciphertexts { 328 ctxBytes, err := contexts[i].MarshalText() 329 if err != nil { 330 return nil, err 331 } 332 plaintext, err := c.client.Decrypt(ctx, keyID, ciphertexts[i], ctxBytes) 333 if err != nil { 334 return nil, err 335 } 336 plaintexts = append(plaintexts, plaintext) 337 } 338 return plaintexts, nil 339 } 340 341 // HMAC generates the HMAC checksum of the given msg using the key 342 // with the given keyID at the KMS. 343 func (c *kesClient) HMAC(ctx context.Context, keyID string, msg []byte) ([]byte, error) { 344 c.lock.RLock() 345 defer c.lock.RUnlock() 346 347 return c.client.HMAC(context.Background(), keyID, msg) 348 } 349 350 // DescribePolicy describes a policy by returning its metadata. 351 // e.g. who created the policy at which point in time. 352 func (c *kesClient) DescribePolicy(ctx context.Context, policy string) (*kes.PolicyInfo, error) { 353 c.lock.RLock() 354 defer c.lock.RUnlock() 355 356 return c.client.DescribePolicy(ctx, policy) 357 } 358 359 // ListPolicies returns an iterator over all policy names. 360 func (c *kesClient) ListPolicies(ctx context.Context) (*kes.ListIter[string], error) { 361 c.lock.RLock() 362 defer c.lock.RUnlock() 363 364 return &kes.ListIter[string]{ 365 NextFunc: c.client.ListPolicies, 366 }, nil 367 } 368 369 // GetPolicy gets a policy from KMS. 370 func (c *kesClient) GetPolicy(ctx context.Context, policy string) (*kes.Policy, error) { 371 c.lock.RLock() 372 defer c.lock.RUnlock() 373 374 return c.client.GetPolicy(ctx, policy) 375 } 376 377 // DescribeIdentity describes an identity by returning its metadata. 378 // e.g. which policy is currently assigned and whether its an admin identity. 379 func (c *kesClient) DescribeIdentity(ctx context.Context, identity string) (*kes.IdentityInfo, error) { 380 c.lock.RLock() 381 defer c.lock.RUnlock() 382 383 return c.client.DescribeIdentity(ctx, kes.Identity(identity)) 384 } 385 386 // DescribeSelfIdentity describes the identity issuing the request. 387 // It infers the identity from the TLS client certificate used to authenticate. 388 // It returns the identity and policy information for the client identity. 389 func (c *kesClient) DescribeSelfIdentity(ctx context.Context) (*kes.IdentityInfo, *kes.Policy, error) { 390 c.lock.RLock() 391 defer c.lock.RUnlock() 392 393 return c.client.DescribeSelf(ctx) 394 } 395 396 // ListPolicies returns an iterator over all identities. 397 func (c *kesClient) ListIdentities(ctx context.Context) (*kes.ListIter[kes.Identity], error) { 398 c.lock.RLock() 399 defer c.lock.RUnlock() 400 401 return &kes.ListIter[kes.Identity]{ 402 NextFunc: c.client.ListIdentities, 403 }, nil 404 } 405 406 // Verify verifies all KMS endpoints and returns details 407 func (c *kesClient) Verify(ctx context.Context) []VerifyResult { 408 c.lock.RLock() 409 defer c.lock.RUnlock() 410 411 results := []VerifyResult{} 412 kmsContext := Context{"MinIO admin API": "ServerInfoHandler"} // Context for a test key operation 413 for _, endpoint := range c.client.Endpoints { 414 client := kes.Client{ 415 Endpoints: []string{endpoint}, 416 HTTPClient: c.client.HTTPClient, 417 } 418 419 // 1. Get stats for the KES instance 420 state, err := client.Status(ctx) 421 if err != nil { 422 results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint}) 423 continue 424 } 425 426 // 2. Generate a new key using the KMS. 427 kmsCtx, err := kmsContext.MarshalText() 428 if err != nil { 429 results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint}) 430 continue 431 } 432 result := VerifyResult{Status: "online", Endpoint: endpoint, Version: state.Version} 433 key, err := client.GenerateKey(ctx, env.Get(EnvKESKeyName, ""), kmsCtx) 434 if err != nil { 435 result.Encrypt = fmt.Sprintf("Encryption failed: %v", err) 436 } else { 437 result.Encrypt = "success" 438 } 439 // 3. Verify that we can indeed decrypt the (encrypted) key 440 decryptedKey, err := client.Decrypt(ctx, env.Get(EnvKESKeyName, ""), key.Ciphertext, kmsCtx) 441 switch { 442 case err != nil: 443 result.Decrypt = fmt.Sprintf("Decryption failed: %v", err) 444 case subtle.ConstantTimeCompare(key.Plaintext, decryptedKey) != 1: 445 result.Decrypt = "Decryption failed: decrypted key does not match generated key" 446 default: 447 result.Decrypt = "success" 448 } 449 results = append(results, result) 450 } 451 return results 452 }