k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/envelope/testing/v1beta1/kms_plugin_mock.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  /*
     5  Copyright 2017 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package v1beta1
    21  
    22  import (
    23  	"context"
    24  	"encoding/base64"
    25  	"fmt"
    26  	"net"
    27  	"os"
    28  	"sync"
    29  	"testing"
    30  	"time"
    31  
    32  	"google.golang.org/grpc"
    33  	"google.golang.org/grpc/codes"
    34  	"google.golang.org/grpc/status"
    35  
    36  	"k8s.io/apimachinery/pkg/util/wait"
    37  	"k8s.io/klog/v2"
    38  	kmsapi "k8s.io/kms/apis/v1beta1"
    39  )
    40  
    41  const (
    42  	// Now only supported unix domain socket.
    43  	unixProtocol = "unix"
    44  
    45  	// Current version for the protocol interface definition.
    46  	kmsapiVersion = "v1beta1"
    47  )
    48  
    49  // Base64Plugin gRPC sever for a mock KMS provider.
    50  // Uses base64 to simulate encrypt and decrypt.
    51  type Base64Plugin struct {
    52  	grpcServer         *grpc.Server
    53  	listener           net.Listener
    54  	mu                 *sync.Mutex
    55  	lastEncryptRequest *kmsapi.EncryptRequest
    56  	inFailedState      bool
    57  	ver                string
    58  	socketPath         string
    59  }
    60  
    61  // NewBase64Plugin is a constructor for Base64Plugin.
    62  func NewBase64Plugin(t *testing.T, socketPath string) *Base64Plugin {
    63  	server := grpc.NewServer()
    64  	result := &Base64Plugin{
    65  		grpcServer: server,
    66  		mu:         &sync.Mutex{},
    67  		ver:        kmsapiVersion,
    68  		socketPath: socketPath,
    69  	}
    70  
    71  	kmsapi.RegisterKeyManagementServiceServer(server, result)
    72  	if err := result.start(); err != nil {
    73  		t.Fatalf("failed to start KMS plugin, err: %v", err)
    74  	}
    75  	t.Cleanup(result.CleanUp)
    76  	if err := waitForBase64PluginToBeUp(result); err != nil {
    77  		t.Fatalf("failed to start KMS plugin: err: %v", err)
    78  	}
    79  	return result
    80  }
    81  
    82  // waitForBase64PluginToBeUp waits until the plugin is ready to serve requests.
    83  func waitForBase64PluginToBeUp(plugin *Base64Plugin) error {
    84  	var gRPCErr error
    85  	pollErr := wait.PollImmediate(1*time.Second, wait.ForeverTestTimeout, func() (bool, error) {
    86  		_, gRPCErr = plugin.Encrypt(context.Background(), &kmsapi.EncryptRequest{Plain: []byte("foo")})
    87  		return gRPCErr == nil, nil
    88  	})
    89  
    90  	if pollErr != nil {
    91  		return fmt.Errorf("failed to start KMS plugin, gRPC error: %v, poll error: %v", gRPCErr, pollErr)
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // LastEncryptRequest returns the last EncryptRequest.Plain sent to the plugin.
    98  func (s *Base64Plugin) LastEncryptRequest() []byte {
    99  	return s.lastEncryptRequest.Plain
   100  }
   101  
   102  // SetVersion sets the version of kms-plugin.
   103  func (s *Base64Plugin) SetVersion(ver string) {
   104  	s.ver = ver
   105  }
   106  
   107  // start starts plugin's gRPC service.
   108  func (s *Base64Plugin) start() error {
   109  	var err error
   110  	s.listener, err = net.Listen(unixProtocol, s.socketPath)
   111  	if err != nil {
   112  		return fmt.Errorf("failed to listen on the unix socket, error: %v", err)
   113  	}
   114  	klog.InfoS("Starting KMS Plugin", "socketPath", s.socketPath)
   115  
   116  	go s.grpcServer.Serve(s.listener)
   117  	return nil
   118  }
   119  
   120  // CleanUp stops gRPC server and the underlying listener.
   121  func (s *Base64Plugin) CleanUp() {
   122  	s.grpcServer.Stop()
   123  	_ = s.listener.Close()
   124  	_ = os.Remove(s.socketPath)
   125  }
   126  
   127  // EnterFailedState places the plugin into failed state.
   128  func (s *Base64Plugin) EnterFailedState() {
   129  	s.mu.Lock()
   130  	defer s.mu.Unlock()
   131  	s.inFailedState = true
   132  }
   133  
   134  // ExitFailedState removes the plugin from the failed state.
   135  func (s *Base64Plugin) ExitFailedState() {
   136  	s.mu.Lock()
   137  	defer s.mu.Unlock()
   138  	s.inFailedState = false
   139  }
   140  
   141  // Version returns the version of the kms-plugin.
   142  func (s *Base64Plugin) Version(ctx context.Context, request *kmsapi.VersionRequest) (*kmsapi.VersionResponse, error) {
   143  	klog.V(3).InfoS("Received request for Version", "request", request)
   144  	return &kmsapi.VersionResponse{Version: s.ver, RuntimeName: "testKMS", RuntimeVersion: "0.0.1"}, nil
   145  }
   146  
   147  // Decrypt performs base64 decoding of the payload of kms.DecryptRequest.
   148  func (s *Base64Plugin) Decrypt(ctx context.Context, request *kmsapi.DecryptRequest) (*kmsapi.DecryptResponse, error) {
   149  	klog.V(3).InfoS("Received Decrypt Request", "cipher", string(request.Cipher))
   150  
   151  	s.mu.Lock()
   152  	defer s.mu.Unlock()
   153  	if s.inFailedState {
   154  		return nil, status.Error(codes.FailedPrecondition, "failed precondition - key disabled")
   155  	}
   156  
   157  	buf := make([]byte, base64.StdEncoding.DecodedLen(len(request.Cipher)))
   158  	n, err := base64.StdEncoding.Decode(buf, request.Cipher)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	return &kmsapi.DecryptResponse{Plain: buf[:n]}, nil
   164  }
   165  
   166  // Encrypt performs base64 encoding of the payload of kms.EncryptRequest.
   167  func (s *Base64Plugin) Encrypt(ctx context.Context, request *kmsapi.EncryptRequest) (*kmsapi.EncryptResponse, error) {
   168  	klog.V(3).InfoS("Received Encrypt Request", "plain", string(request.Plain))
   169  	s.mu.Lock()
   170  	defer s.mu.Unlock()
   171  	s.lastEncryptRequest = request
   172  
   173  	if s.inFailedState {
   174  		return nil, status.Error(codes.FailedPrecondition, "failed precondition - key disabled")
   175  	}
   176  
   177  	buf := make([]byte, base64.StdEncoding.EncodedLen(len(request.Plain)))
   178  	base64.StdEncoding.Encode(buf, request.Plain)
   179  
   180  	return &kmsapi.EncryptResponse{Cipher: buf}, nil
   181  }