kms: encrypt IAM/config data with the KMS (#12041)

This commit changes the config/IAM encryption
process. Instead of encrypting config data
(users, policies etc.) with the root credentials
MinIO now encrypts this data with a KMS - if configured.

Therefore, this PR moves the MinIO-KMS configuration (via
env. variables) to a "top-level" configuration.
The KMS configuration cannot be stored in the config file
since it is used to decrypt the config file in the first
place.

As a consequence, this commit also removes support for
Hashicorp Vault - which has been deprecated anyway.

Signed-off-by: Andreas Auernhammer <aead@mail.de>
This commit is contained in:
Andreas Auernhammer
2021-04-22 17:45:30 +02:00
committed by Harshavardhana
parent e05e14309c
commit 3455f786fa
41 changed files with 553 additions and 2157 deletions

View File

@@ -1,3 +1,5 @@
// +build fips
// MinIO Cloud Storage, (C) 2021 MinIO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,8 +14,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//+build fips
package fips
import (
@@ -37,6 +37,6 @@ func cipherSuitesTLS() []uint16 {
}
}
func ellipticCurvesTLS() []tls.Curve {
func ellipticCurvesTLS() []tls.CurveID {
return []tls.CurveID{tls.CurveP256}
}

View File

@@ -52,7 +52,7 @@ func Parse(s string) (KMS, error) {
}
// New returns a single-key KMS that derives new DEKs from the
// given key.
// given key. The given key must always be 32 bytes.
func New(keyID string, key []byte) (KMS, error) {
if len(key) != 32 {
return nil, errors.New("kms: invalid key length " + strconv.Itoa(len(key)))

View File

@@ -1,5 +1,5 @@
/*
* MinIO Cloud Storage, (C) 2018 MinIO, Inc.
* MinIO Cloud Storage, (C) 2018-2021 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,21 +19,18 @@ package madmin
import (
"bytes"
"crypto/sha256"
"errors"
"io"
"io/ioutil"
"github.com/minio/minio/pkg/argon2"
"github.com/minio/minio/pkg/fips"
"github.com/secure-io/sio-go"
"github.com/secure-io/sio-go/sioutil"
"golang.org/x/crypto/pbkdf2"
)
var idKey func([]byte, []byte, []byte, []byte, uint32) []byte
func init() {
idKey = argon2.NewIDKey(1, 64*1024, 4)
}
// EncryptData encrypts the data with an unique key
// derived from password using the Argon2id PBKDF.
//
@@ -43,24 +40,35 @@ func init() {
func EncryptData(password string, data []byte) ([]byte, error) {
salt := sioutil.MustRandom(32)
// Derive an unique 256 bit key from the password and the random salt.
key := idKey([]byte(password), salt, nil, nil, 32)
var (
id byte
err error
stream *sio.Stream
)
if useAES() { // Only use AES-GCM if we can use an optimized implementation
id = aesGcm
if fips.Enabled() {
key := pbkdf2.Key([]byte(password), salt, pbkdf2Cost, 32, sha256.New)
stream, err = sio.AES_256_GCM.Stream(key)
if err != nil {
return nil, err
}
id = pbkdf2AESGCM
} else {
id = c20p1305
stream, err = sio.ChaCha20Poly1305.Stream(key)
}
if err != nil {
return nil, err
key := argon2.IDKey([]byte(password), salt, argon2idTime, argon2idMemory, argon2idThreads, 32)
if sioutil.NativeAES() {
stream, err = sio.AES_256_GCM.Stream(key)
if err != nil {
return nil, err
}
id = argon2idAESGCM
} else {
stream, err = sio.ChaCha20Poly1305.Stream(key)
if err != nil {
return nil, err
}
id = argon2idChaCHa20Poly1305
}
}
nonce := sioutil.MustRandom(stream.NonceSize())
// ciphertext = salt || AEAD ID | nonce | encrypted data
@@ -110,33 +118,43 @@ func DecryptData(password string, data io.Reader) ([]byte, error) {
return nil, err
}
key := idKey([]byte(password), salt[:], nil, nil, 32)
var (
err error
stream *sio.Stream
)
switch id[0] {
case aesGcm:
switch {
case id[0] == argon2idAESGCM:
key := argon2.IDKey([]byte(password), salt[:], argon2idTime, argon2idMemory, argon2idThreads, 32)
stream, err = sio.AES_256_GCM.Stream(key)
case c20p1305:
case id[0] == argon2idChaCHa20Poly1305:
key := argon2.IDKey([]byte(password), salt[:], argon2idTime, argon2idMemory, argon2idThreads, 32)
stream, err = sio.ChaCha20Poly1305.Stream(key)
case id[0] == pbkdf2AESGCM:
key := pbkdf2.Key([]byte(password), salt[:], pbkdf2Cost, 32, sha256.New)
stream, err = sio.AES_256_GCM.Stream(key)
default:
err = errors.New("madmin: invalid AEAD algorithm ID")
err = errors.New("madmin: invalid encryption algorithm ID")
}
if err != nil {
return nil, err
}
enBytes, err := ioutil.ReadAll(stream.DecryptReader(data, nonce[:], nil))
plaintext, err := ioutil.ReadAll(stream.DecryptReader(data, nonce[:], nil))
if err != nil {
if err == sio.NotAuthentic {
return enBytes, ErrMaliciousData
}
return nil, err
}
return enBytes, err
return plaintext, err
}
const (
aesGcm = 0x00
c20p1305 = 0x01
argon2idAESGCM = 0x00
argon2idChaCHa20Poly1305 = 0x01
pbkdf2AESGCM = 0x02
)
const (
argon2idTime = 1
argon2idMemory = 64 * 1024
argon2idThreads = 4
pbkdf2Cost = 8192
)

View File

@@ -1,22 +0,0 @@
// MinIO Cloud Storage, (C) 2021 MinIO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build fips
package madmin
// useAES always returns true since AES is the only
// option out of AES-GCM and ChaCha20-Poly1305 that
// is approved by the NIST.
func useAES() bool { return true }

View File

@@ -1,24 +0,0 @@
// MinIO Cloud Storage, (C) 2021 MinIO, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !fips
package madmin
import "github.com/secure-io/sio-go/sioutil"
// useAES returns true if the executing CPU provides
// AES-GCM hardware instructions and an optimized
// assembler implementation is available.
func useAES() bool { return sioutil.NativeAES() }