Skip to content

Commit

Permalink
Keep an up-to-date copy of the KMS master key
Browse files Browse the repository at this point in the history
Validate KMS master key using a test of encrypting decryption, in order to startup MinIO
  • Loading branch information
allanrogerr committed Apr 12, 2024
1 parent f7ed9a7 commit b915814
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 13 deletions.
21 changes: 16 additions & 5 deletions cmd/common-main.go
Original file line number Diff line number Diff line change
Expand Up @@ -962,12 +962,23 @@ func handleKMSConfig() {
if err != nil {
logger.Fatal(err, "Unable to initialize a connection to KES as specified by the shell environment")
}
// We check that the default key ID exists or try to create it otherwise.
// This implicitly checks that we can communicate to KES. We don't treat
// a policy error as failure condition since MinIO may not have the permission
// Try to generate/decrypt a data encryption key. Only try to create key if this fails.
// This implicitly checks that we can communicate to KES.
// We don't treat a policy error as failure condition since MinIO may not have the permission
// to create keys - just to generate/decrypt data encryption keys.
if err = KMS.CreateKey(context.Background(), env.Get(kms.EnvKESKeyName, "")); err != nil && !errors.Is(err, kes.ErrKeyExists) && !errors.Is(err, kes.ErrNotAllowed) {
logger.Fatal(err, "Unable to initialize a connection to KES as specified by the shell environment")
ctx := context.WithValue(context.Background(), "ValidateKeyOnly", "true")

Check failure on line 969 in cmd/common-main.go

View workflow job for this annotation

GitHub Actions / Go 1.21.x on ubuntu-latest

context-keys-type: should not use basic type string as key in context.WithValue (revive)
results := KMS.Verify(ctx)
validKey := false
for _, result := range results {
if validKey == false && result.Status == "online" && result.Decrypt == "success" && result.Encrypt == "success" {
validKey = true
break
}
}
if !validKey {
if err = KMS.CreateKey(context.Background(), env.Get(kms.EnvKESKeyName, "")); err != nil && !errors.Is(err, kes.ErrKeyExists) && !errors.Is(err, kes.ErrNotAllowed) {
logger.Fatal(err, "Unable to initialize a connection to KES as specified by the shell environment")
}
}
GlobalKMS = KMS
}
Expand Down
46 changes: 38 additions & 8 deletions internal/kms/kes.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ import (
"crypto/x509"
"errors"
"fmt"
"strings"
"sync"

"github.com/minio/kms-go/kes"
"github.com/minio/pkg/v2/certs"
"github.com/minio/pkg/v2/env"
"strings"
"sync"
"time"
)

const (
Expand Down Expand Up @@ -138,9 +138,33 @@ func NewWithConfig(config Config) (KMS, error) {
}
}
}()

go func() {
c.keepKeyInCache()
}()
return c, nil
}

// Request KES keep an up-to-date copy of the KMS master key to allow minio to start up even if KMS is down. The
// cached key may still be evicted if the period of this function is longer than that of KES .cache.expiry.unused
func (c *kesClient) keepKeyInCache() {
ctx := context.WithValue(context.Background(), "ValidateKeyOnly", "true")

Check failure on line 151 in internal/kms/kes.go

View workflow job for this annotation

GitHub Actions / Go 1.21.x on ubuntu-latest

context-keys-type: should not use basic type string as key in context.WithValue (revive)

ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
case <-ctx.Done():
return
}
results := c.Verify(ctx)
for _, result := range results {
fmt.Println(result.Endpoint, result.Status, result.Version, result.Decrypt, result.Encrypt)
}
}
}

type kesClient struct {
lock sync.RWMutex
defaultKeyID string
Expand Down Expand Up @@ -416,17 +440,23 @@ func (c *kesClient) Verify(ctx context.Context) []VerifyResult {
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
state := kes.State{}
// Permission on /status is not required to validate kes key encryption and decryption
if ctx.Value("ValidateKeyOnly") != "true" {
// 1. Get stats for the KES instance
clientState, err := client.Status(ctx)
if err != nil {
results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint})
continue
}
state = clientState
}

// 2. Generate a new key using the KMS.
kmsCtx, err := kmsContext.MarshalText()
if err != nil {
results = append(results, VerifyResult{Status: "offline", Endpoint: endpoint})
fmt.Println(err)
continue
}
result := VerifyResult{Status: "online", Endpoint: endpoint, Version: state.Version}
Expand Down

0 comments on commit b915814

Please sign in to comment.