From 9b9871cfbbfbb3d8e9f2184c2b896831c6c3aa45 Mon Sep 17 00:00:00 2001 From: Shubhendu Date: Thu, 13 Jul 2023 12:20:38 +0530 Subject: [PATCH] Added `endpoint` and `versions` attributes to KMS details (#17350) Now it would list details of all KMS instances with additional attributes `endpoint` and `version`. In the case of k8s-based deployment the list would consist of a single entry. Signed-off-by: Shubhendu Ram Tripathi --- cmd/admin-handlers.go | 27 +++++++++++++++++--- go.mod | 2 +- go.sum | 4 +-- internal/kms/kes.go | 50 ++++++++++++++++++++++++++++++++++++++ internal/kms/kms.go | 12 +++++++++ internal/kms/single-key.go | 7 ++++++ 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 5594c266b..739f77fa5 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1868,8 +1868,6 @@ func getPoolsInfo(ctx context.Context, allDisks []madmin.Disk) (map[int]map[int] } func getServerInfo(ctx context.Context, poolsInfoEnabled bool, r *http.Request) madmin.InfoMessage { - kmsStat := fetchKMSStatus() - ldap := madmin.LDAP{} if globalIAMSys.LDAPConfig.Enabled() { ldapConn, err := globalIAMSys.LDAPConfig.LDAP.Connect() @@ -1949,7 +1947,8 @@ func getServerInfo(ctx context.Context, poolsInfoEnabled bool, r *http.Request) domain := globalDomainNames services := madmin.Services{ - KMS: kmsStat, + KMS: fetchKMSStatus(), + KMSStatus: fetchKMSStatusV2(ctx), LDAP: ldap, Logger: log, Audit: audit, @@ -2569,6 +2568,28 @@ func fetchKMSStatus() madmin.KMS { return kmsStat } +// fetchKMSStatusV2 fetches KMS-related status information for all instances +func fetchKMSStatusV2(ctx context.Context) []madmin.KMS { + if GlobalKMS == nil { + return []madmin.KMS{} + } + + results := GlobalKMS.Verify(ctx) + + stats := []madmin.KMS{} + for _, result := range results { + stats = append(stats, madmin.KMS{ + Status: result.Status, + Endpoint: result.Endpoint, + Encrypt: result.Encrypt, + Decrypt: result.Decrypt, + Version: result.Version, + }) + } + + return stats +} + // fetchLoggerDetails return log info func fetchLoggerInfo() ([]madmin.Logger, []madmin.Audit) { var loggerInfo []madmin.Logger diff --git a/go.mod b/go.mod index 525809671..b05ff7c7c 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( github.com/minio/dperf v0.5.0 github.com/minio/highwayhash v1.0.2 github.com/minio/kes-go v0.1.0 - github.com/minio/madmin-go/v3 v3.0.5 + github.com/minio/madmin-go/v3 v3.0.6 github.com/minio/minio-go/v7 v7.0.59 github.com/minio/mux v1.9.0 github.com/minio/pkg v1.7.5 diff --git a/go.sum b/go.sum index 5908890af..083e9ea11 100644 --- a/go.sum +++ b/go.sum @@ -483,8 +483,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/kes-go v0.1.0 h1:h201DyOYP5sTqajkxFGxmXz/kPbT8HQNX1uh3Yx2PFc= github.com/minio/kes-go v0.1.0/go.mod h1:VorHLaIYis9/MxAHAtXN4d8PUMNKhIxTIlvFt0hBOEo= -github.com/minio/madmin-go/v3 v3.0.5 h1:ynWTsnszHnQVJWRL2OE4ysCvCNG0uHgdTvJpdLazf9c= -github.com/minio/madmin-go/v3 v3.0.5/go.mod h1:lPrMoc1aeiIWmmrxBthkDqzMPQwC/Lu9ByuyM2wenJk= +github.com/minio/madmin-go/v3 v3.0.6 h1:rlU0UCwRhi/bI5R9Pg5df88ddqFNFA5mpmxScAanQCA= +github.com/minio/madmin-go/v3 v3.0.6/go.mod h1:lPrMoc1aeiIWmmrxBthkDqzMPQwC/Lu9ByuyM2wenJk= github.com/minio/mc v0.0.0-20230706154612-72958227ad65 h1:27INveRWSp7yAEy4szNp15DOA2dyOwnxTGt/p0JuTh4= github.com/minio/mc v0.0.0-20230706154612-72958227ad65/go.mod h1:41ndsUBIAA/dRjOQ/0KY4d8vI70gDiKeMo1zusOQRWk= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= diff --git a/internal/kms/kes.go b/internal/kms/kes.go index f13e14c78..21107467f 100644 --- a/internal/kms/kes.go +++ b/internal/kms/kes.go @@ -20,9 +20,11 @@ package kms import ( "bytes" "context" + "crypto/subtle" "crypto/tls" "crypto/x509" "errors" + "fmt" "strings" "sync" @@ -467,3 +469,51 @@ func (c *kesClient) ListIdentities(ctx context.Context, pattern string) (*kes.Id return c.enclave.ListIdentities(ctx, pattern) } + +// Verify verifies all KMS endpoints and returns details +func (c *kesClient) Verify(ctx context.Context) []VerifyResult { + c.lock.RLock() + defer c.lock.RUnlock() + + results := []VerifyResult{} + kmsContext := Context{"MinIO admin API": "ServerInfoHandler"} // Context for a test key operation + for _, endpoint := range c.client.Endpoints { + client := kes.Client{ + Endpoints: []string{endpoint}, + HTTPClient: c.client.HTTPClient, + } + + // 1. Get stats for the KES instance + state, err := client.Status(ctx) + if err != nil { + results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint}) + continue + } + + // 2. Generate a new key using the KMS. + kmsCtx, err := kmsContext.MarshalText() + if err != nil { + results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint}) + continue + } + result := VerifyResult{Status: "online", Endpoint: endpoint, Version: state.Version} + key, err := client.GenerateKey(ctx, env.Get(EnvKESKeyName, ""), kmsCtx) + if err != nil { + result.Encrypt = fmt.Sprintf("Encryption failed: %v", err) + } else { + result.Encrypt = "success" + } + // 3. Verify that we can indeed decrypt the (encrypted) key + decryptedKey, err := client.Decrypt(ctx, env.Get(EnvKESKeyName, ""), key.Ciphertext, kmsCtx) + switch { + case err != nil: + result.Decrypt = fmt.Sprintf("Decryption failed: %v", err) + case subtle.ConstantTimeCompare(key.Plaintext, decryptedKey) != 1: + result.Decrypt = "Decryption failed: decrypted key does not match generated key" + default: + result.Decrypt = "success" + } + results = append(results, result) + } + return results +} diff --git a/internal/kms/kms.go b/internal/kms/kms.go index e0cce75f4..f2b9ec24b 100644 --- a/internal/kms/kms.go +++ b/internal/kms/kms.go @@ -67,6 +67,18 @@ type KMS interface { // by the key ID. The contexts must match the context value // used to generate the ciphertexts. DecryptAll(ctx context.Context, keyID string, ciphertext [][]byte, context []Context) ([][]byte, error) + + // Verify verifies all KMS endpoints and returns the details + Verify(cxt context.Context) []VerifyResult +} + +// VerifyResult describes the verification result details a KMS endpoint +type VerifyResult struct { + Endpoint string + Decrypt string + Encrypt string + Version string + Status string } // Status describes the current state of a KMS. diff --git a/internal/kms/single-key.go b/internal/kms/single-key.go index abe773e71..bfb29b34d 100644 --- a/internal/kms/single-key.go +++ b/internal/kms/single-key.go @@ -303,6 +303,13 @@ func (kms secretKey) DecryptAll(_ context.Context, keyID string, ciphertexts [][ return plaintexts, nil } +// Verify verifies all KMS endpoints and returns details +func (kms secretKey) Verify(cxt context.Context) []VerifyResult { + return []VerifyResult{ + {Endpoint: "self"}, + } +} + type encryptedKey struct { Algorithm string `json:"aead"` IV []byte `json:"iv"`