diff --git a/hack/.golint_failures b/hack/.golint_failures index a1947195ee6..e3bf2b4fb9d 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -594,6 +594,7 @@ staging/src/k8s.io/apiserver/pkg/storage/storagebackend staging/src/k8s.io/apiserver/pkg/storage/testing staging/src/k8s.io/apiserver/pkg/storage/tests staging/src/k8s.io/apiserver/pkg/storage/value +staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1 staging/src/k8s.io/apiserver/pkg/util/feature staging/src/k8s.io/apiserver/pkg/util/flag staging/src/k8s.io/apiserver/pkg/util/proxy diff --git a/hack/update-generated-kms-dockerized.sh b/hack/update-generated-kms-dockerized.sh new file mode 100755 index 00000000000..d28ab214faa --- /dev/null +++ b/hack/update-generated-kms-dockerized.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# Copyright 2018 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. +KUBE_KMS_GRPC_ROOT="${KUBE_ROOT}/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/" +source "${KUBE_ROOT}/hack/lib/init.sh" + +kube::golang::setup_env + +BINS=( + vendor/k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo +) +make -C "${KUBE_ROOT}" WHAT="${BINS[*]}" + +if [[ -z "$(which protoc)" || "$(protoc --version)" != "libprotoc 3."* ]]; then + echo "Generating protobuf requires protoc 3.0.0-beta1 or newer. Please download and" + echo "install the platform appropriate Protobuf package for your OS: " + echo + echo " https://github.com/google/protobuf/releases" + echo + echo "WARNING: Protobuf changes are not being validated" + exit 1 +fi + +function cleanup { + rm -f ${KUBE_KMS_GRPC_ROOT}/service.pb.go.bak +} + +trap cleanup EXIT + +gogopath=$(dirname $(kube::util::find-binary "protoc-gen-gogo")) + +PATH="${gogopath}:${PATH}" \ + protoc \ + --proto_path="${KUBE_KMS_GRPC_ROOT}" \ + --proto_path="${KUBE_ROOT}/vendor" \ + --gogo_out=plugins=grpc:${KUBE_KMS_GRPC_ROOT} ${KUBE_KMS_GRPC_ROOT}/service.proto + +# Update boilerplate for the generated file. +echo "$(cat hack/boilerplate/boilerplate.go.txt ${KUBE_KMS_GRPC_ROOT}/service.pb.go)" > ${KUBE_KMS_GRPC_ROOT}/service.pb.go +sed -i".bak" "s/Copyright YEAR/Copyright $(date '+%Y')/g" ${KUBE_KMS_GRPC_ROOT}/service.pb.go + +# Run gofmt to clean up the generated code. +kube::golang::verify_go_version +gofmt -l -s -w ${KUBE_KMS_GRPC_ROOT}/service.pb.go diff --git a/hack/update-generated-kms.sh b/hack/update-generated-kms.sh new file mode 100755 index 00000000000..95a0ee80ca3 --- /dev/null +++ b/hack/update-generated-kms.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Copyright 2018 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. + +# NOTE: All output from this script needs to be copied back to the calling +# source tree. This is managed in kube::build::copy_output in build/common.sh. +# If the output set is changed update that function. + +${KUBE_ROOT}/build/run.sh hack/update-generated-kms-dockerized.sh "$@" + +# ex: ts=2 sw=2 et filetype=sh diff --git a/hack/verify-generated-kms.sh b/hack/verify-generated-kms.sh new file mode 100755 index 00000000000..03738b52e04 --- /dev/null +++ b/hack/verify-generated-kms.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Copyright 2018 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. + +set -o errexit +set -o nounset +set -o pipefail + +KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. +KUBE_KMS_GRPC_ROOT="${KUBE_ROOT}/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/" +source "${KUBE_ROOT}/hack/lib/init.sh" + +kube::golang::setup_env + +function cleanup { + rm -rf ${KUBE_KMS_GRPC_ROOT}/_tmp/ +} + +trap cleanup EXIT + +mkdir -p ${KUBE_KMS_GRPC_ROOT}/_tmp +cp ${KUBE_KMS_GRPC_ROOT}/service.pb.go ${KUBE_KMS_GRPC_ROOT}/_tmp/ + +ret=0 +KUBE_VERBOSE=3 "${KUBE_ROOT}/hack/update-generated-kms.sh" +diff -I "gzipped FileDescriptorProto" -I "0x" -Naupr ${KUBE_KMS_GRPC_ROOT}/_tmp/service.pb.go ${KUBE_KMS_GRPC_ROOT}/service.pb.go || ret=$? +if [[ $ret -eq 0 ]]; then + echo "Generated KMS gRPC is up to date." + cp ${KUBE_KMS_GRPC_ROOT}/_tmp/service.pb.go ${KUBE_KMS_GRPC_ROOT}/ +else + echo "Generated KMS gRPC is out of date. Please run hack/update-generated-kms.sh" + exit 1 +fi diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD index 94de9c826c0..aebe16461ab 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/BUILD +++ b/staging/src/k8s.io/apiserver/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/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go index ad2f0927c6a..4c6fd5a39cf 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go +++ b/staging/src/k8s.io/apiserver/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 @@ -150,18 +153,18 @@ func GetPrefixTransformers(config *ResourceConfig) ([]value.PrefixTransformer, e 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) + + // 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("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) + return nil, fmt.Errorf("could not configure KMS plugin %q, error: %v", provider.KMS.Name, err) } + transformer, err = getEnvelopePrefixTransformer(provider.KMS, envelopeService, kmsTransformerPrefixV1) found = true } diff --git a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config_test.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config_test.go index ac0b3d75151..957d7dedeb9 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config_test.go +++ b/staging/src/k8s.io/apiserver/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: unix:///tmp/testprovider.sock cachesize: 10 - aescbc: keys: @@ -89,7 +82,7 @@ resources: secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY= - kms: name: testprovider - configfile: testproviderconfig + endpoint: unix:///tmp/testprovider.sock cachesize: 10 - aescbc: keys: @@ -115,7 +108,7 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== - kms: name: testprovider - configfile: testproviderconfig + endpoint: unix:///tmp/testprovider.sock cachesize: 10 - identity: {} - secretbox: @@ -149,7 +142,7 @@ resources: secret: dGhpcyBpcyBwYXNzd29yZA== - kms: name: testprovider - configfile: testproviderconfig + endpoint: unix:///tmp/testprovider.sock cachesize: 10 - identity: {} - aesgcm: @@ -169,7 +162,7 @@ resources: providers: - kms: name: testprovider - configfile: testproviderconfig + endpoint: unix:///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/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/plugins.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/plugins.go deleted file mode 100644 index 310c7238f59..00000000000 --- a/staging/src/k8s.io/apiserver/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/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go index 1603e044a31..67dfa6c9fce 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go +++ b/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/types.go @@ -81,6 +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. - ConfigFile string `json:"configfile"` + // the gRPC server listening address, for example "unix:///var/run/kms-provider.sock". + Endpoint string `json:"endpoint"` } diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD index cb5d4db5948..0984fd2088c 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/BUILD @@ -8,22 +8,35 @@ load( go_library( name = "go_default_library", - srcs = ["envelope.go"], + srcs = [ + "envelope.go", + "grpc_service.go", + ], importpath = "k8s.io/apiserver/pkg/storage/value/encrypt/envelope", deps = [ + "//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/hashicorp/golang-lru:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library", + "//vendor/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:go_default_library", ], ) go_test( name = "go_default_test", - srcs = ["envelope_test.go"], + srcs = [ + "envelope_test.go", + "grpc_service_unix_test.go", + ], embed = [":go_default_library"], importpath = "k8s.io/apiserver/pkg/storage/value/encrypt/envelope", deps = [ + "//vendor/golang.org/x/sys/unix:go_default_library", + "//vendor/google.golang.org/grpc: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/v1beta1:go_default_library", ], ) @@ -36,6 +49,9 @@ filegroup( filegroup( name = "all-srcs", - srcs = [":package-srcs"], + srcs = [ + ":package-srcs", + "//staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1:all-srcs", + ], tags = ["automanaged"], ) diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go new file mode 100644 index 00000000000..67b0f62972f --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service.go @@ -0,0 +1,144 @@ +/* +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 envelope transforms values for storage at rest using a Envelope provider +package envelope + +import ( + "fmt" + "net" + "net/url" + "time" + + "github.com/golang/glog" + + "google.golang.org/grpc" + + "golang.org/x/net/context" + + kmsapi "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1" +) + +const ( + // Now only supportied unix domain socket. + unixProtocol = "unix" + + // 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. +type gRPCService struct { + // gRPC client instance + kmsClient kmsapi.KMSServiceClient + connection *grpc.ClientConn +} + +// NewGRPCService returns an envelope.Service which use gRPC to communicate the remote KMS provider. +func NewGRPCService(endpoint string) (Service, error) { + glog.V(4).Infof("Configure KMS provider with endpoint: %s", endpoint) + + addr, err := parseEndpoint(endpoint) + if err != nil { + return nil, err + } + + 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) + } + + kmsClient := kmsapi.NewKMSServiceClient(connection) + + err = checkAPIVersion(kmsClient) + if err != nil { + connection.Close() + return nil, fmt.Errorf("failed check version for %q, error: %v", addr, err) + } + + 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, error) { + if len(endpoint) == 0 { + 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) + } + + if u.Scheme != unixProtocol { + return "", fmt.Errorf("unsupported scheme %q for remote KMS provider", u.Scheme) + } + return u.Path, nil +} + +// Check the KMS provider API version. +// 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(ctx, request) + if err != nil { + return fmt.Errorf("failed get version from remote KMS provider: %v", err) + } + if response.Version != kmsapiVersion { + return fmt.Errorf("KMS provider api version %s is not supported, only %s is supported now", + response.Version, kmsapiVersion) + } + + glog.V(4).Infof("KMS provider %s initialized, version: %s", response.RuntimeName, response.RuntimeVersion) + return nil +} + +// 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(ctx, request) + if err != nil { + return nil, err + } + return response.Plain, nil +} + +// 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(ctx, request) + if err != nil { + return nil, err + } + return response.Cipher, nil +} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service_unix_test.go b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service_unix_test.go new file mode 100644 index 00000000000..500078cb252 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/grpc_service_unix_test.go @@ -0,0 +1,173 @@ +/* +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. +*/ + +// +build !windows + +// Package envelope transforms values for storage at rest using a Envelope provider +package envelope + +import ( + "context" + "encoding/base64" + "fmt" + "net" + "os" + "reflect" + "testing" + + "google.golang.org/grpc" + + "golang.org/x/sys/unix" + + kmsapi "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1" +) + +const ( + sockFile = "/tmp/kms-provider.sock" +) + +// Normal encryption and decryption operation. +func TestGRPCService(t *testing.T) { + // Start a test gRPC server. + server, err := startTestKMSProvider() + if err != nil { + t.Fatalf("failed to start test KMS provider server, error: %v", err) + } + defer stopTestKMSProvider(server) + + // Create the gRPC client service. + endpoint := unixProtocol + "://" + sockFile + service, err := NewGRPCService(endpoint) + if err != nil { + t.Fatalf("failed to create envelope service, error: %v", err) + } + defer destroyService(service) + + // Call service to encrypt data. + data := []byte("test data") + cipher, err := service.Encrypt(data) + if err != nil { + t.Fatalf("failed when execute encrypt, error: %v", err) + } + + // Call service to decrypt data. + result, err := service.Decrypt(cipher) + if err != nil { + t.Fatalf("failed when execute decrypt, error: %v", err) + } + + if !reflect.DeepEqual(data, result) { + t.Errorf("expect: %v, but: %v", data, result) + } +} + +func destroyService(service Service) { + s := service.(*gRPCService) + s.connection.Close() +} + +// Test all those invalid configuration for KMS provider. +func TestInvalidConfiguration(t *testing.T) { + // Start a test gRPC server. + server, err := startTestKMSProvider() + if err != nil { + t.Fatalf("failed to start test KMS provider server, error: %v", err) + } + defer stopTestKMSProvider(server) + + invalidConfigs := []struct { + name string + apiVersion string + endpoint string + }{ + {"emptyConfiguration", kmsapiVersion, ""}, + {"invalidScheme", kmsapiVersion, "tcp://localhost:6060"}, + {"unavailableEndpoint", kmsapiVersion, unixProtocol + "://" + sockFile + ".nonexist"}, + {"invalidAPIVersion", "invalidVersion", unixProtocol + "://" + sockFile}, + } + + for _, testCase := range invalidConfigs { + t.Run(testCase.name, func(t *testing.T) { + setAPIVersion(testCase.apiVersion) + defer setAPIVersion(kmsapiVersion) + + _, err := NewGRPCService(testCase.endpoint) + if err == nil { + t.Fatalf("should fail to create envelope service for %s.", testCase.name) + } + }) + } +} + +// Start the gRPC server that listens on unix socket. +func startTestKMSProvider() (*grpc.Server, error) { + if err := cleanSockFile(); err != nil { + return nil, err + } + + listener, err := net.Listen(unixProtocol, sockFile) + if err != nil { + return nil, fmt.Errorf("failed to listen on the unix socket, error: %v", err) + } + + server := grpc.NewServer() + kmsapi.RegisterKMSServiceServer(server, &base64Server{}) + go server.Serve(listener) + return server, nil +} + +func stopTestKMSProvider(server *grpc.Server) { + server.Stop() + cleanSockFile() +} + +func cleanSockFile() error { + err := unix.Unlink(sockFile) + if err != nil && !os.IsNotExist(err) { + return fmt.Errorf("failed to delete the socket file, error: %v", err) + } + return nil +} + +// Fake gRPC sever for remote KMS provider. +// Use base64 to simulate encrypt and decrypt. +type base64Server struct{} + +var testProviderAPIVersion = kmsapiVersion + +func setAPIVersion(apiVersion string) { + testProviderAPIVersion = apiVersion +} + +func (s *base64Server) Version(ctx context.Context, request *kmsapi.VersionRequest) (*kmsapi.VersionResponse, error) { + return &kmsapi.VersionResponse{Version: testProviderAPIVersion, RuntimeName: "testKMS", RuntimeVersion: "0.0.1"}, nil +} + +func (s *base64Server) Decrypt(ctx context.Context, request *kmsapi.DecryptRequest) (*kmsapi.DecryptResponse, error) { + buf := make([]byte, base64.StdEncoding.DecodedLen(len(request.Cipher))) + n, err := base64.StdEncoding.Decode(buf, request.Cipher) + if err != nil { + return nil, err + } + + return &kmsapi.DecryptResponse{Plain: buf[:n]}, nil +} + +func (s *base64Server) Encrypt(ctx context.Context, request *kmsapi.EncryptRequest) (*kmsapi.EncryptResponse, error) { + buf := make([]byte, base64.StdEncoding.EncodedLen(len(request.Plain))) + base64.StdEncoding.Encode(buf, request.Plain) + return &kmsapi.EncryptResponse{Cipher: buf}, nil +} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/BUILD b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/BUILD new file mode 100644 index 00000000000..5fafc66b161 --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/BUILD @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +filegroup( + name = "go_default_library_protos", + srcs = ["service.proto"], + visibility = ["//visibility:public"], +) + +go_library( + name = "go_default_library", + srcs = ["service.pb.go"], + importpath = "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1", + visibility = ["//visibility:public"], + deps = [ + "//vendor/github.com/gogo/protobuf/proto:go_default_library", + "//vendor/golang.org/x/net/context:go_default_library", + "//vendor/google.golang.org/grpc:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.pb.go b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.pb.go new file mode 100644 index 00000000000..9e61bef00ac --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.pb.go @@ -0,0 +1,370 @@ +/* +Copyright 2018 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. +*/ + +// Code generated by protoc-gen-gogo. +// source: service.proto +// DO NOT EDIT! + +/* +Package v1beta1 is a generated protocol buffer package. + +It is generated from these files: + service.proto + +It has these top-level messages: + VersionRequest + VersionResponse + DecryptRequest + DecryptResponse + EncryptRequest + EncryptResponse +*/ +package v1beta1 + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type VersionRequest struct { + // Version of the KMS plugin API. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` +} + +func (m *VersionRequest) Reset() { *m = VersionRequest{} } +func (m *VersionRequest) String() string { return proto.CompactTextString(m) } +func (*VersionRequest) ProtoMessage() {} +func (*VersionRequest) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{0} } + +func (m *VersionRequest) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +type VersionResponse struct { + // Version of the KMS plugin API. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // Name of the KMS provider. + RuntimeName string `protobuf:"bytes,2,opt,name=runtime_name,json=runtimeName,proto3" json:"runtime_name,omitempty"` + // Version of the KMS provider. The string must be semver-compatible. + RuntimeVersion string `protobuf:"bytes,3,opt,name=runtime_version,json=runtimeVersion,proto3" json:"runtime_version,omitempty"` +} + +func (m *VersionResponse) Reset() { *m = VersionResponse{} } +func (m *VersionResponse) String() string { return proto.CompactTextString(m) } +func (*VersionResponse) ProtoMessage() {} +func (*VersionResponse) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{1} } + +func (m *VersionResponse) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *VersionResponse) GetRuntimeName() string { + if m != nil { + return m.RuntimeName + } + return "" +} + +func (m *VersionResponse) GetRuntimeVersion() string { + if m != nil { + return m.RuntimeVersion + } + return "" +} + +type DecryptRequest struct { + // Version of the KMS plugin API. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // The data to be decrypted. + Cipher []byte `protobuf:"bytes,2,opt,name=cipher,proto3" json:"cipher,omitempty"` +} + +func (m *DecryptRequest) Reset() { *m = DecryptRequest{} } +func (m *DecryptRequest) String() string { return proto.CompactTextString(m) } +func (*DecryptRequest) ProtoMessage() {} +func (*DecryptRequest) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{2} } + +func (m *DecryptRequest) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *DecryptRequest) GetCipher() []byte { + if m != nil { + return m.Cipher + } + return nil +} + +type DecryptResponse struct { + // The decrypted data. + Plain []byte `protobuf:"bytes,1,opt,name=plain,proto3" json:"plain,omitempty"` +} + +func (m *DecryptResponse) Reset() { *m = DecryptResponse{} } +func (m *DecryptResponse) String() string { return proto.CompactTextString(m) } +func (*DecryptResponse) ProtoMessage() {} +func (*DecryptResponse) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{3} } + +func (m *DecryptResponse) GetPlain() []byte { + if m != nil { + return m.Plain + } + return nil +} + +type EncryptRequest struct { + // Version of the KMS plugin API. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // The data to be encrypted. + Plain []byte `protobuf:"bytes,2,opt,name=plain,proto3" json:"plain,omitempty"` +} + +func (m *EncryptRequest) Reset() { *m = EncryptRequest{} } +func (m *EncryptRequest) String() string { return proto.CompactTextString(m) } +func (*EncryptRequest) ProtoMessage() {} +func (*EncryptRequest) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{4} } + +func (m *EncryptRequest) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *EncryptRequest) GetPlain() []byte { + if m != nil { + return m.Plain + } + return nil +} + +type EncryptResponse struct { + // The encrypted data. + Cipher []byte `protobuf:"bytes,1,opt,name=cipher,proto3" json:"cipher,omitempty"` +} + +func (m *EncryptResponse) Reset() { *m = EncryptResponse{} } +func (m *EncryptResponse) String() string { return proto.CompactTextString(m) } +func (*EncryptResponse) ProtoMessage() {} +func (*EncryptResponse) Descriptor() ([]byte, []int) { return fileDescriptorService, []int{5} } + +func (m *EncryptResponse) GetCipher() []byte { + if m != nil { + return m.Cipher + } + return nil +} + +func init() { + proto.RegisterType((*VersionRequest)(nil), "v1beta1.VersionRequest") + proto.RegisterType((*VersionResponse)(nil), "v1beta1.VersionResponse") + proto.RegisterType((*DecryptRequest)(nil), "v1beta1.DecryptRequest") + proto.RegisterType((*DecryptResponse)(nil), "v1beta1.DecryptResponse") + proto.RegisterType((*EncryptRequest)(nil), "v1beta1.EncryptRequest") + proto.RegisterType((*EncryptResponse)(nil), "v1beta1.EncryptResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for KMSService service + +type KMSServiceClient interface { + // Version returns the runtime name and runtime version of the KMS provider. + Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) + // Execute decryption operation in KMS provider. + Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) + // Execute encryption operation in KMS provider. + Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) +} + +type kMSServiceClient struct { + cc *grpc.ClientConn +} + +func NewKMSServiceClient(cc *grpc.ClientConn) KMSServiceClient { + return &kMSServiceClient{cc} +} + +func (c *kMSServiceClient) Version(ctx context.Context, in *VersionRequest, opts ...grpc.CallOption) (*VersionResponse, error) { + out := new(VersionResponse) + err := grpc.Invoke(ctx, "/v1beta1.KMSService/Version", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *kMSServiceClient) Decrypt(ctx context.Context, in *DecryptRequest, opts ...grpc.CallOption) (*DecryptResponse, error) { + out := new(DecryptResponse) + err := grpc.Invoke(ctx, "/v1beta1.KMSService/Decrypt", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *kMSServiceClient) Encrypt(ctx context.Context, in *EncryptRequest, opts ...grpc.CallOption) (*EncryptResponse, error) { + out := new(EncryptResponse) + err := grpc.Invoke(ctx, "/v1beta1.KMSService/Encrypt", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for KMSService service + +type KMSServiceServer interface { + // Version returns the runtime name and runtime version of the KMS provider. + Version(context.Context, *VersionRequest) (*VersionResponse, error) + // Execute decryption operation in KMS provider. + Decrypt(context.Context, *DecryptRequest) (*DecryptResponse, error) + // Execute encryption operation in KMS provider. + Encrypt(context.Context, *EncryptRequest) (*EncryptResponse, error) +} + +func RegisterKMSServiceServer(s *grpc.Server, srv KMSServiceServer) { + s.RegisterService(&_KMSService_serviceDesc, srv) +} + +func _KMSService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VersionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KMSServiceServer).Version(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1beta1.KMSService/Version", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KMSServiceServer).Version(ctx, req.(*VersionRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _KMSService_Decrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DecryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KMSServiceServer).Decrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1beta1.KMSService/Decrypt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KMSServiceServer).Decrypt(ctx, req.(*DecryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _KMSService_Encrypt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EncryptRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KMSServiceServer).Encrypt(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1beta1.KMSService/Encrypt", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KMSServiceServer).Encrypt(ctx, req.(*EncryptRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _KMSService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1beta1.KMSService", + HandlerType: (*KMSServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Version", + Handler: _KMSService_Version_Handler, + }, + { + MethodName: "Decrypt", + Handler: _KMSService_Decrypt_Handler, + }, + { + MethodName: "Encrypt", + Handler: _KMSService_Encrypt_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "service.proto", +} + +func init() { proto.RegisterFile("service.proto", fileDescriptorService) } + +var fileDescriptorService = []byte{ + // 279 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x52, 0xcd, 0x4a, 0xc4, 0x30, + 0x10, 0xde, 0xae, 0xb8, 0xc5, 0xb1, 0xb6, 0x10, 0x44, 0x8b, 0x27, 0xcd, 0x65, 0xd5, 0x43, 0x61, + 0xf5, 0x2e, 0x22, 0x7a, 0x12, 0x3d, 0x74, 0xc1, 0xab, 0x74, 0xcb, 0x80, 0x01, 0x9b, 0xc6, 0x24, + 0x5b, 0xf1, 0x1d, 0x7d, 0x28, 0xb1, 0x99, 0xd6, 0xb4, 0x22, 0xee, 0x71, 0x26, 0xdf, 0xdf, 0xcc, + 0x04, 0xf6, 0x0c, 0xea, 0x46, 0x94, 0x98, 0x29, 0x5d, 0xdb, 0x9a, 0x85, 0xcd, 0x62, 0x85, 0xb6, + 0x58, 0xf0, 0x73, 0x88, 0x9f, 0x50, 0x1b, 0x51, 0xcb, 0x1c, 0xdf, 0xd6, 0x68, 0x2c, 0x4b, 0x21, + 0x6c, 0x5c, 0x27, 0x0d, 0x8e, 0x83, 0xd3, 0x9d, 0xbc, 0x2b, 0xf9, 0x3b, 0x24, 0x3d, 0xd6, 0xa8, + 0x5a, 0x1a, 0xfc, 0x1b, 0xcc, 0x4e, 0x20, 0xd2, 0x6b, 0x69, 0x45, 0x85, 0xcf, 0xb2, 0xa8, 0x30, + 0x9d, 0xb6, 0xcf, 0xbb, 0xd4, 0x7b, 0x2c, 0x2a, 0x64, 0x73, 0x48, 0x3a, 0x48, 0x27, 0xb2, 0xd5, + 0xa2, 0x62, 0x6a, 0x93, 0x1b, 0xbf, 0x81, 0xf8, 0x16, 0x4b, 0xfd, 0xa1, 0xec, 0xbf, 0x21, 0xd9, + 0x01, 0xcc, 0x4a, 0xa1, 0x5e, 0x50, 0xb7, 0x8e, 0x51, 0x4e, 0x15, 0x9f, 0x43, 0xd2, 0x6b, 0x50, + 0xf8, 0x7d, 0xd8, 0x56, 0xaf, 0x85, 0x70, 0x12, 0x51, 0xee, 0x0a, 0x7e, 0x0d, 0xf1, 0x9d, 0xdc, + 0xd0, 0xac, 0x57, 0x98, 0xfa, 0x0a, 0x67, 0x90, 0xf4, 0x0a, 0x64, 0xf5, 0x93, 0x2a, 0xf0, 0x53, + 0x5d, 0x7c, 0x06, 0x00, 0xf7, 0x0f, 0xcb, 0xa5, 0x3b, 0x0e, 0xbb, 0x82, 0x90, 0x66, 0x66, 0x87, + 0x19, 0x9d, 0x28, 0x1b, 0xde, 0xe7, 0x28, 0xfd, 0xfd, 0xe0, 0x4c, 0xf8, 0xe4, 0x9b, 0x4f, 0x43, + 0x7a, 0xfc, 0xe1, 0xea, 0x3c, 0xfe, 0x68, 0x1f, 0x8e, 0x4f, 0xc9, 0x3d, 0xfe, 0x70, 0x1b, 0x1e, + 0x7f, 0x34, 0x24, 0x9f, 0xac, 0x66, 0xed, 0xef, 0xba, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x28, + 0x69, 0xfc, 0xea, 0x6e, 0x02, 0x00, 0x00, +} diff --git a/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.proto b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.proto new file mode 100644 index 00000000000..90e62acaefd --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.proto @@ -0,0 +1,54 @@ +// To regenerate service.pb.go run hack/update-generated-kms.sh +syntax = "proto3"; + +package v1beta1; + +// This service defines the public APIs for remote KMS provider. +service KMSService { + // Version returns the runtime name and runtime version of the KMS provider. + rpc Version(VersionRequest) returns (VersionResponse) {} + + // Execute decryption operation in KMS provider. + rpc Decrypt(DecryptRequest) returns (DecryptResponse) {} + // Execute encryption operation in KMS provider. + rpc Encrypt(EncryptRequest) returns (EncryptResponse) {} +} + +message VersionRequest { + // Version of the KMS plugin API. + string version = 1; +} + +message VersionResponse { + // Version of the KMS plugin API. + string version = 1; + // Name of the KMS provider. + string runtime_name = 2; + // Version of the KMS provider. The string must be semver-compatible. + string runtime_version = 3; +} + +message DecryptRequest { + // Version of the KMS plugin API. + string version = 1; + // The data to be decrypted. + bytes cipher = 2; +} + +message DecryptResponse { + // The decrypted data. + bytes plain = 1; +} + +message EncryptRequest { + // Version of the KMS plugin API. + string version = 1; + // The data to be encrypted. + bytes plain = 2; +} + +message EncryptResponse { + // The encrypted data. + bytes cipher = 1; +} +