Add KMS plugin registry

This commit is contained in:
Saksham Sharma 2017-07-27 12:34:23 -07:00
parent a571ea43f9
commit 49989439d7
4 changed files with 182 additions and 0 deletions

View File

@ -12,14 +12,17 @@ go_library(
name = "go_default_library",
srcs = [
"config.go",
"plugins.go",
"types.go",
],
tags = ["automanaged"],
deps = [
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/aes:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/identity:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/secretbox:go_default_library",
],

View File

@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/storage/value"
aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope"
"k8s.io/apiserver/pkg/storage/value/encrypt/identity"
"k8s.io/apiserver/pkg/storage/value/encrypt/secretbox"
)
@ -38,6 +39,7 @@ const (
aesCBCTransformerPrefixV1 = "k8s:enc:aescbc:v1:"
aesGCMTransformerPrefixV1 = "k8s:enc:aesgcm:v1:"
secretboxTransformerPrefixV1 = "k8s:enc:secretbox:v1:"
kmsTransformerPrefixV1 = "k8s:enc:kms:v1:"
)
// GetTransformerOverrides returns the transformer overrides by reading and parsing the encryption provider configuration file
@ -144,6 +146,27 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e
found = true
}
if provider.KMS != nil {
if found == true {
return nil, fmt.Errorf("more than one provider specified in a single element, should split into different list elements")
}
f, err := os.Open(provider.KMS.ConfigFile)
if err != nil {
return nil, fmt.Errorf("error opening KMS provider configuration file %q: %v", provider.KMS.ConfigFile, err)
}
defer f.Close()
envelopeService, pluginFound, err := KMSPluginRegistry.getPlugin(provider.KMS.Name, f)
if err != nil {
return nil, fmt.Errorf("could not configure KMS plugin %q, %v", provider.KMS.Name, err)
}
if pluginFound == false {
return nil, fmt.Errorf("KMS plugin %q not found", provider.KMS.Name)
}
transformer, err = getEnvelopePrefixTransformer(provider.KMS, envelopeService)
found = true
}
if err != nil {
return result, err
}
@ -258,3 +281,16 @@ func GetSecretboxPrefixTransformer(config *SecretboxConfig) (value.PrefixTransfo
}
return result, nil
}
// getEnvelopePrefixTransformer returns a prefix transformer from the provided config.
// envelopeService is used as the root of trust.
func getEnvelopePrefixTransformer(config *KMSConfig, envelopeService envelope.Service) (value.PrefixTransformer, error) {
envelopeTransformer, err := envelope.NewEnvelopeTransformer(envelopeService, config.CacheSize, aestransformer.NewCBCTransformer)
if err != nil {
return value.PrefixTransformer{}, err
}
return value.PrefixTransformer{
Transformer: envelopeTransformer,
Prefix: []byte(kmsTransformerPrefixV1 + config.Name + ":"),
}, nil
}

View File

@ -0,0 +1,130 @@
/*
Copyright 2017 The Kubernetes Authors.
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 encryptionconfig
import (
"bytes"
"io"
"io/ioutil"
"reflect"
"sync"
"github.com/golang/glog"
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope"
)
// Factory is a function that returns an envelope Service for encryption providers.
// The config parameter provides an io.Reader handler to the factory in
// order to load specific configurations. If no configuration is provided
// the parameter is nil.
type Factory func(config io.Reader) (envelope.Service, error)
type cloudKMSFactory func(name string) (envelope.Service, error)
// KMSPlugins contains all registered KMS options.
type KMSPlugins struct {
lock sync.RWMutex
registry map[string]Factory
cloudKMS cloudKMSFactory
}
var (
// PluginEnabledFn checks whether a plugin is enabled. By default, if you ask about it, it's enabled.
PluginEnabledFn = func(name string, config io.Reader) bool {
return true
}
// KMSPluginRegistry contains the registered KMS plugins which can be used for configuring
// encryption providers.
KMSPluginRegistry = KMSPlugins{}
)
// PluginEnabledFunc is a function type that can provide an external check on whether an admission plugin may be enabled
type PluginEnabledFunc func(name string, config io.Reader) bool
// Register registers a plugin Factory by name. This
// is expected to happen during app startup.
func (ps *KMSPlugins) Register(name string, plugin Factory) {
ps.lock.Lock()
defer ps.lock.Unlock()
_, found := ps.registry[name]
if ps.registry == nil {
ps.registry = map[string]Factory{}
}
if found {
glog.Fatalf("KMS plugin %q was registered twice", name)
}
glog.V(1).Infof("Registered KMS plugin %q", name)
ps.registry[name] = plugin
}
// RegisterCloudProvidedKMSPlugin registers the cloud's KMS provider as
// an envelope.Service. This service is provided by the cloudprovider interface.
func (ps *KMSPlugins) RegisterCloudProvidedKMSPlugin(cloudKMSGetter cloudKMSFactory) {
ps.cloudKMS = cloudKMSGetter
}
// getPlugin creates an instance of the named plugin. It returns `false` if the
// the name is not known. The error is returned only when the named provider was
// known but failed to initialize. The config parameter specifies the io.Reader
// handler of the configuration file for the cloud provider, or nil for no configuration.
func (ps *KMSPlugins) getPlugin(name string, config io.Reader) (envelope.Service, bool, error) {
f, found := ps.fetchPluginFromRegistry(name)
if !found {
return nil, false, nil
}
config1, config2, err := splitStream(config)
if err != nil {
return nil, true, err
}
if !PluginEnabledFn(name, config1) {
return nil, true, nil
}
ret, err := f(config2)
return ret, true, err
}
// fetchPluginFromRegistry tries to get a registered plugin with the requested name.
func (ps *KMSPlugins) fetchPluginFromRegistry(name string) (Factory, bool) {
ps.lock.RLock()
defer ps.lock.RUnlock()
// Map lookup defaults to single value context
f, found := ps.registry[name]
return f, found
}
// getCloudProvidedPlugin creates an instance of the named cloud provided KMS plugin.
func (ps *KMSPlugins) getCloudProvidedPlugin(name string) (envelope.Service, error) {
return ps.cloudKMS(name)
}
// splitStream reads the stream bytes and constructs two copies of it.
func splitStream(config io.Reader) (io.Reader, io.Reader, error) {
if config == nil || reflect.ValueOf(config).IsNil() {
return nil, nil, nil
}
configBytes, err := ioutil.ReadAll(config)
if err != nil {
return nil, nil, err
}
return bytes.NewBuffer(configBytes), bytes.NewBuffer(configBytes), nil
}

View File

@ -45,6 +45,8 @@ type ProviderConfig struct {
Secretbox *SecretboxConfig `json:"secretbox,omitempty"`
// identity is the (empty) configuration for the identity transformer.
Identity *IdentityConfig `json:"identity,omitempty"`
// kms contains the name and path to configuration file for a KMS based envelope transformer.
KMS *KMSConfig `json:"kms,omitempty"`
}
// AESConfig contains the API configuration for an AES transformer.
@ -71,3 +73,14 @@ type Key struct {
// IdentityConfig is an empty struct to allow identity transformer in provider configuration.
type IdentityConfig struct{}
// KMS contains the name and path to configuration file for a KMS based envelope transformer.
type KMSConfig struct {
// name is the name of the KMS plugin to be used.
Name string `json:"name"`
// cacheSize is the maximum number of secrets which are cached in memory. The default value is 1000.
// +optional
CacheSize int `json:"cachesize,omitempty"`
// configfile is the path to the configuration file for the named KMS provider.
ConfigFile string `json:"configfile"`
}