mirror of
https://github.com/minio/minio.git
synced 2024-12-25 06:35:56 -05:00
fix: allow parsing keys in both new and old format (#12144)
Bonus fix fallback to decrypt previously encrypted content as well using older master key ciphertext format. Signed-off-by: Harshavardhana <harsha@minio.io>
This commit is contained in:
parent
5d954ea228
commit
f420996dfa
@ -341,28 +341,37 @@ func handleCommonEnvVars() {
|
|||||||
case env.IsSet(config.EnvKMSMasterKey) && env.IsSet(config.EnvKESEndpoint):
|
case env.IsSet(config.EnvKMSMasterKey) && env.IsSet(config.EnvKESEndpoint):
|
||||||
logger.Fatal(errors.New("ambigious KMS configuration"), fmt.Sprintf("The environment contains %q as well as %q", config.EnvKMSMasterKey, config.EnvKESEndpoint))
|
logger.Fatal(errors.New("ambigious KMS configuration"), fmt.Sprintf("The environment contains %q as well as %q", config.EnvKMSMasterKey, config.EnvKESEndpoint))
|
||||||
}
|
}
|
||||||
if env.IsSet(config.EnvKMSSecretKey) {
|
|
||||||
KMS, err := kms.Parse(env.Get(config.EnvKMSSecretKey, ""))
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err, "Unable to parse the KMS secret key inherited from the shell environment")
|
|
||||||
}
|
|
||||||
GlobalKMS = KMS
|
|
||||||
} else if env.IsSet(config.EnvKMSMasterKey) {
|
|
||||||
logger.LogIf(GlobalContext, errors.New("legacy KMS configuration"), fmt.Sprintf("The environment variable %q is deprecated and will be removed in the future", config.EnvKMSMasterKey))
|
|
||||||
|
|
||||||
v := strings.SplitN(env.Get(config.EnvKMSMasterKey, ""), ":", 2)
|
parseMasterKey := func(key string) error {
|
||||||
if len(v) != 2 {
|
KMS, err := kms.Parse(env.Get(key, ""))
|
||||||
logger.Fatal(errors.New("invalid "+config.EnvKMSMasterKey), "Unable to parse the KMS secret key inherited from the shell environment")
|
|
||||||
}
|
|
||||||
secretKey, err := hex.DecodeString(v[1])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Fatal(err, "Unable to parse the KMS secret key inherited from the shell environment")
|
v := strings.SplitN(env.Get(key, ""), ":", 2)
|
||||||
}
|
if len(v) != 2 {
|
||||||
KMS, err := kms.New(v[0], secretKey)
|
return errors.New("invalid " + key)
|
||||||
if err != nil {
|
}
|
||||||
logger.Fatal(err, "Unable to parse the KMS secret key inherited from the shell environment")
|
secretKey, err := hex.DecodeString(v[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
KMS, err = kms.New(v[0], secretKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GlobalKMS = KMS
|
GlobalKMS = KMS
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if env.IsSet(config.EnvKMSSecretKey) {
|
||||||
|
if err = parseMasterKey(config.EnvKMSSecretKey); err != nil {
|
||||||
|
logger.Fatal(err, "Unable to parse the KMS secret key inherited from the shell environment")
|
||||||
|
}
|
||||||
|
} else if env.IsSet(config.EnvKMSMasterKey) {
|
||||||
|
logger.LogIf(GlobalContext, errors.New("legacy KMS configuration"),
|
||||||
|
fmt.Sprintf("The environment variable %q is deprecated and will be removed in the future", config.EnvKMSMasterKey))
|
||||||
|
if err = parseMasterKey(config.EnvKMSMasterKey); err != nil {
|
||||||
|
logger.Fatal(err, "Unable to parse the KMS secret key inherited from the shell environment")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if env.IsSet(config.EnvKESEndpoint) {
|
if env.IsSet(config.EnvKESEndpoint) {
|
||||||
kesEndpoints, err := crypto.ParseKESEndpoints(env.Get(config.EnvKESEndpoint, ""))
|
kesEndpoints, err := crypto.ParseKESEndpoints(env.Get(config.EnvKESEndpoint, ""))
|
||||||
|
61
docs/kms/IAM.md
Normal file
61
docs/kms/IAM.md
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# KMS IAM/Config Encryption
|
||||||
|
|
||||||
|
MinIO will soon release a change that re-works the encryption of IAM and
|
||||||
|
configuration data. Currently, MinIO encrypts IAM data (user/temp. credentials,
|
||||||
|
policies and other configuration data) with the cluster root credentials before
|
||||||
|
storing it on the backend disks. After release `RELEASE.2021-04-22T15-44-28Z`
|
||||||
|
onwards, MinIO will use the KMS provided keys to encrypt the IAM data instead
|
||||||
|
of the cluster root credentials. If the KMS is not enabled, MinIO will store
|
||||||
|
the IAM data as plain text in its backend.
|
||||||
|
|
||||||
|
### FAQ
|
||||||
|
|
||||||
|
> Why is this change needed? Was the previous encryption not secure (enough)?
|
||||||
|
|
||||||
|
The previous mechanism of encryption using the cluster root credentials itself
|
||||||
|
is secure. The purpose of this change is to improve the encryption key
|
||||||
|
management by delegating it to the KMS. Once the cluster root credentials are no
|
||||||
|
longer needed to decrypt persistent data at the backend, they can be rotated
|
||||||
|
much more easily. By using a KMS to protect the IAM data a potential security or
|
||||||
|
compliance team has much more control over how/when a MinIO cluster can be
|
||||||
|
started.
|
||||||
|
|
||||||
|
> Does this mean I need an enterprise KMS setup to run MinIO (securely)?
|
||||||
|
|
||||||
|
No, MinIO does not depend on any third-party KMS provider. You have three options here:
|
||||||
|
|
||||||
|
- Run MinIO without a KMS. In this case all IAM data will be stored in plain-text.
|
||||||
|
|
||||||
|
- Run MinIO with a single secret key. MinIO supports a static cryptographic key
|
||||||
|
that can act as minimal KMS. With this method all IAM data will be stored
|
||||||
|
encrypted. The encryption key has to be passed as environment variable.
|
||||||
|
|
||||||
|
- Run MinIO with KES (minio/kes) in combination with any supported KMS as
|
||||||
|
secure key store. For example, you can run MinIO + KES + Hashicorp Vault.
|
||||||
|
|
||||||
|
> What about an exiting MinIO deployment? Can I just upgrade my cluster?
|
||||||
|
|
||||||
|
Yes, MinIO will try to transparently migrate any existing IAM data and either stores
|
||||||
|
it in plaintext (no KMS) or re-encrypts using the KMS.
|
||||||
|
|
||||||
|
> Is this change backward compatible? Will it break my setup?
|
||||||
|
|
||||||
|
This change is not backward compatible for all setups. In particular, the native
|
||||||
|
Hashicorp Vault integration - which has been deprecated already - won't be
|
||||||
|
supported anymore. KES is now mandatory if a third-party KMS should be used.
|
||||||
|
|
||||||
|
Further, since the configuration data is encrypted with the KMS, the KMS
|
||||||
|
configuration itself can no longer be stored in the MinIO config file and
|
||||||
|
instead must be provided via environment variables. If you have set your KMS
|
||||||
|
configuration using e.g. the `mc admin config` commands you will need to adjust
|
||||||
|
your deployment.
|
||||||
|
|
||||||
|
Even though this change is backward compatible we do not expect that it affects
|
||||||
|
the vast majority of deployments in any negative way.
|
||||||
|
|
||||||
|
> Will an upgrade of an existing MinIO cluster impact the SLA of the cluster or will it even cause downtime?
|
||||||
|
|
||||||
|
No, an upgrade should not cause any downtime. However, on the first startup -
|
||||||
|
since MinIO will attempt to migrate any existing IAM data - the boot process may
|
||||||
|
take slightly longer, but may not be visibly noticeable. Once the migration has
|
||||||
|
completed, any subsequent restart should be as fast as before or even faster.
|
@ -28,7 +28,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/minio/sio"
|
||||||
"github.com/secure-io/sio-go/sioutil"
|
"github.com/secure-io/sio-go/sioutil"
|
||||||
"golang.org/x/crypto/chacha20"
|
"golang.org/x/crypto/chacha20"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
@ -55,7 +57,7 @@ func Parse(s string) (KMS, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New returns a single-key KMS that derives new DEKs from the
|
// New returns a single-key KMS that derives new DEKs from the
|
||||||
// given key. The given key must always be 32 bytes.
|
// given key.
|
||||||
func New(keyID string, key []byte) (KMS, error) {
|
func New(keyID string, key []byte) (KMS, error) {
|
||||||
if len(key) != 32 {
|
if len(key) != 32 {
|
||||||
return nil, errors.New("kms: invalid key length " + strconv.Itoa(len(key)))
|
return nil, errors.New("kms: invalid key length " + strconv.Itoa(len(key)))
|
||||||
@ -168,11 +170,39 @@ func (kms secretKey) GenerateKey(keyID string, context Context) (DEK, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kms secretKey) legacyDecryptKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) {
|
||||||
|
var derivedKey = kms.deriveKey(keyID, ctx)
|
||||||
|
|
||||||
|
var key [32]byte
|
||||||
|
out, err := sio.DecryptBuffer(key[:0], sealedKey, sio.Config{Key: derivedKey[:]})
|
||||||
|
if err != nil || len(out) != 32 {
|
||||||
|
return nil, err // TODO(aead): upgrade sio to use sio.Error
|
||||||
|
}
|
||||||
|
return key[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kms secretKey) deriveKey(keyID string, context Context) (key [32]byte) {
|
||||||
|
if context == nil {
|
||||||
|
context = Context{}
|
||||||
|
}
|
||||||
|
ctxBytes, _ := context.MarshalText()
|
||||||
|
|
||||||
|
mac := hmac.New(sha256.New, kms.key[:])
|
||||||
|
mac.Write([]byte(keyID))
|
||||||
|
mac.Write(ctxBytes)
|
||||||
|
mac.Sum(key[:0])
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
func (kms secretKey) DecryptKey(keyID string, ciphertext []byte, context Context) ([]byte, error) {
|
func (kms secretKey) DecryptKey(keyID string, ciphertext []byte, context Context) ([]byte, error) {
|
||||||
if keyID != kms.keyID {
|
if keyID != kms.keyID {
|
||||||
return nil, fmt.Errorf("kms: key %q does not exist", keyID)
|
return nil, fmt.Errorf("kms: key %q does not exist", keyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !utf8.Valid(ciphertext) {
|
||||||
|
return kms.legacyDecryptKey(keyID, ciphertext, context)
|
||||||
|
}
|
||||||
|
|
||||||
var encryptedKey encryptedKey
|
var encryptedKey encryptedKey
|
||||||
if err := json.Unmarshal(ciphertext, &encryptedKey); err != nil {
|
if err := json.Unmarshal(ciphertext, &encryptedKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
Loading…
Reference in New Issue
Block a user