From be4ee1ba371a642b0dd8593b671b9850f8fe6838 Mon Sep 17 00:00:00 2001 From: Wu Qiang Date: Fri, 26 Jan 2018 11:53:24 +0000 Subject: [PATCH] Remove configfile for kms in encryption config Kubernetes-commit: 5ae61ed386e3fbc3b7e91d343afadadd52ac027d --- pkg/server/options/encryptionconfig/BUILD | 2 - pkg/server/options/encryptionconfig/config.go | 34 ++--- .../options/encryptionconfig/config_test.go | 61 ++++----- .../options/encryptionconfig/plugins.go | 118 ------------------ pkg/server/options/encryptionconfig/types.go | 6 +- .../value/encrypt/envelope/grpc_service.go | 43 ++++--- 6 files changed, 74 insertions(+), 190 deletions(-) delete mode 100644 pkg/server/options/encryptionconfig/plugins.go diff --git a/pkg/server/options/encryptionconfig/BUILD b/pkg/server/options/encryptionconfig/BUILD index 94de9c826..aebe16461 100644 --- a/pkg/server/options/encryptionconfig/BUILD +++ b/pkg/server/options/encryptionconfig/BUILD @@ -10,13 +10,11 @@ go_library( name = "go_default_library", srcs = [ "config.go", - "plugins.go", "types.go", ], importpath = "k8s.io/apiserver/pkg/server/options/encryptionconfig", 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", diff --git a/pkg/server/options/encryptionconfig/config.go b/pkg/server/options/encryptionconfig/config.go index 650b8a37c..4c6fd5a39 100644 --- a/pkg/server/options/encryptionconfig/config.go +++ b/pkg/server/options/encryptionconfig/config.go @@ -102,6 +102,9 @@ func ParseEncryptionConfiguration(f io.Reader) (map[schema.GroupResource]value.T return result, nil } +// The factory to create kms service. This is to make writing test easier. +var envelopeServiceFactory = envelope.NewGRPCService + // GetPrefixTransformers constructs and returns the appropriate prefix transformers for the passed resource using its configuration func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, error) { var result []value.PrefixTransformer @@ -151,28 +154,15 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e return nil, fmt.Errorf("more than one provider specified in a single element, should split into different list elements") } - var envelopeService envelope.Service - if len(provider.KMS.ConfigFile) > 0 { - // There should be no KMS provider plugins on API server side in future. - 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() - pluginFound := false - 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) - } - } else { - // Get gRPC client service with endpoint. - envelopeService, err = envelope.NewGRPCService(provider.KMS.Endpoint) - if err != nil { - return nil, fmt.Errorf("could not configure KMS plugin %q, error: %v", provider.KMS.Name, err) - } + // Ensure the endpoint is provided. + if len(provider.KMS.Endpoint) == 0 { + return nil, fmt.Errorf("remote KMS provider can't use empty string as endpoint") + } + + // Get gRPC client service with endpoint. + envelopeService, err := envelopeServiceFactory(provider.KMS.Endpoint) + if err != nil { + return nil, fmt.Errorf("could not configure KMS plugin %q, error: %v", provider.KMS.Name, err) } transformer, err = getEnvelopePrefixTransformer(provider.KMS, envelopeService, kmsTransformerPrefixV1) diff --git a/pkg/server/options/encryptionconfig/config_test.go b/pkg/server/options/encryptionconfig/config_test.go index ac0b3d751..f85319bff 100644 --- a/pkg/server/options/encryptionconfig/config_test.go +++ b/pkg/server/options/encryptionconfig/config_test.go @@ -19,9 +19,6 @@ package encryptionconfig import ( "bytes" "encoding/base64" - "fmt" - "io" - "os" "strings" "testing" @@ -35,10 +32,6 @@ const ( sampleContextText = "0123456789" - // Modify these in all configurations if changed - testEnvelopeServiceConfigPath = "testproviderconfig" - testEnvelopeServiceProviderName = "testprovider" - correctConfigWithIdentityFirst = ` kind: EncryptionConfig apiVersion: v1 @@ -56,7 +49,7 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== - kms: name: testprovider - configfile: testproviderconfig + endpoint: /tmp/testprovider.sock cachesize: 10 - aescbc: keys: @@ -89,7 +82,7 @@ resources: secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= - kms: name: testprovider - configfile: testproviderconfig + endpoint: /tmp/testprovider.sock cachesize: 10 - aescbc: keys: @@ -115,7 +108,7 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== - kms: name: testprovider - configfile: testproviderconfig + endpoint: /tmp/testprovider.sock cachesize: 10 - identity: {} - secretbox: @@ -149,7 +142,7 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== - kms: name: testprovider - configfile: testproviderconfig + endpoint: /tmp/testprovider.sock cachesize: 10 - identity: {} - aesgcm: @@ -169,7 +162,7 @@ resources: providers: - kms: name: testprovider - configfile: testproviderconfig + endpoint: /tmp/testprovider.sock cachesize: 10 - secretbox: keys: @@ -218,40 +211,45 @@ resources: - name: key2 secret: YSBzZWNyZXQgYSBzZWNyZXQ= ` + + incorrectConfigNoEndpointForKMS = ` +kind: EncryptionConfig +apiVersion: v1 +resources: + - resources: + - secrets + providers: + - kms: + name: testprovider + cachesize: 10 +` ) // testEnvelopeService is a mock envelope service which can be used to simulate remote Envelope services // for testing of the envelope transformer with other transformers. type testEnvelopeService struct { - disabled bool } func (t *testEnvelopeService) Decrypt(data []byte) ([]byte, error) { - if t.disabled { - return nil, fmt.Errorf("Envelope service was disabled") - } return base64.StdEncoding.DecodeString(string(data)) } func (t *testEnvelopeService) Encrypt(data []byte) ([]byte, error) { - if t.disabled { - return nil, fmt.Errorf("Envelope service was disabled") - } return []byte(base64.StdEncoding.EncodeToString(data)), nil } -func (t *testEnvelopeService) SetDisabledStatus(status bool) { - t.disabled = status +// The factory method to create mock envelope service. +func newMockEnvelopeService(endpoint string) (envelope.Service, error) { + return &testEnvelopeService{}, nil } -var _ envelope.Service = &testEnvelopeService{} - func TestEncryptionProviderConfigCorrect(t *testing.T) { - os.OpenFile(testEnvelopeServiceConfigPath, os.O_CREATE, 0666) - defer os.Remove(testEnvelopeServiceConfigPath) - KMSPluginRegistry.Register(testEnvelopeServiceProviderName, func(_ io.Reader) (envelope.Service, error) { - return &testEnvelopeService{}, nil - }) + // Set factory for mock envelope service + factory := envelopeServiceFactory + envelopeServiceFactory = newMockEnvelopeService + defer func() { + envelopeServiceFactory = factory + }() // Creates compound/prefix transformers with different ordering of available transformers. // Transforms data using one of them, and tries to untransform using the others. @@ -337,3 +335,10 @@ func TestEncryptionProviderConfigInvalidKey(t *testing.T) { t.Fatalf("invalid configuration file (bad AES key) got parsed:\n%s", incorrectConfigInvalidKey) } } + +// Throw error if kms has no endpoint +func TestEncryptionProviderConfigNoEndpointForKMS(t *testing.T) { + if _, err := ParseEncryptionConfiguration(strings.NewReader(incorrectConfigNoEndpointForKMS)); err == nil { + t.Fatalf("invalid configuration file (kms has no endpoint) got parsed:\n%s", incorrectConfigNoEndpointForKMS) + } +} diff --git a/pkg/server/options/encryptionconfig/plugins.go b/pkg/server/options/encryptionconfig/plugins.go deleted file mode 100644 index 310c7238f..000000000 --- a/pkg/server/options/encryptionconfig/plugins.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -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) - -// KMSPlugins contains all registered KMS options. -type KMSPlugins struct { - lock sync.RWMutex - registry map[string]Factory -} - -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. It does not allow -// registering a plugin by the same name twice. -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) - } else { - ps.registry[name] = plugin - glog.V(1).Infof("Registered KMS plugin %q", name) - } -} - -// 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 -} - -// 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 -} diff --git a/pkg/server/options/encryptionconfig/types.go b/pkg/server/options/encryptionconfig/types.go index 23b37debe..67dfa6c9f 100644 --- a/pkg/server/options/encryptionconfig/types.go +++ b/pkg/server/options/encryptionconfig/types.go @@ -81,10 +81,6 @@ type KMSConfig struct { // 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. - // +optional - ConfigFile string `json:"configfile,omitempty"` // the gRPC server listening address, for example "unix:///var/run/kms-provider.sock". - // +optional - Endpoint string `json:"endpoint,omitempty"` + Endpoint string `json:"endpoint"` } diff --git a/pkg/storage/value/encrypt/envelope/grpc_service.go b/pkg/storage/value/encrypt/envelope/grpc_service.go index cf33ab05d..67b0f6297 100644 --- a/pkg/storage/value/encrypt/envelope/grpc_service.go +++ b/pkg/storage/value/encrypt/envelope/grpc_service.go @@ -38,6 +38,9 @@ const ( // Current version for the protocal interface definition. kmsapiVersion = "v1beta1" + + // The timeout that communicate with KMS server. + timeout = 30 * time.Second ) // The gRPC implementation for envelope.Service. @@ -51,16 +54,12 @@ type gRPCService struct { func NewGRPCService(endpoint string) (Service, error) { glog.V(4).Infof("Configure KMS provider with endpoint: %s", endpoint) - protocol, addr, err := parseEndpoint(endpoint) + addr, err := parseEndpoint(endpoint) if err != nil { return nil, err } - dialer := func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout(protocol, addr, timeout) - } - - connection, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithDialer(dialer)) + connection, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(timeout), grpc.WithDialer(unixDial)) if err != nil { return nil, fmt.Errorf("connect remote KMS provider %q failed, error: %v", addr, err) } @@ -76,28 +75,36 @@ func NewGRPCService(endpoint string) (Service, error) { return &gRPCService{kmsClient: kmsClient, connection: connection}, nil } +// This dialer explicitly ask gRPC to use unix socket as network. +func unixDial(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout(unixProtocol, addr, timeout) +} + // Parse the endpoint to extract schema, host or path. -func parseEndpoint(endpoint string) (string, string, error) { +func parseEndpoint(endpoint string) (string, error) { if len(endpoint) == 0 { - return "", "", fmt.Errorf("remote KMS provider can't use empty string as endpoint") + return "", fmt.Errorf("remote KMS provider can't use empty string as endpoint") } u, err := url.Parse(endpoint) if err != nil { - return "", "", fmt.Errorf("invalid endpoint %q for remote KMS provider, error: %v", endpoint, err) + return "", fmt.Errorf("invalid endpoint %q for remote KMS provider, error: %v", endpoint, err) } if u.Scheme != unixProtocol { - return "", "", fmt.Errorf("unsupported scheme %q for remote KMS provider", u.Scheme) + return "", fmt.Errorf("unsupported scheme %q for remote KMS provider", u.Scheme) } - return unixProtocol, u.Path, nil + return u.Path, nil } // Check the KMS provider API version. -// Only matching kubeRuntimeAPIVersion is supported now. +// Only matching kmsapiVersion is supported now. func checkAPIVersion(kmsClient kmsapi.KMSServiceClient) error { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + request := &kmsapi.VersionRequest{Version: kmsapiVersion} - response, err := kmsClient.Version(context.Background(), request) + response, err := kmsClient.Version(ctx, request) if err != nil { return fmt.Errorf("failed get version from remote KMS provider: %v", err) } @@ -112,8 +119,11 @@ func checkAPIVersion(kmsClient kmsapi.KMSServiceClient) error { // Decrypt a given data string to obtain the original byte data. func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + request := &kmsapi.DecryptRequest{Cipher: cipher, Version: kmsapiVersion} - response, err := g.kmsClient.Decrypt(context.Background(), request) + response, err := g.kmsClient.Decrypt(ctx, request) if err != nil { return nil, err } @@ -122,8 +132,11 @@ func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) { // Encrypt bytes to a string ciphertext. func (g *gRPCService) Encrypt(plain []byte) ([]byte, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + request := &kmsapi.EncryptRequest{Plain: plain, Version: kmsapiVersion} - response, err := g.kmsClient.Encrypt(context.Background(), request) + response, err := g.kmsClient.Encrypt(ctx, request) if err != nil { return nil, err }