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", name = "go_default_library",
srcs = [ srcs = [
"config.go", "config.go",
"plugins.go",
"types.go", "types.go",
], ],
importpath = "k8s.io/apiserver/pkg/server/options/encryptionconfig", importpath = "k8s.io/apiserver/pkg/server/options/encryptionconfig",
deps = [ deps = [
"//vendor/github.com/ghodss/yaml:go_default_library", "//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/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/value: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/aes:go_default_library",

View File

@ -102,6 +102,9 @@ func ParseEncryptionConfiguration(f io.Reader) (map[schema.GroupResource]value.T
return result, nil 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 // GetPrefixTransformers constructs and returns the appropriate prefix transformers for the passed resource using its configuration
func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, error) { func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, error) {
var result []value.PrefixTransformer 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") return nil, fmt.Errorf("more than one provider specified in a single element, should split into different list elements")
} }
var envelopeService envelope.Service // Ensure the endpoint is provided.
if len(provider.KMS.ConfigFile) > 0 { if len(provider.KMS.Endpoint) == 0 {
// There should be no KMS provider plugins on API server side in future. return nil, fmt.Errorf("remote KMS provider can't use empty string as endpoint")
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) // Get gRPC client service with endpoint.
} envelopeService, err := envelopeServiceFactory(provider.KMS.Endpoint)
defer f.Close() if err != nil {
pluginFound := false return nil, fmt.Errorf("could not configure KMS plugin %q, error: %v", provider.KMS.Name, err)
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)
}
} }
transformer, err = getEnvelopePrefixTransformer(provider.KMS, envelopeService, kmsTransformerPrefixV1) transformer, err = getEnvelopePrefixTransformer(provider.KMS, envelopeService, kmsTransformerPrefixV1)

View File

@ -19,9 +19,6 @@ package encryptionconfig
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt"
"io"
"os"
"strings" "strings"
"testing" "testing"
@ -35,10 +32,6 @@ const (
sampleContextText = "0123456789" sampleContextText = "0123456789"
// Modify these in all configurations if changed
testEnvelopeServiceConfigPath = "testproviderconfig"
testEnvelopeServiceProviderName = "testprovider"
correctConfigWithIdentityFirst = ` correctConfigWithIdentityFirst = `
kind: EncryptionConfig kind: EncryptionConfig
apiVersion: v1 apiVersion: v1
@ -56,7 +49,7 @@ resources:
secret: dGhpcyBpcyBwYXNzd29yZA== secret: dGhpcyBpcyBwYXNzd29yZA==
- kms: - kms:
name: testprovider name: testprovider
configfile: testproviderconfig endpoint: /tmp/testprovider.sock
cachesize: 10 cachesize: 10
- aescbc: - aescbc:
keys: keys:
@ -89,7 +82,7 @@ resources:
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- kms: - kms:
name: testprovider name: testprovider
configfile: testproviderconfig endpoint: /tmp/testprovider.sock
cachesize: 10 cachesize: 10
- aescbc: - aescbc:
keys: keys:
@ -115,7 +108,7 @@ resources:
secret: dGhpcyBpcyBwYXNzd29yZA== secret: dGhpcyBpcyBwYXNzd29yZA==
- kms: - kms:
name: testprovider name: testprovider
configfile: testproviderconfig endpoint: /tmp/testprovider.sock
cachesize: 10 cachesize: 10
- identity: {} - identity: {}
- secretbox: - secretbox:
@ -149,7 +142,7 @@ resources:
secret: dGhpcyBpcyBwYXNzd29yZA== secret: dGhpcyBpcyBwYXNzd29yZA==
- kms: - kms:
name: testprovider name: testprovider
configfile: testproviderconfig endpoint: /tmp/testprovider.sock
cachesize: 10 cachesize: 10
- identity: {} - identity: {}
- aesgcm: - aesgcm:
@ -169,7 +162,7 @@ resources:
providers: providers:
- kms: - kms:
name: testprovider name: testprovider
configfile: testproviderconfig endpoint: /tmp/testprovider.sock
cachesize: 10 cachesize: 10
- secretbox: - secretbox:
keys: keys:
@ -218,40 +211,45 @@ resources:
- name: key2 - name: key2
secret: YSBzZWNyZXQgYSBzZWNyZXQ= 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 // testEnvelopeService is a mock envelope service which can be used to simulate remote Envelope services
// for testing of the envelope transformer with other transformers. // for testing of the envelope transformer with other transformers.
type testEnvelopeService struct { type testEnvelopeService struct {
disabled bool
} }
func (t *testEnvelopeService) Decrypt(data []byte) ([]byte, error) { 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)) return base64.StdEncoding.DecodeString(string(data))
} }
func (t *testEnvelopeService) Encrypt(data []byte) ([]byte, error) { 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 return []byte(base64.StdEncoding.EncodeToString(data)), nil
} }
func (t *testEnvelopeService) SetDisabledStatus(status bool) { // The factory method to create mock envelope service.
t.disabled = status func newMockEnvelopeService(endpoint string) (envelope.Service, error) {
return &testEnvelopeService{}, nil
} }
var _ envelope.Service = &testEnvelopeService{}
func TestEncryptionProviderConfigCorrect(t *testing.T) { func TestEncryptionProviderConfigCorrect(t *testing.T) {
os.OpenFile(testEnvelopeServiceConfigPath, os.O_CREATE, 0666) // Set factory for mock envelope service
defer os.Remove(testEnvelopeServiceConfigPath) factory := envelopeServiceFactory
KMSPluginRegistry.Register(testEnvelopeServiceProviderName, func(_ io.Reader) (envelope.Service, error) { envelopeServiceFactory = newMockEnvelopeService
return &testEnvelopeService{}, nil defer func() {
}) envelopeServiceFactory = factory
}()
// Creates compound/prefix transformers with different ordering of available transformers. // Creates compound/prefix transformers with different ordering of available transformers.
// Transforms data using one of them, and tries to untransform using the others. // 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) 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. // cacheSize is the maximum number of secrets which are cached in memory. The default value is 1000.
// +optional // +optional
CacheSize int `json:"cachesize,omitempty"` 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". // the gRPC server listening address, for example "unix:///var/run/kms-provider.sock".
// +optional Endpoint string `json:"endpoint"`
Endpoint string `json:"endpoint,omitempty"`
} }

View File

@ -38,6 +38,9 @@ const (
// Current version for the protocal interface definition. // Current version for the protocal interface definition.
kmsapiVersion = "v1beta1" kmsapiVersion = "v1beta1"
// The timeout that communicate with KMS server.
timeout = 30 * time.Second
) )
// The gRPC implementation for envelope.Service. // The gRPC implementation for envelope.Service.
@ -51,16 +54,12 @@ type gRPCService struct {
func NewGRPCService(endpoint string) (Service, error) { func NewGRPCService(endpoint string) (Service, error) {
glog.V(4).Infof("Configure KMS provider with endpoint: %s", endpoint) glog.V(4).Infof("Configure KMS provider with endpoint: %s", endpoint)
protocol, addr, err := parseEndpoint(endpoint) addr, err := parseEndpoint(endpoint)
if err != nil { if err != nil {
return nil, err return nil, err
} }
dialer := func(addr string, timeout time.Duration) (net.Conn, error) { connection, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithTimeout(timeout), grpc.WithDialer(unixDial))
return net.DialTimeout(protocol, addr, timeout)
}
connection, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithDialer(dialer))
if err != nil { if err != nil {
return nil, fmt.Errorf("connect remote KMS provider %q failed, error: %v", addr, err) 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 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. // 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 { 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) u, err := url.Parse(endpoint)
if err != nil { 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 { 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. // Check the KMS provider API version.
// Only matching kubeRuntimeAPIVersion is supported now. // Only matching kmsapiVersion is supported now.
func checkAPIVersion(kmsClient kmsapi.KMSServiceClient) error { func checkAPIVersion(kmsClient kmsapi.KMSServiceClient) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
request := &kmsapi.VersionRequest{Version: kmsapiVersion} request := &kmsapi.VersionRequest{Version: kmsapiVersion}
response, err := kmsClient.Version(context.Background(), request) response, err := kmsClient.Version(ctx, request)
if err != nil { if err != nil {
return fmt.Errorf("failed get version from remote KMS provider: %v", err) 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. // Decrypt a given data string to obtain the original byte data.
func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) { func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
request := &kmsapi.DecryptRequest{Cipher: cipher, Version: kmsapiVersion} 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 { if err != nil {
return nil, err return nil, err
} }
@ -122,8 +132,11 @@ func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) {
// Encrypt bytes to a string ciphertext. // Encrypt bytes to a string ciphertext.
func (g *gRPCService) Encrypt(plain []byte) ([]byte, error) { func (g *gRPCService) Encrypt(plain []byte) ([]byte, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
request := &kmsapi.EncryptRequest{Plain: plain, Version: kmsapiVersion} 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 { if err != nil {
return nil, err return nil, err
} }