Remove configfile for kms in encryption config

Kubernetes-commit: 5ae61ed386e3fbc3b7e91d343afadadd52ac027d
This commit is contained in:
Wu Qiang 2018-01-26 11:53:24 +00:00 committed by Kubernetes Publisher
parent a32d2bb427
commit be4ee1ba37
6 changed files with 74 additions and 190 deletions

View File

@ -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",

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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"`
}

View File

@ -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
}