2019-10-08 01:47:56 -04:00
// MinIO Cloud Storage, (C) 2017-2019 MinIO, Inc.
2018-08-17 15:52:14 -04:00
//
// 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
2019-10-08 01:47:56 -04:00
import (
"errors"
2020-08-31 21:10:52 -04:00
"math/rand"
2019-12-18 18:10:57 -05:00
"net/http"
2021-03-09 03:02:35 -05:00
"os"
2019-11-09 12:27:23 -05:00
"reflect"
2019-10-08 01:47:56 -04:00
"strconv"
2020-08-31 21:10:52 -04:00
"strings"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
"github.com/minio/minio/cmd/config"
2020-08-31 21:10:52 -04:00
"github.com/minio/minio/pkg/ellipses"
2019-10-08 01:47:56 -04:00
"github.com/minio/minio/pkg/env"
2019-10-31 02:39:09 -04:00
xnet "github.com/minio/minio/pkg/net"
2019-10-08 01:47:56 -04:00
)
2019-10-23 01:59:13 -04:00
// KMSConfig has the KMS config for hashicorp vault
type KMSConfig struct {
AutoEncryption bool ` json:"-" `
Vault VaultConfig ` json:"vault" `
2019-12-13 15:57:11 -05:00
Kes KesConfig ` json:"kes" `
2019-10-23 01:59:13 -04:00
}
// 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"
)
2019-12-13 15:57:11 -05:00
// KMS kes constants.
const (
KMSKesEndpoint = "endpoint"
KMSKesKeyFile = "key_file"
KMSKesCertFile = "cert_file"
KMSKesCAPath = "capath"
KMSKesKeyName = "key_name"
)
2019-10-23 01:59:13 -04:00
// DefaultKVS - default KV crypto config
var (
2019-12-13 15:57:11 -05:00
DefaultVaultKVS = config . KVS {
2019-11-20 18:10:24 -05:00
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 : "" ,
} ,
2019-10-23 01:59:13 -04:00
}
2019-12-13 15:57:11 -05:00
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 : "" ,
} ,
}
2019-10-23 01:59:13 -04:00
)
2019-10-08 01:47:56 -04:00
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".
2019-10-23 01:59:13 -04:00
EnvKMSMasterKey = "MINIO_KMS_MASTER_KEY"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSAutoEncryption is the environment variable used to en/disable
2019-10-08 01:47:56 -04:00
// 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".
2019-10-23 01:59:13 -04:00
EnvKMSAutoEncryption = "MINIO_KMS_AUTO_ENCRYPTION"
2019-10-08 01:47:56 -04:00
)
const (
2019-10-23 01:59:13 -04:00
// EnvKMSVaultEndpoint is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the vault HTTPS endpoint.
2019-10-23 01:59:13 -04:00
EnvKMSVaultEndpoint = "MINIO_KMS_VAULT_ENDPOINT"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultAuthType is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the authentication type for vault.
2019-10-23 01:59:13 -04:00
EnvKMSVaultAuthType = "MINIO_KMS_VAULT_AUTH_TYPE"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultAppRoleID is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the vault AppRole ID.
2019-10-23 01:59:13 -04:00
EnvKMSVaultAppRoleID = "MINIO_KMS_VAULT_APPROLE_ID"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultAppSecretID is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the vault AppRole secret corresponding to the AppRole ID.
2019-10-23 01:59:13 -04:00
EnvKMSVaultAppSecretID = "MINIO_KMS_VAULT_APPROLE_SECRET"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultKeyVersion is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the vault key version.
2019-10-23 01:59:13 -04:00
EnvKMSVaultKeyVersion = "MINIO_KMS_VAULT_KEY_VERSION"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultKeyName is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// the vault named key-ring. In the S3 context it's referred as
// customer master key ID (CMK-ID).
2019-10-23 01:59:13 -04:00
EnvKMSVaultKeyName = "MINIO_KMS_VAULT_KEY_NAME"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultCAPath is the environment variable used to specify the
2019-10-08 01:47:56 -04:00
// path to a directory of PEM-encoded CA cert files. These CA cert
// files are used to authenticate MinIO to Vault over mTLS.
2019-10-23 01:59:13 -04:00
EnvKMSVaultCAPath = "MINIO_KMS_VAULT_CAPATH"
2019-10-08 01:47:56 -04:00
2019-10-23 01:59:13 -04:00
// EnvKMSVaultNamespace is the environment variable used to specify
2019-10-08 01:47:56 -04:00
// vault namespace. The vault namespace is used if the enterprise
// version of Hashicorp Vault is used.
2019-10-23 01:59:13 -04:00
EnvKMSVaultNamespace = "MINIO_KMS_VAULT_NAMESPACE"
2019-10-08 01:47:56 -04:00
)
2019-12-13 15:57:11 -05:00
const (
// EnvKMSKesEndpoint is the environment variable used to specify
2020-08-31 21:10:52 -04:00
// one or multiple KES server HTTPS endpoints. The individual
// endpoints should be separated by ','.
2019-12-13 15:57:11 -05:00
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 {
2019-11-09 12:27:23 -05:00
Auth : VaultAuth {
Type : "approle" ,
} ,
}
2019-12-13 15:57:11 -05:00
var defaultKesCfg = KesConfig { }
// EnabledVault returns true if HashiCorp Vault is enabled.
func EnabledVault ( kvs config . KVS ) bool {
2019-12-04 18:32:37 -05:00
endpoint := kvs . Get ( KMSVaultEndpoint )
return endpoint != ""
}
2019-12-13 15:57:11 -05:00
// 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 ) )
2020-08-31 21:10:52 -04:00
var endpoints [ ] string
for _ , endpoint := range strings . Split ( endpointStr , "," ) {
2020-08-31 22:27:49 -04:00
if strings . TrimSpace ( endpoint ) == "" {
continue
}
2020-08-31 21:10:52 -04:00
if ! ellipses . HasEllipses ( endpoint ) {
endpoints = append ( endpoints , endpoint )
continue
}
pattern , err := ellipses . FindEllipsesPatterns ( endpoint )
2019-12-13 15:57:11 -05:00
if err != nil {
return kesCfg , err
}
2020-08-31 21:10:52 -04:00
for _ , p := range pattern {
endpoints = append ( endpoints , p . Expand ( ) ... )
}
2019-12-13 15:57:11 -05:00
}
2020-08-31 22:27:49 -04:00
if len ( endpoints ) == 0 {
return kesCfg , nil
}
2019-12-13 15:57:11 -05:00
2020-08-31 21:10:52 -04:00
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 {
endpoint , err := xnet . ParseHTTPURL ( endpoint )
if err != nil {
return kesCfg , err
}
kesCfg . Endpoint [ ( randNum + i ) % len ( endpoints ) ] = endpoint . String ( )
}
2019-12-13 15:57:11 -05:00
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
}
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
2019-12-18 18:10:57 -05:00
func LookupConfig ( c config . Config , defaultRootCAsDir string , transport * http . Transport ) ( KMSConfig , error ) {
2019-12-13 15:57:11 -05:00
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
}
2019-12-18 18:10:57 -05:00
kesCfg . Transport = transport
2019-12-13 15:57:11 -05:00
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
2019-10-08 01:47:56 -04:00
// 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.
2019-12-13 15:57:11 -05:00
func LookupVaultConfig ( kvs config . KVS ) ( VaultConfig , error ) {
if err := config . CheckValidKeys ( config . KmsVaultSubSys , kvs , DefaultVaultKVS ) ; err != nil {
return VaultConfig { } , err
2019-10-23 01:59:13 -04:00
}
2019-12-13 15:57:11 -05:00
vcfg , err := lookupConfigLegacy ( kvs )
2019-10-23 01:59:13 -04:00
if err != nil {
2019-12-13 15:57:11 -05:00
return vcfg , err
2019-10-23 01:59:13 -04:00
}
2019-12-13 15:57:11 -05:00
if vcfg . Enabled {
return vcfg , nil
2019-10-23 01:59:13 -04:00
}
2019-11-13 20:38:05 -05:00
2019-12-13 15:57:11 -05:00
vcfg = VaultConfig {
2019-10-31 02:39:09 -04:00
Auth : VaultAuth {
Type : "approle" ,
} ,
}
2019-12-13 15:57:11 -05:00
2019-10-31 02:39:09 -04:00
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 {
2019-12-13 15:57:11 -05:00
return vcfg , err
2019-10-31 02:39:09 -04:00
}
endpointStr = endpoint . String ( )
}
2019-12-13 15:57:11 -05:00
2019-10-31 02:39:09 -04:00
vcfg . Endpoint = endpointStr
2019-10-23 01:59:13 -04:00
vcfg . CAPath = env . Get ( EnvKMSVaultCAPath , kvs . Get ( KMSVaultCAPath ) )
vcfg . Auth . Type = env . Get ( EnvKMSVaultAuthType , kvs . Get ( KMSVaultAuthType ) )
2019-11-09 12:27:23 -05:00
if vcfg . Auth . Type == "" {
vcfg . Auth . Type = "approle"
}
2019-12-13 15:57:11 -05:00
2019-10-23 01:59:13 -04:00
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 ) )
2019-11-09 12:27:23 -05:00
if keyVersion := env . Get ( EnvKMSVaultKeyVersion , kvs . Get ( KMSVaultKeyVersion ) ) ; keyVersion != "" {
2019-10-23 01:59:13 -04:00
vcfg . Key . Version , err = strconv . Atoi ( keyVersion )
if err != nil {
2020-01-06 19:15:22 -05:00
return vcfg , Errorf ( "Unable to parse VaultKeyVersion value (`%s`)" , keyVersion )
2019-10-23 01:59:13 -04:00
}
2019-10-08 01:47:56 -04:00
}
2019-12-13 15:57:11 -05:00
if reflect . DeepEqual ( vcfg , defaultVaultCfg ) {
return vcfg , nil
2019-11-09 12:27:23 -05:00
}
// Verify all the proper settings.
2019-10-23 01:59:13 -04:00
if err = vcfg . Verify ( ) ; err != nil {
2019-12-13 15:57:11 -05:00
return vcfg , err
2019-10-23 01:59:13 -04:00
}
2019-11-09 12:27:23 -05:00
vcfg . Enabled = true
2019-12-13 15:57:11 -05:00
return vcfg , nil
2019-10-08 01:47:56 -04:00
}
// NewKMS - initialize a new KMS.
2019-10-23 01:59:13 -04:00
func NewKMS ( cfg KMSConfig ) ( kms KMS , err error ) {
2019-12-13 15:57:11 -05:00
// Lookup KMS master kes - only available through ENV.
2019-10-31 02:39:09 -04:00
if masterKeyLegacy := env . Get ( EnvKMSMasterKeyLegacy , "" ) ; len ( masterKeyLegacy ) != 0 {
2019-11-09 12:27:23 -05:00
if cfg . Vault . Enabled { // Vault and KMS master key provided
2019-10-23 01:59:13 -04:00
return kms , errors . New ( "Ambiguous KMS configuration: vault configuration and a master key are provided at the same time" )
}
2019-12-13 15:57:11 -05:00
if cfg . Kes . Enabled {
return kms , errors . New ( "Ambiguous KMS configuration: kes configuration and a master key are provided at the same time" )
}
2019-10-23 01:59:13 -04:00
kms , err = ParseMasterKey ( masterKeyLegacy )
if err != nil {
return kms , err
}
2019-10-31 02:39:09 -04:00
} else if masterKey := env . Get ( EnvKMSMasterKey , "" ) ; len ( masterKey ) != 0 {
2019-11-09 12:27:23 -05:00
if cfg . Vault . Enabled { // Vault and KMS master key provided
2019-10-08 01:47:56 -04:00
return kms , errors . New ( "Ambiguous KMS configuration: vault configuration and a master key are provided at the same time" )
}
2019-12-13 15:57:11 -05:00
if cfg . Kes . Enabled {
return kms , errors . New ( "Ambiguous KMS configuration: kes configuration and a master key are provided at the same time" )
}
2019-10-08 01:47:56 -04:00
kms , err = ParseMasterKey ( masterKey )
if err != nil {
return kms , err
}
2019-12-13 15:57:11 -05:00
} 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" )
2019-11-09 12:27:23 -05:00
} else if cfg . Vault . Enabled {
2021-03-09 03:02:35 -05:00
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" )
}
2019-10-23 01:59:13 -04:00
kms , err = NewVault ( cfg . Vault )
2019-10-08 01:47:56 -04:00
if err != nil {
return kms , err
}
2019-12-13 15:57:11 -05:00
} else if cfg . Kes . Enabled {
kms , err = NewKes ( cfg . Kes )
if err != nil {
return kms , err
}
2019-10-08 01:47:56 -04:00
}
2019-10-23 01:59:13 -04:00
if cfg . AutoEncryption && kms == nil {
2019-10-08 01:47:56 -04:00
return kms , errors . New ( "Invalid KMS configuration: auto-encryption is enabled but no valid KMS configuration is present" )
}
return kms , nil
}