apiserver/pkg/storage/value/encrypt/envelope/grpc_service_test.go

219 lines
5.7 KiB
Go

/*
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 (
"crypto/tls"
"encoding/base64"
"fmt"
"net"
"reflect"
"testing"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"golang.org/x/net/context"
"k8s.io/client-go/util/cert"
)
const (
// Port 0 to select any available port
listenerAddr = "127.0.0.1:0"
cafile = "testdata/ca.crt"
serverCert = "testdata/server.crt"
serverKey = "testdata/server.key"
clientCert = "testdata/client.crt"
clientKey = "testdata/client.key"
)
func TestTcpEndpoint(t *testing.T) {
// Start the gRPC server that listens on tcp socket.
listener, err := tcpListner()
if err != nil {
t.Fatal(err)
}
server := startTestKmsProvider(listener)
defer server.Stop()
endpoint := tcpProtocol + "://" + listener.Addr().String()
verifyService(t, endpoint, "", "", "")
}
func TestTlsEndpoint(t *testing.T) {
// Start the gRPC server that listens on tcp socket.
listener, err := tcpListner()
if err != nil {
t.Fatal(err)
}
tlsOption, err := tlsServerOption()
if err != nil {
t.Fatal(err)
}
server := startTestKmsProvider(listener, tlsOption)
defer server.Stop()
// There are 2 TLS case: no auth and client auth.
endpoint := tcpProtocol + "://" + listener.Addr().String()
certConfigs := []struct {
name string
serverCACert string
clientCert string
clientKey string
}{
{"noAuth", cafile, "", ""},
{"clientAuth", cafile, clientCert, clientKey},
}
for _, testCase := range certConfigs {
t.Run(testCase.name, func(t *testing.T) {
verifyService(t, endpoint, testCase.serverCACert, testCase.clientCert, testCase.clientKey)
})
}
}
func TestInvalidConfiguration(t *testing.T) {
// Start the gRPC server that listens on tcp socket.
listener, err := tcpListner()
if err != nil {
t.Fatal(err)
}
tlsOption, err := tlsServerOption()
if err != nil {
t.Fatal(err)
}
server := startTestKmsProvider(listener, tlsOption)
defer server.Stop()
endpoint := tcpProtocol + "://" + listener.Addr().String()
invalidConfigs := []struct {
name string
endpoint string
serverCACert string
clientCert string
clientKey string
}{
{"emptyConfiguration", "", "", "", ""},
{"invalidEndpoint", "http://localhost:80", "", "", ""},
{"invalidServerCACert", endpoint, "non-exits.pem", "", ""},
{"missClientKey", endpoint, cafile, clientCert, ""},
{"invalidClientCert", endpoint, cafile, "non-exists.pem", clientKey},
}
for _, testCase := range invalidConfigs {
t.Run(testCase.name, func(t *testing.T) {
_, err := NewEnvelopeService(
testCase.endpoint,
testCase.serverCACert,
testCase.clientCert,
testCase.clientKey,
)
if err == nil {
t.Fatalf("should fail to create envelope service for %s.", testCase.name)
}
})
}
}
func verifyService(t *testing.T, endpoint, serverCACert, clientCert, clientKey string) {
service, err := NewEnvelopeService(endpoint, serverCACert, clientCert, clientKey)
if err != nil {
t.Fatalf("failed to create envelope service, error: %v", err)
}
defer destroyService(service)
data := []byte("test data")
cipher, err := service.Encrypt(data)
if err != nil {
t.Fatalf("failed when execute encrypt, error: %v", err)
}
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) {
gs := service.(*gRPCService)
gs.connection.Close()
}
func tcpListner() (net.Listener, error) {
listener, err := net.Listen(tcpProtocol, listenerAddr)
if err != nil {
return nil, fmt.Errorf("failed to listen on the tcp address, error: %v", err)
}
return listener, nil
}
func startTestKmsProvider(listener net.Listener, options ...grpc.ServerOption) *grpc.Server {
server := grpc.NewServer(options...)
RegisterKmsServiceServer(server, &base64Server{})
go server.Serve(listener)
return server
}
func tlsServerOption() (grpc.ServerOption, error) {
certificate, err := tls.LoadX509KeyPair(serverCert, serverKey)
if err != nil {
return nil, fmt.Errorf("bad server cert or key, error: %v", err)
}
certPool, err := cert.NewPool(cafile)
if err != nil {
return nil, fmt.Errorf("bad ca file, error: %v", err)
}
tlsConfig := &tls.Config{
ClientAuth: tls.VerifyClientCertIfGiven,
Certificates: []tls.Certificate{certificate},
ClientCAs: certPool,
}
return grpc.Creds(credentials.NewTLS(tlsConfig)), nil
}
// Fake gRPC sever for remote KMS provider.
type base64Server struct{}
func (b *base64Server) Decrypt(ctx context.Context, request *DecryptRequest) (*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 &DecryptResponse{Plain: buf[:n]}, nil
}
func (b *base64Server) Encrypt(ctx context.Context, request *EncryptRequest) (*EncryptResponse, error) {
buf := make([]byte, base64.StdEncoding.EncodedLen(len(request.Plain)))
base64.StdEncoding.Encode(buf, request.Plain)
return &EncryptResponse{Cipher: buf}, nil
}