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

@@ -15,12 +15,7 @@
package crypto
import (
"errors"
"math/rand"
"net/http"
"os"
"reflect"
"strconv"
"strings"
"github.com/minio/minio/cmd/config"
@@ -29,101 +24,7 @@ import (
xnet "github.com/minio/minio/pkg/net"
)
// KMSConfig has the KMS config for hashicorp vault
type KMSConfig struct {
AutoEncryption bool `json:"-"`
Vault VaultConfig `json:"vault"`
Kes KesConfig `json:"kes"`
}
// KMS Vault constants.
const (
KMSVaultEndpoint = "endpoint"
KMSVaultCAPath = "capath"
KMSVaultKeyName = "key_name"
KMSVaultKeyVersion = "key_version"
KMSVaultNamespace = "namespace"
KMSVaultAuthType = "auth_type"
KMSVaultAppRoleID = "auth_approle_id"
KMSVaultAppRoleSecret = "auth_approle_secret"
)
// KMS kes constants.
const (
KMSKesEndpoint = "endpoint"
KMSKesKeyFile = "key_file"
KMSKesCertFile = "cert_file"
KMSKesCAPath = "capath"
KMSKesKeyName = "key_name"
)
// DefaultKVS - default KV crypto config
var (
DefaultVaultKVS = config.KVS{
config.KV{
Key: KMSVaultEndpoint,
Value: "",
},
config.KV{
Key: KMSVaultKeyName,
Value: "",
},
config.KV{
Key: KMSVaultAuthType,
Value: "approle",
},
config.KV{
Key: KMSVaultAppRoleID,
Value: "",
},
config.KV{
Key: KMSVaultAppRoleSecret,
Value: "",
},
config.KV{
Key: KMSVaultCAPath,
Value: "",
},
config.KV{
Key: KMSVaultKeyVersion,
Value: "",
},
config.KV{
Key: KMSVaultNamespace,
Value: "",
},
}
DefaultKesKVS = config.KVS{
config.KV{
Key: KMSKesEndpoint,
Value: "",
},
config.KV{
Key: KMSKesKeyName,
Value: "",
},
config.KV{
Key: KMSKesCertFile,
Value: "",
},
config.KV{
Key: KMSKesKeyFile,
Value: "",
},
config.KV{
Key: KMSKesCAPath,
Value: "",
},
}
)
const (
// EnvKMSMasterKey is the environment variable used to specify
// a KMS master key used to protect SSE-S3 per-object keys.
// Valid values must be of the from: "KEY_ID:32_BYTE_HEX_VALUE".
EnvKMSMasterKey = "MINIO_KMS_MASTER_KEY"
// EnvKMSAutoEncryption is the environment variable used to en/disable
// SSE-S3 auto-encryption. SSE-S3 auto-encryption, if enabled,
// requires a valid KMS configuration and turns any non-SSE-C
@@ -132,301 +33,49 @@ const (
EnvKMSAutoEncryption = "MINIO_KMS_AUTO_ENCRYPTION"
)
const (
// EnvKMSVaultEndpoint is the environment variable used to specify
// the vault HTTPS endpoint.
EnvKMSVaultEndpoint = "MINIO_KMS_VAULT_ENDPOINT"
// EnvKMSVaultAuthType is the environment variable used to specify
// the authentication type for vault.
EnvKMSVaultAuthType = "MINIO_KMS_VAULT_AUTH_TYPE"
// EnvKMSVaultAppRoleID is the environment variable used to specify
// the vault AppRole ID.
EnvKMSVaultAppRoleID = "MINIO_KMS_VAULT_APPROLE_ID"
// EnvKMSVaultAppSecretID is the environment variable used to specify
// the vault AppRole secret corresponding to the AppRole ID.
EnvKMSVaultAppSecretID = "MINIO_KMS_VAULT_APPROLE_SECRET"
// EnvKMSVaultKeyVersion is the environment variable used to specify
// the vault key version.
EnvKMSVaultKeyVersion = "MINIO_KMS_VAULT_KEY_VERSION"
// EnvKMSVaultKeyName is the environment variable used to specify
// the vault named key-ring. In the S3 context it's referred as
// customer master key ID (CMK-ID).
EnvKMSVaultKeyName = "MINIO_KMS_VAULT_KEY_NAME"
// EnvKMSVaultCAPath is the environment variable used to specify the
// path to a directory of PEM-encoded CA cert files. These CA cert
// files are used to authenticate MinIO to Vault over mTLS.
EnvKMSVaultCAPath = "MINIO_KMS_VAULT_CAPATH"
// EnvKMSVaultNamespace is the environment variable used to specify
// vault namespace. The vault namespace is used if the enterprise
// version of Hashicorp Vault is used.
EnvKMSVaultNamespace = "MINIO_KMS_VAULT_NAMESPACE"
)
const (
// EnvKMSKesEndpoint is the environment variable used to specify
// one or multiple KES server HTTPS endpoints. The individual
// endpoints should be separated by ','.
EnvKMSKesEndpoint = "MINIO_KMS_KES_ENDPOINT"
// EnvKMSKesKeyFile is the environment variable used to specify
// the TLS private key used by MinIO to authenticate to the kes
// server HTTPS via mTLS.
EnvKMSKesKeyFile = "MINIO_KMS_KES_KEY_FILE"
// EnvKMSKesCertFile is the environment variable used to specify
// the TLS certificate used by MinIO to authenticate to the kes
// server HTTPS via mTLS.
EnvKMSKesCertFile = "MINIO_KMS_KES_CERT_FILE"
// EnvKMSKesCAPath is the environment variable used to specify
// the TLS root certificates used by MinIO to verify the certificate
// presented by to the kes server when establishing a TLS connection.
EnvKMSKesCAPath = "MINIO_KMS_KES_CA_PATH"
// EnvKMSKesKeyName is the environment variable used to specify
// the (default) key at the kes server. In the S3 context it's
// referred as customer master key ID (CMK-ID).
EnvKMSKesKeyName = "MINIO_KMS_KES_KEY_NAME"
)
var defaultVaultCfg = VaultConfig{
Auth: VaultAuth{
Type: "approle",
},
}
var defaultKesCfg = KesConfig{}
// EnabledVault returns true if HashiCorp Vault is enabled.
func EnabledVault(kvs config.KVS) bool {
endpoint := kvs.Get(KMSVaultEndpoint)
return endpoint != ""
}
// EnabledKes returns true if kes as KMS is enabled.
func EnabledKes(kvs config.KVS) bool {
endpoint := kvs.Get(KMSKesEndpoint)
return endpoint != ""
}
// LookupKesConfig lookup kes server configuration.
func LookupKesConfig(kvs config.KVS) (KesConfig, error) {
kesCfg := KesConfig{}
endpointStr := env.Get(EnvKMSKesEndpoint, kvs.Get(KMSKesEndpoint))
var endpoints []string
// ParseKESEndpoints parses the given endpoint string and
// returns a list of valid endpoint URLs. The order of the
// returned endpoints is randomized.
func ParseKESEndpoints(endpointStr string) ([]string, error) {
var rawEndpoints []string
for _, endpoint := range strings.Split(endpointStr, ",") {
if strings.TrimSpace(endpoint) == "" {
continue
}
if !ellipses.HasEllipses(endpoint) {
endpoints = append(endpoints, endpoint)
rawEndpoints = append(rawEndpoints, endpoint)
continue
}
pattern, err := ellipses.FindEllipsesPatterns(endpoint)
if err != nil {
return kesCfg, err
return nil, Errorf("Invalid KES endpoint %q: %v", endpointStr, err)
}
for _, p := range pattern {
endpoints = append(endpoints, p.Expand()...)
rawEndpoints = append(rawEndpoints, p.Expand()...)
}
}
if len(endpoints) == 0 {
return kesCfg, nil
if len(rawEndpoints) == 0 {
return nil, Errorf("Invalid KES endpoint %q", endpointStr)
}
randNum := rand.Intn(len(endpoints) + 1) // We add 1 b/c len(endpoints) may be 0: See: rand.Intn docs
kesCfg.Endpoint = make([]string, len(endpoints))
for i, endpoint := range endpoints {
var (
randNum = rand.Intn(len(rawEndpoints))
endpoints = make([]string, len(rawEndpoints))
)
for i, endpoint := range rawEndpoints {
endpoint, err := xnet.ParseHTTPURL(endpoint)
if err != nil {
return kesCfg, err
return nil, Errorf("Invalid KES endpoint %q: %v", endpointStr, err)
}
kesCfg.Endpoint[(randNum+i)%len(endpoints)] = endpoint.String()
endpoints[(randNum+i)%len(rawEndpoints)] = endpoint.String()
}
kesCfg.KeyFile = env.Get(EnvKMSKesKeyFile, kvs.Get(KMSKesKeyFile))
kesCfg.CertFile = env.Get(EnvKMSKesCertFile, kvs.Get(KMSKesCertFile))
kesCfg.CAPath = env.Get(EnvKMSKesCAPath, kvs.Get(KMSKesCAPath))
kesCfg.DefaultKeyID = env.Get(EnvKMSKesKeyName, kvs.Get(KMSKesKeyName))
if reflect.DeepEqual(kesCfg, defaultKesCfg) {
return kesCfg, nil
}
// Verify all the proper settings.
if err := kesCfg.Verify(); err != nil {
return kesCfg, err
}
kesCfg.Enabled = true
return kesCfg, nil
return endpoints, nil
}
func lookupAutoEncryption() (bool, error) {
autoBool, err := config.ParseBool(env.Get(EnvAutoEncryptionLegacy, config.EnableOff))
if err != nil {
return false, err
}
if !autoBool {
autoBool, err = config.ParseBool(env.Get(EnvKMSAutoEncryption, config.EnableOff))
if err != nil {
return false, err
}
}
return autoBool, nil
}
// LookupConfig lookup vault or kes config, returns KMSConfig
// to configure KMS object for object encryption
func LookupConfig(c config.Config, defaultRootCAsDir string, transport *http.Transport) (KMSConfig, error) {
vcfg, err := LookupVaultConfig(c[config.KmsVaultSubSys][config.Default])
if err != nil {
return KMSConfig{}, err
}
kesCfg, err := LookupKesConfig(c[config.KmsKesSubSys][config.Default])
if err != nil {
return KMSConfig{}, err
}
kesCfg.Transport = transport
if kesCfg.Enabled && kesCfg.CAPath == "" {
kesCfg.CAPath = defaultRootCAsDir
}
autoEncrypt, err := lookupAutoEncryption()
if err != nil {
return KMSConfig{}, err
}
kmsCfg := KMSConfig{
AutoEncryption: autoEncrypt,
Vault: vcfg,
Kes: kesCfg,
}
return kmsCfg, nil
}
// LookupVaultConfig extracts the KMS configuration provided by environment
// variables and merge them with the provided KMS configuration. The
// merging follows the following rules:
//
// 1. A valid value provided as environment variable is higher prioritized
// than the provided configuration and overwrites the value from the
// configuration file.
//
// 2. A value specified as environment variable never changes the configuration
// file. So it is never made a persistent setting.
//
// It sets the global KMS configuration according to the merged configuration
// on succes.
func LookupVaultConfig(kvs config.KVS) (VaultConfig, error) {
if err := config.CheckValidKeys(config.KmsVaultSubSys, kvs, DefaultVaultKVS); err != nil {
return VaultConfig{}, err
}
vcfg, err := lookupConfigLegacy(kvs)
if err != nil {
return vcfg, err
}
if vcfg.Enabled {
return vcfg, nil
}
vcfg = VaultConfig{
Auth: VaultAuth{
Type: "approle",
},
}
endpointStr := env.Get(EnvKMSVaultEndpoint, kvs.Get(KMSVaultEndpoint))
if endpointStr != "" {
// Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present
endpoint, err := xnet.ParseHTTPURL(endpointStr)
if err != nil {
return vcfg, err
}
endpointStr = endpoint.String()
}
vcfg.Endpoint = endpointStr
vcfg.CAPath = env.Get(EnvKMSVaultCAPath, kvs.Get(KMSVaultCAPath))
vcfg.Auth.Type = env.Get(EnvKMSVaultAuthType, kvs.Get(KMSVaultAuthType))
if vcfg.Auth.Type == "" {
vcfg.Auth.Type = "approle"
}
vcfg.Auth.AppRole.ID = env.Get(EnvKMSVaultAppRoleID, kvs.Get(KMSVaultAppRoleID))
vcfg.Auth.AppRole.Secret = env.Get(EnvKMSVaultAppSecretID, kvs.Get(KMSVaultAppRoleSecret))
vcfg.Key.Name = env.Get(EnvKMSVaultKeyName, kvs.Get(KMSVaultKeyName))
vcfg.Namespace = env.Get(EnvKMSVaultNamespace, kvs.Get(KMSVaultNamespace))
if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" {
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
if err != nil {
return vcfg, Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
}
}
if reflect.DeepEqual(vcfg, defaultVaultCfg) {
return vcfg, nil
}
// Verify all the proper settings.
if err = vcfg.Verify(); err != nil {
return vcfg, err
}
vcfg.Enabled = true
return vcfg, nil
}
// NewKMS - initialize a new KMS.
func NewKMS(cfg KMSConfig) (kms KMS, err error) {
// Lookup KMS master kes - only available through ENV.
if masterKeyLegacy := env.Get(EnvKMSMasterKeyLegacy, ""); len(masterKeyLegacy) != 0 {
if cfg.Vault.Enabled { // Vault and KMS master key provided
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
}
if cfg.Kes.Enabled {
return kms, errors.New("Ambiguous KMS configuration: kes configuration and a master key are provided at the same time")
}
kms, err = ParseMasterKey(masterKeyLegacy)
if err != nil {
return kms, err
}
} else if masterKey := env.Get(EnvKMSMasterKey, ""); len(masterKey) != 0 {
if cfg.Vault.Enabled { // Vault and KMS master key provided
return kms, errors.New("Ambiguous KMS configuration: vault configuration and a master key are provided at the same time")
}
if cfg.Kes.Enabled {
return kms, errors.New("Ambiguous KMS configuration: kes configuration and a master key are provided at the same time")
}
kms, err = ParseMasterKey(masterKey)
if err != nil {
return kms, err
}
} else if cfg.Vault.Enabled && cfg.Kes.Enabled {
return kms, errors.New("Ambiguous KMS configuration: vault configuration and kes configuration are provided at the same time")
} else if cfg.Vault.Enabled {
if v, ok := os.LookupEnv("MINIO_KMS_VAULT_DEPRECATION"); !ok || v != "off" { // TODO(aead): Remove once Vault support has been removed
return kms, errors.New("Hashicorp Vault is deprecated and will be removed Oct. 2021. To temporarily enable Hashicorp Vault support, set MINIO_KMS_VAULT_DEPRECATION=off")
}
kms, err = NewVault(cfg.Vault)
if err != nil {
return kms, err
}
} else if cfg.Kes.Enabled {
kms, err = NewKes(cfg.Kes)
if err != nil {
return kms, err
}
}
if cfg.AutoEncryption && kms == nil {
return kms, errors.New("Invalid KMS configuration: auto-encryption is enabled but no valid KMS configuration is present")
}
return kms, nil
// LookupAutoEncryption returns true if and only if
// the MINIO_KMS_AUTO_ENCRYPTION env. variable is
// set to "on".
func LookupAutoEncryption() bool {
auto, _ := config.ParseBool(env.Get(EnvKMSAutoEncryption, config.EnableOff))
return auto
}

View File

@@ -15,95 +15,3 @@
*/
package crypto
import "github.com/minio/minio/cmd/config"
// Help template for KMS vault
var (
HelpVault = config.HelpKVS{
config.HelpKV{
Key: KMSVaultEndpoint,
Description: `API endpoint e.g. "http://vault-endpoint-ip:8200"`,
Type: "url",
},
config.HelpKV{
Key: KMSVaultKeyName,
Description: `unique transit key name - e.g. "my-minio-key"`,
Type: "string",
},
config.HelpKV{
Key: KMSVaultAuthType,
Description: `supported auth type(s) ["approle"], defaults to "approle"`,
Type: "string",
},
config.HelpKV{
Key: KMSVaultAppRoleID,
Description: `unique role ID for approle`,
Type: "string",
},
config.HelpKV{
Key: KMSVaultAppRoleSecret,
Description: `unique secret ID for approle`,
Type: "string",
},
config.HelpKV{
Key: KMSVaultNamespace,
Description: `optional KMS namespace e.g. "customer1"`,
Optional: true,
Type: "string",
},
config.HelpKV{
Key: KMSVaultKeyVersion,
Description: `optional key version number`,
Optional: true,
Type: "number",
},
config.HelpKV{
Key: KMSVaultCAPath,
Description: `optional path to PEM-encoded CA certs e.g. "/home/user/custom-certs"`,
Optional: true,
Type: "path",
},
config.HelpKV{
Key: config.Comment,
Description: config.DefaultComment,
Optional: true,
Type: "sentence",
},
}
HelpKes = config.HelpKVS{
config.HelpKV{
Key: KMSKesEndpoint,
Description: `API endpoint - e.g. "https://kes-endpoint:7373"`,
Type: "url",
},
config.HelpKV{
Key: KMSKesKeyName,
Description: `unique key name - e.g. "my-minio-key"`,
Type: "string",
},
config.HelpKV{
Key: KMSKesCertFile,
Description: `path to client certificate for TLS auth - e.g. /etc/keys/public.crt`,
Type: "path",
},
config.HelpKV{
Key: KMSKesKeyFile,
Description: `path to client private key for TLS auth - e.g. /etc/keys/private.key`,
Type: "path",
},
config.HelpKV{
Key: KMSKesCAPath,
Description: `path to PEM-encoded cert(s) to verify kes server cert - e.g. /etc/keys/CAs`,
Optional: true,
Type: "path",
},
config.HelpKV{
Key: config.Comment,
Description: config.DefaultComment,
Optional: true,
Type: "sentence",
},
}
)

View File

@@ -41,6 +41,9 @@ func GenerateKey(extKey []byte, random io.Reader) (key ObjectKey) {
if random == nil {
random = rand.Reader
}
if len(extKey) != 32 { // safety check
logger.CriticalIf(context.Background(), errors.New("crypto: invalid key length"))
}
var nonce [32]byte
if _, err := io.ReadFull(random, nonce[:]); err != nil {
logger.CriticalIf(context.Background(), errOutOfEntropy)
@@ -77,6 +80,9 @@ type SealedKey struct {
// key is also cryptographically bound to the object's path (bucket/object) and the
// domain (SSE-C or SSE-S3).
func (key ObjectKey) Seal(extKey []byte, iv [32]byte, domain, bucket, object string) SealedKey {
if len(extKey) != 32 {
logger.CriticalIf(context.Background(), errors.New("crypto: invalid key length"))
}
var (
sealingKey [32]byte
encryptedKey bytes.Buffer

View File

@@ -15,17 +15,7 @@
package crypto
import (
"bytes"
"context"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"errors"
"io"
"github.com/minio/minio/cmd/logger"
"github.com/minio/minio/pkg/kms"
"github.com/minio/sio"
)
// Context is a list of key-value pairs cryptographically
@@ -37,73 +27,3 @@ type Context = kms.Context
// data key generation and unsealing of KMS-generated
// data keys.
type KMS = kms.KMS
type masterKeyKMS struct {
keyID string
masterKey [32]byte
}
// NewMasterKey returns a basic KMS implementation from a single 256 bit master key.
//
// The KMS accepts any keyID but binds the keyID and context cryptographically
// to the generated keys.
func NewMasterKey(keyID string, key [32]byte) KMS { return &masterKeyKMS{keyID: keyID, masterKey: key} }
func (m *masterKeyKMS) Stat() (kms.Status, error) {
return kms.Status{
Name: "MasterKey",
DefaultKey: m.keyID,
}, nil
}
func (m *masterKeyKMS) CreateKey(keyID string) error {
return errors.New("crypto: creating keys is not supported by a static master key")
}
func (m *masterKeyKMS) GenerateKey(keyID string, ctx Context) (kms.DEK, error) {
if keyID == "" {
keyID = m.keyID
}
var key [32]byte
if _, err := io.ReadFull(rand.Reader, key[:]); err != nil {
logger.CriticalIf(context.Background(), errOutOfEntropy)
}
var (
buffer bytes.Buffer
derivedKey = m.deriveKey(keyID, ctx)
)
if n, err := sio.Encrypt(&buffer, bytes.NewReader(key[:]), sio.Config{Key: derivedKey[:]}); err != nil || n != 64 {
logger.CriticalIf(context.Background(), errors.New("KMS: unable to encrypt data key"))
}
return kms.DEK{
KeyID: m.keyID,
Plaintext: key[:],
Ciphertext: buffer.Bytes(),
}, nil
}
func (m *masterKeyKMS) DecryptKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) {
var derivedKey = m.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 (m *masterKeyKMS) deriveKey(keyID string, context Context) (key [32]byte) {
if context == nil {
context = Context{}
}
ctxBytes, _ := context.MarshalText()
mac := hmac.New(sha256.New, m.masterKey[:])
mac.Write([]byte(keyID))
mac.Write(ctxBytes)
mac.Sum(key[:0])
return key
}

View File

@@ -1,104 +0,0 @@
// MinIO Cloud Storage, (C) 2015, 2016, 2017, 2018 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.
package crypto
import (
"bytes"
"fmt"
"path"
"testing"
)
var masterKeyKMSTests = []struct {
GenKeyID, UnsealKeyID string
GenContext, UnsealContext Context
ShouldFail bool
}{
{GenKeyID: "", UnsealKeyID: "", GenContext: Context{}, UnsealContext: nil, ShouldFail: false}, // 0
{GenKeyID: "ac47be7f", UnsealKeyID: "ac47be7f", GenContext: Context{}, UnsealContext: Context{}, ShouldFail: false}, // 1
{GenKeyID: "ac47be7f", UnsealKeyID: "ac47be7f", GenContext: Context{"bucket": "object"}, UnsealContext: Context{"bucket": "object"}, ShouldFail: false}, // 2
{GenKeyID: "", UnsealKeyID: "", GenContext: Context{"bucket": path.Join("bucket", "object")}, UnsealContext: Context{"bucket": path.Join("bucket", "object")}, ShouldFail: false}, // 3
{GenKeyID: "", UnsealKeyID: "", GenContext: Context{"a": "a", "0": "0", "b": "b"}, UnsealContext: Context{"b": "b", "a": "a", "0": "0"}, ShouldFail: false}, // 4
{GenKeyID: "ac47be7f", UnsealKeyID: "ac47be7e", GenContext: Context{}, UnsealContext: Context{}, ShouldFail: true}, // 5
{GenKeyID: "ac47be7f", UnsealKeyID: "ac47be7f", GenContext: Context{"bucket": "object"}, UnsealContext: Context{"Bucket": "object"}, ShouldFail: true}, // 6
{GenKeyID: "", UnsealKeyID: "", GenContext: Context{"bucket": path.Join("bucket", "Object")}, UnsealContext: Context{"bucket": path.Join("bucket", "object")}, ShouldFail: true}, // 7
{GenKeyID: "", UnsealKeyID: "", GenContext: Context{"a": "a", "0": "1", "b": "b"}, UnsealContext: Context{"b": "b", "a": "a", "0": "0"}, ShouldFail: true}, // 8
}
func TestMasterKeyKMS(t *testing.T) {
for i, test := range masterKeyKMSTests {
kms := NewMasterKey(test.GenKeyID, [32]byte{})
key, err := kms.GenerateKey(test.GenKeyID, test.GenContext)
if err != nil {
t.Errorf("Test %d: KMS failed to generate key: %v", i, err)
}
unsealedKey, err := kms.DecryptKey(test.UnsealKeyID, key.Ciphertext, test.UnsealContext)
if err != nil && !test.ShouldFail {
t.Errorf("Test %d: KMS failed to unseal the generated key: %v", i, err)
}
if err == nil && test.ShouldFail {
t.Errorf("Test %d: KMS unsealed the generated key successfully but should have failed", i)
}
if !test.ShouldFail && !bytes.Equal(key.Plaintext, unsealedKey[:]) {
t.Errorf("Test %d: The generated and unsealed key differ", i)
}
}
}
var contextMarshalTextTests = []struct {
Context Context
ExpectedJSON string
}{
0: {Context: Context{}, ExpectedJSON: "{}"},
1: {Context: Context{"a": "b"}, ExpectedJSON: `{"a":"b"}`},
2: {Context: Context{"a": "b", "c": "d"}, ExpectedJSON: `{"a":"b","c":"d"}`},
3: {Context: Context{"c": "d", "a": "b"}, ExpectedJSON: `{"a":"b","c":"d"}`},
4: {Context: Context{"0": "1", "-": "2", ".": "#"}, ExpectedJSON: `{"-":"2",".":"#","0":"1"}`},
// rfc 8259 escapes
5: {Context: Context{"0": "1", "key\\": "val\tue\r\n", "\"": "\""}, ExpectedJSON: `{"\"":"\"","0":"1","key\\":"val\tue\r\n"}`},
// html sensitive escapes
6: {Context: Context{"a": "<>&"}, ExpectedJSON: `{"a":"\u003c\u003e\u0026"}`},
}
func TestContextMarshalText(t *testing.T) {
for i, test := range contextMarshalTextTests {
text, err := test.Context.MarshalText()
if err != nil {
t.Fatalf("Test %d: Failed to encode context: %v", i, err)
}
if string(text) != test.ExpectedJSON {
t.Errorf("Test %d: JSON representation differ - got: '%s' want: '%s'", i, string(text), test.ExpectedJSON)
}
}
}
func BenchmarkContext(b *testing.B) {
tests := []Context{{}, {"bucket": "warp-benchmark-bucket"}, {"0": "1", "-": "2", ".": "#"}, {"34trg": "dfioutr89", "ikjfdghkjf": "jkedfhgfjkhg", "sdfhsdjkh": "if88889", "asddsirfh804": "kjfdshgdfuhgfg78-45604586#$%<>&"}}
for _, test := range tests {
b.Run(fmt.Sprintf("%d-elems", len(test)), func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := test.MarshalText()
if err != nil {
b.Fatal(err)
}
}
})
}
}

View File

@@ -1,184 +0,0 @@
/*
* MinIO Cloud Storage, (C) 2019 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.
*/
package crypto
import (
"reflect"
"strconv"
"github.com/minio/minio/cmd/config"
"github.com/minio/minio/pkg/env"
xnet "github.com/minio/minio/pkg/net"
)
const (
// EnvKMSMasterKeyLegacy is the environment variable used to specify
// a KMS master key used to protect SSE-S3 per-object keys.
// Valid values must be of the from: "KEY_ID:32_BYTE_HEX_VALUE".
EnvKMSMasterKeyLegacy = "MINIO_SSE_MASTER_KEY"
// EnvAutoEncryptionLegacy is the environment variable used to en/disable
// SSE-S3 auto-encryption. SSE-S3 auto-encryption, if enabled,
// requires a valid KMS configuration and turns any non-SSE-C
// request into an SSE-S3 request.
// If present EnvAutoEncryption must be either "on" or "off".
EnvAutoEncryptionLegacy = "MINIO_SSE_AUTO_ENCRYPTION"
)
const (
// EnvLegacyVaultEndpoint is the environment variable used to specify
// the vault HTTPS endpoint.
EnvLegacyVaultEndpoint = "MINIO_SSE_VAULT_ENDPOINT"
// EnvLegacyVaultAuthType is the environment variable used to specify
// the authentication type for vault.
EnvLegacyVaultAuthType = "MINIO_SSE_VAULT_AUTH_TYPE"
// EnvLegacyVaultAppRoleID is the environment variable used to specify
// the vault AppRole ID.
EnvLegacyVaultAppRoleID = "MINIO_SSE_VAULT_APPROLE_ID"
// EnvLegacyVaultAppSecretID is the environment variable used to specify
// the vault AppRole secret corresponding to the AppRole ID.
EnvLegacyVaultAppSecretID = "MINIO_SSE_VAULT_APPROLE_SECRET"
// EnvLegacyVaultKeyVersion is the environment variable used to specify
// the vault key version.
EnvLegacyVaultKeyVersion = "MINIO_SSE_VAULT_KEY_VERSION"
// EnvLegacyVaultKeyName is the environment variable used to specify
// the vault named key-ring. In the S3 context it's referred as
// customer master key ID (CMK-ID).
EnvLegacyVaultKeyName = "MINIO_SSE_VAULT_KEY_NAME"
// EnvLegacyVaultCAPath is the environment variable used to specify the
// path to a directory of PEM-encoded CA cert files. These CA cert
// files are used to authenticate MinIO to Vault over mTLS.
EnvLegacyVaultCAPath = "MINIO_SSE_VAULT_CAPATH"
// EnvLegacyVaultNamespace is the environment variable used to specify
// vault namespace. The vault namespace is used if the enterprise
// version of Hashicorp Vault is used.
EnvLegacyVaultNamespace = "MINIO_SSE_VAULT_NAMESPACE"
)
// SetKMSConfig helper to migrate from older KMSConfig to new KV.
func SetKMSConfig(s config.Config, cfg KMSConfig) {
if cfg.Vault.Endpoint == "" {
return
}
s[config.KmsVaultSubSys][config.Default] = config.KVS{
config.KV{
Key: KMSVaultEndpoint,
Value: cfg.Vault.Endpoint,
},
config.KV{
Key: KMSVaultCAPath,
Value: cfg.Vault.CAPath,
},
config.KV{
Key: KMSVaultAuthType,
Value: func() string {
if cfg.Vault.Auth.Type != "" {
return cfg.Vault.Auth.Type
}
return "approle"
}(),
},
config.KV{
Key: KMSVaultAppRoleID,
Value: cfg.Vault.Auth.AppRole.ID,
},
config.KV{
Key: KMSVaultAppRoleSecret,
Value: cfg.Vault.Auth.AppRole.Secret,
},
config.KV{
Key: KMSVaultKeyName,
Value: cfg.Vault.Key.Name,
},
config.KV{
Key: KMSVaultKeyVersion,
Value: strconv.Itoa(cfg.Vault.Key.Version),
},
config.KV{
Key: KMSVaultNamespace,
Value: cfg.Vault.Namespace,
},
}
}
// lookupConfigLegacy extracts the KMS configuration provided by legacy
// environment variables and merge them with the provided KMS configuration.
// The merging follows the following rules:
//
// 1. A valid value provided as environment variable has higher priority
// than the provided configuration and overwrites the value from the
// configuration file.
//
// 2. A value specified as environment variable never changes the configuration
// file. So it is never made a persistent setting.
//
// It sets the global KMS configuration according to the merged configuration
// on success.
func lookupConfigLegacy(kvs config.KVS) (VaultConfig, error) {
vcfg := VaultConfig{
Auth: VaultAuth{
Type: "approle",
},
}
endpointStr := env.Get(EnvLegacyVaultEndpoint, "")
if endpointStr != "" {
// Lookup Hashicorp-Vault configuration & overwrite config entry if ENV var is present
endpoint, err := xnet.ParseHTTPURL(endpointStr)
if err != nil {
return vcfg, err
}
endpointStr = endpoint.String()
}
var err error
vcfg.Endpoint = endpointStr
vcfg.CAPath = env.Get(EnvLegacyVaultCAPath, "")
vcfg.Auth.Type = env.Get(EnvLegacyVaultAuthType, "")
if vcfg.Auth.Type == "" {
vcfg.Auth.Type = "approle"
}
vcfg.Auth.AppRole.ID = env.Get(EnvLegacyVaultAppRoleID, "")
vcfg.Auth.AppRole.Secret = env.Get(EnvLegacyVaultAppSecretID, "")
vcfg.Key.Name = env.Get(EnvLegacyVaultKeyName, "")
vcfg.Namespace = env.Get(EnvLegacyVaultNamespace, "")
if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" {
vcfg.Key.Version, err = strconv.Atoi(keyVersion)
if err != nil {
return vcfg, Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)",
EnvLegacyVaultKeyVersion, keyVersion)
}
}
if reflect.DeepEqual(vcfg, defaultVaultCfg) {
return vcfg, nil
}
if err = vcfg.Verify(); err != nil {
return vcfg, err
}
vcfg.Enabled = true
return vcfg, nil
}

View File

@@ -1,41 +0,0 @@
// MinIO Cloud Storage, (C) 2017-2019 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.
package crypto
import (
"encoding/hex"
"strings"
)
// ParseMasterKey parses the value of the environment variable
// `EnvKMSMasterKey` and returns a key-ID and a master-key KMS on success.
func ParseMasterKey(envArg string) (KMS, error) {
values := strings.SplitN(envArg, ":", 2)
if len(values) != 2 {
return nil, Errorf("Invalid KMS master key: %s does not contain a ':'", envArg)
}
var (
keyID = values[0]
hexKey = values[1]
)
if len(hexKey) != 64 { // 2 hex bytes = 1 byte
return nil, Errorf("Invalid KMS master key: %s not a 32 bytes long HEX value", hexKey)
}
var masterKey [32]byte
if _, err := hex.Decode(masterKey[:], []byte(hexKey)); err != nil {
return nil, Errorf("Invalid KMS master key: %v", err)
}
return NewMasterKey(keyID, masterKey), nil
}

View File

@@ -1,62 +0,0 @@
// MinIO Cloud Storage, (C) 2019 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.
package crypto
import "testing"
func TestParseMasterKey(t *testing.T) {
tests := []struct {
envValue string
expectedKeyID string
success bool
}{
{
envValue: "invalid-value",
success: false,
},
{
envValue: "too:many:colons",
success: false,
},
{
envValue: "myminio-key:not-a-hex",
success: false,
},
{
envValue: "my-minio-key:6368616e676520746869732070617373776f726420746f206120736563726574",
expectedKeyID: "my-minio-key",
success: true,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.envValue, func(t *testing.T) {
kms, err := ParseMasterKey(tt.envValue)
if tt.success && err != nil {
t.Error(err)
}
if !tt.success && err == nil {
t.Error("Unexpected failure")
}
if kms != nil {
stat, _ := kms.Stat()
if err == nil && stat.DefaultKey != tt.expectedKeyID {
t.Errorf("Expected keyID %s, got %s", tt.expectedKeyID, stat.DefaultKey)
}
}
})
}
}

View File

@@ -182,45 +182,3 @@ func TestSSECopyUnsealObjectKey(t *testing.T) {
}
}
}
var s3UnsealObjectKeyTests = []struct {
KMS KMS
Bucket, Object string
Metadata map[string]string
ExpectedErr error
}{
{ // 0 - Valid KMS key-ID and valid metadata entries for bucket/object
KMS: NewMasterKey("my-minio-key", [32]byte{}),
Bucket: "bucket",
Object: "object",
Metadata: map[string]string{
"X-Minio-Internal-Server-Side-Encryption-Iv": "hhVY0LKR1YtZbzAKxTWUfZt5enDfYX6Fxz1ma8Kiudc=",
"X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key": "IAAfALhsOeD5AE3s5Zgq3DZ5VFGsOa3B0ksVC86veDcaj+fXv2U0VadhPaOKYr9Emd5ssOsO0uIhIIrKiOy9rA==",
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key": "IAAfAMRS2iw45FsfiF3QXajSYVWj1lxMpQm6DxDGPtADCX6fJQQ4atHBtfpgqJFyeQmIHsm0FBI+UlHw1Lv4ug==",
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id": "test-key-1",
"X-Minio-Internal-Server-Side-Encryption-Seal-Algorithm": "DAREv2-HMAC-SHA256",
},
ExpectedErr: nil,
},
{ // 1 - Valid KMS key-ID for invalid metadata entries for bucket/object
KMS: NewMasterKey("my-minio-key", [32]byte{}),
Bucket: "bucket",
Object: "object",
Metadata: map[string]string{
"X-Minio-Internal-Server-Side-Encryption-Iv": "hhVY0LKR1YtZbzAKxTWUfZt5enDfYX6Fxz1ma8Kiudc=",
"X-Minio-Internal-Server-Side-Encryption-S3-Sealed-Key": "IAAfALhsOeD5AE3s5Zgq3DZ5VFGsOa3B0ksVC86veDcaj+fXv2U0VadhPaOKYr9Emd5ssOsO0uIhIIrKiOy9rA==",
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Sealed-Key": "IAAfAMRS2iw45FsfiF3QXajSYVWj1lxMpQm6DxDGPtADCX6fJQQ4atHBtfpgqJFyeQmIHsm0FBI+UlHw1Lv4ug==",
"X-Minio-Internal-Server-Side-Encryption-S3-Kms-Key-Id": "test-key-1",
},
ExpectedErr: errMissingInternalSealAlgorithm,
},
}
func TestS3UnsealObjectKey(t *testing.T) {
for i, test := range s3UnsealObjectKeyTests {
if _, err := S3.UnsealObjectKey(test.KMS, test.Metadata, test.Bucket, test.Object); err != test.ExpectedErr {
t.Errorf("Test %d: got: %v - want: %v", i, err, test.ExpectedErr)
}
}
}

View File

@@ -1,289 +0,0 @@
// MinIO Cloud Storage, (C) 2015, 2016, 2017, 2018 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.
package crypto
import (
"encoding/base64"
"errors"
"fmt"
"strings"
"time"
vault "github.com/hashicorp/vault/api"
"github.com/minio/minio/pkg/kms"
)
var (
//ErrKMSAuthLogin is raised when there is a failure authenticating to KMS
ErrKMSAuthLogin = Errorf("Vault service did not return auth info")
)
// VaultKey represents vault encryption key-ring.
type VaultKey struct {
Name string `json:"name"` // The name of the encryption key-ring
Version int `json:"version"` // The key version
}
// VaultAuth represents vault authentication type.
// Currently the only supported authentication type is AppRole.
type VaultAuth struct {
Type string `json:"type"` // The authentication type
AppRole VaultAppRole `json:"approle"` // The AppRole authentication credentials
}
// VaultAppRole represents vault AppRole authentication credentials
type VaultAppRole struct {
ID string `json:"id"` // The AppRole access ID
Secret string `json:"secret"` // The AppRole secret
}
// VaultConfig represents vault configuration.
type VaultConfig struct {
Enabled bool `json:"-"`
Endpoint string `json:"endpoint"` // The vault API endpoint as URL
CAPath string `json:"-"` // The path to PEM-encoded certificate files used for mTLS. Currently not used in config file.
Auth VaultAuth `json:"auth"` // The vault authentication configuration
Key VaultKey `json:"key-id"` // The named key used for key-generation / decryption.
Namespace string `json:"-"` // The vault namespace of enterprise vault instances
}
// vaultService represents a connection to a vault KMS.
type vaultService struct {
config *VaultConfig
client *vault.Client
secret *vault.Secret
leaseDuration time.Duration
}
var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements KMS
// Verify returns a nil error if the vault configuration
// is valid. A valid configuration is either empty or
// contains valid non-default values.
func (v *VaultConfig) Verify() (err error) {
switch {
case v.Endpoint == "":
err = Errorf("crypto: missing hashicorp vault endpoint")
case strings.ToLower(v.Auth.Type) != "approle":
err = Errorf("crypto: invalid hashicorp vault authentication type: %s is not supported", v.Auth.Type)
case v.Auth.AppRole.ID == "":
err = Errorf("crypto: missing hashicorp vault AppRole ID")
case v.Auth.AppRole.Secret == "":
err = Errorf("crypto: missing hashicorp vault AppSecret ID")
case v.Key.Name == "":
err = Errorf("crypto: missing hashicorp vault key name")
case v.Key.Version < 0:
err = Errorf("crypto: invalid hashicorp vault key version: The key version must not be negative")
}
return
}
// NewVault initializes Hashicorp Vault KMS by authenticating
// to Vault with the credentials in config and gets a client
// token for future api calls.
func NewVault(config VaultConfig) (KMS, error) {
if !config.Enabled {
return nil, nil
}
if err := config.Verify(); err != nil {
return nil, err
}
vaultCfg := vault.Config{Address: config.Endpoint}
if err := vaultCfg.ConfigureTLS(&vault.TLSConfig{CAPath: config.CAPath}); err != nil {
return nil, err
}
client, err := vault.NewClient(&vaultCfg)
if err != nil {
return nil, Errorf("crypto: client error %w", err)
}
if config.Namespace != "" {
client.SetNamespace(config.Namespace)
}
v := &vaultService{client: client, config: &config}
if err := v.authenticate(); err != nil {
return nil, err
}
v.renewToken()
return v, nil
}
// renewToken starts a new go-routine which renews
// the vault authentication token periodically and re-authenticates
// if the token renewal fails
func (v *vaultService) renewToken() {
retryDelay := v.leaseDuration / 2
go func() {
for {
if v.secret == nil {
if err := v.authenticate(); err != nil {
time.Sleep(retryDelay)
continue
}
}
s, err := v.client.Auth().Token().RenewSelf(int(v.leaseDuration))
if err != nil || s == nil {
v.secret = nil
time.Sleep(retryDelay)
continue
}
if ok, err := s.TokenIsRenewable(); !ok || err != nil {
v.secret = nil
continue
}
ttl, err := s.TokenTTL()
if err != nil {
v.secret = nil
continue
}
v.secret = s
retryDelay = ttl / 2
time.Sleep(retryDelay)
}
}()
}
// authenticate logs the app to vault, and starts the auto renewer
// before secret expires
func (v *vaultService) authenticate() (err error) {
payload := map[string]interface{}{
"role_id": v.config.Auth.AppRole.ID,
"secret_id": v.config.Auth.AppRole.Secret,
}
var tokenID string
var ttl time.Duration
var secret *vault.Secret
secret, err = v.client.Logical().Write("auth/approle/login", payload)
if err != nil {
err = Errorf("crypto: client error %w", err)
return
}
if secret == nil {
err = ErrKMSAuthLogin
return
}
tokenID, err = secret.TokenID()
if err != nil {
err = ErrKMSAuthLogin
return
}
ttl, err = secret.TokenTTL()
if err != nil {
err = ErrKMSAuthLogin
return
}
v.client.SetToken(tokenID)
v.secret = secret
v.leaseDuration = ttl
return
}
// Info returns some information about the Vault,
// configuration - like the endpoints or authentication
// method.
func (v *vaultService) Stat() (kms.Status, error) {
return kms.Status{
Endpoints: []string{v.config.Endpoint},
Name: "Hashicorp Vault",
DefaultKey: v.config.Key.Name,
}, nil
}
// CreateKey is a stub that exists such that the Vault
// client implements the KMS interface. It always returns
// a not-implemented error.
//
// Creating keys requires a KES instance between MinIO and Vault.
func (v *vaultService) CreateKey(keyID string) error {
// Creating new keys requires KES.
return errors.New("crypto: creating keys is not supported by Vault")
}
// GenerateKey returns a new plaintext key, generated by the KMS,
// and a sealed version of this plaintext key encrypted using the
// named key referenced by keyID. It also binds the generated key
// cryptographically to the provided context.
func (v *vaultService) GenerateKey(keyID string, ctx Context) (kms.DEK, error) {
if keyID == "" {
keyID = v.config.Key.Name
}
context, err := ctx.MarshalText()
if err != nil {
return kms.DEK{}, err
}
payload := map[string]interface{}{
"context": base64.StdEncoding.EncodeToString(context),
}
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload)
if err != nil {
return kms.DEK{}, Errorf("crypto: client error %w", err)
}
sealKey, ok := s.Data["ciphertext"].(string)
if !ok {
return kms.DEK{}, Errorf("crypto: incorrect 'ciphertext' key type %v", s.Data["ciphertext"])
}
plainKeyB64, ok := s.Data["plaintext"].(string)
if !ok {
return kms.DEK{}, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
}
plainKey, err := base64.StdEncoding.DecodeString(plainKeyB64)
if err != nil {
return kms.DEK{}, Errorf("crypto: invalid base64 key %w", err)
}
return kms.DEK{
KeyID: keyID,
Plaintext: plainKey,
Ciphertext: []byte(sealKey),
}, nil
}
// UnsealKey returns the decrypted sealedKey as plaintext key.
// Therefore it sends the sealedKey to the KMS which decrypts
// it using the named key referenced by keyID and responses with
// the plaintext key.
//
// The context must be same context as the one provided while
// generating the plaintext key / sealedKey.
func (v *vaultService) DecryptKey(keyID string, sealedKey []byte, ctx Context) ([]byte, error) {
context, err := ctx.MarshalText()
if err != nil {
return nil, err
}
payload := map[string]interface{}{
"ciphertext": string(sealedKey),
"context": base64.StdEncoding.EncodeToString(context),
}
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload)
if err != nil {
return nil, Errorf("crypto: client error %w", err)
}
base64Key, ok := s.Data["plaintext"].(string)
if !ok {
return nil, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
}
plainKey, err := base64.StdEncoding.DecodeString(base64Key)
if err != nil {
return nil, Errorf("crypto: invalid base64 key %w", err)
}
return plainKey, nil
}

View File

@@ -1,100 +0,0 @@
// Minio Cloud Storage, (C) 2019 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.
package crypto
import (
"testing"
)
var verifyVaultConfigTests = []struct {
Config VaultConfig
ShouldFail bool
}{
{
ShouldFail: true,
Config: VaultConfig{
Endpoint: "https://127.0.0.1:8080",
Enabled: true,
},
},
{
ShouldFail: true, // 1
Config: VaultConfig{
Enabled: true,
Endpoint: "https://127.0.0.1:8080",
Auth: VaultAuth{Type: "unsupported"},
},
},
{
ShouldFail: true, // 2
Config: VaultConfig{
Enabled: true,
Endpoint: "https://127.0.0.1:8080",
Auth: VaultAuth{
Type: "approle",
AppRole: VaultAppRole{},
},
},
},
{
ShouldFail: true, // 3
Config: VaultConfig{
Enabled: true,
Endpoint: "https://127.0.0.1:8080",
Auth: VaultAuth{
Type: "approle",
AppRole: VaultAppRole{ID: "123456"},
},
},
},
{
ShouldFail: true, // 4
Config: VaultConfig{
Enabled: true,
Endpoint: "https://127.0.0.1:8080",
Auth: VaultAuth{
Type: "approle",
AppRole: VaultAppRole{ID: "123456", Secret: "abcdef"},
},
},
},
{
ShouldFail: true, // 5
Config: VaultConfig{
Enabled: true,
Endpoint: "https://127.0.0.1:8080",
Auth: VaultAuth{
Type: "approle",
AppRole: VaultAppRole{ID: "123456", Secret: "abcdef"},
},
Key: VaultKey{Name: "default-key", Version: -1},
},
},
}
func TestVerifyVaultConfig(t *testing.T) {
for _, test := range verifyVaultConfigTests {
test := test
t.Run(test.Config.Endpoint, func(t *testing.T) {
err := test.Config.Verify()
if test.ShouldFail && err == nil {
t.Errorf("Verify should fail but returned 'err == nil'")
}
if !test.ShouldFail && err != nil {
t.Errorf("Verify should succeed but returned err: %s", err)
}
})
}
}