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 }