k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/envelope/kmsv2/grpc_service.go (about) 1 /* 2 Copyright 2022 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package kmsv2 transforms values for storage at rest using a Envelope provider 18 package kmsv2 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "time" 25 26 "google.golang.org/grpc" 27 "google.golang.org/grpc/credentials/insecure" 28 29 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 30 "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics" 31 "k8s.io/klog/v2" 32 kmsapi "k8s.io/kms/apis/v2" 33 kmsservice "k8s.io/kms/pkg/service" 34 "k8s.io/kms/pkg/util" 35 ) 36 37 const ( 38 // unixProtocol is the only supported protocol for remote KMS provider. 39 unixProtocol = "unix" 40 ) 41 42 // The gRPC implementation for envelope.Service. 43 type gRPCService struct { 44 kmsClient kmsapi.KeyManagementServiceClient 45 connection *grpc.ClientConn 46 callTimeout time.Duration 47 } 48 49 // NewGRPCService returns an envelope.Service which use gRPC to communicate the remote KMS provider. 50 func NewGRPCService(ctx context.Context, endpoint, providerName string, callTimeout time.Duration) (kmsservice.Service, error) { 51 klog.V(4).InfoS("Configure KMS provider", "endpoint", endpoint) 52 53 addr, err := util.ParseEndpoint(endpoint) 54 if err != nil { 55 return nil, err 56 } 57 58 s := &gRPCService{callTimeout: callTimeout} 59 s.connection, err = grpc.Dial( 60 addr, 61 grpc.WithTransportCredentials(insecure.NewCredentials()), 62 grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), 63 grpc.WithContextDialer( 64 func(context.Context, string) (net.Conn, error) { 65 // Ignoring addr and timeout arguments: 66 // addr - comes from the closure 67 c, err := net.DialUnix(unixProtocol, nil, &net.UnixAddr{Name: addr}) 68 if err != nil { 69 klog.ErrorS(err, "failed to create connection to unix socket", "addr", addr) 70 } else { 71 klog.V(4).InfoS("Successfully dialed Unix socket", "addr", addr) 72 } 73 return c, err 74 }), 75 grpc.WithChainUnaryInterceptor(recordMetricsInterceptor(providerName)), 76 ) 77 78 if err != nil { 79 return nil, fmt.Errorf("failed to create connection to %s, error: %v", endpoint, err) 80 } 81 82 s.kmsClient = kmsapi.NewKeyManagementServiceClient(s.connection) 83 84 go func() { 85 defer utilruntime.HandleCrash() 86 87 <-ctx.Done() 88 _ = s.connection.Close() 89 }() 90 91 return s, nil 92 } 93 94 // Decrypt a given data string to obtain the original byte data. 95 func (g *gRPCService) Decrypt(ctx context.Context, uid string, req *kmsservice.DecryptRequest) ([]byte, error) { 96 ctx, cancel := context.WithTimeout(ctx, g.callTimeout) 97 defer cancel() 98 99 request := &kmsapi.DecryptRequest{ 100 Ciphertext: req.Ciphertext, 101 Uid: uid, 102 KeyId: req.KeyID, 103 Annotations: req.Annotations, 104 } 105 response, err := g.kmsClient.Decrypt(ctx, request) 106 if err != nil { 107 return nil, err 108 } 109 return response.Plaintext, nil 110 } 111 112 // Encrypt bytes to a string ciphertext. 113 func (g *gRPCService) Encrypt(ctx context.Context, uid string, plaintext []byte) (*kmsservice.EncryptResponse, error) { 114 ctx, cancel := context.WithTimeout(ctx, g.callTimeout) 115 defer cancel() 116 117 request := &kmsapi.EncryptRequest{ 118 Plaintext: plaintext, 119 Uid: uid, 120 } 121 response, err := g.kmsClient.Encrypt(ctx, request) 122 if err != nil { 123 return nil, err 124 } 125 return &kmsservice.EncryptResponse{ 126 Ciphertext: response.Ciphertext, 127 KeyID: response.KeyId, 128 Annotations: response.Annotations, 129 }, nil 130 } 131 132 // Status returns the status of the KMSv2 provider. 133 func (g *gRPCService) Status(ctx context.Context) (*kmsservice.StatusResponse, error) { 134 ctx, cancel := context.WithTimeout(ctx, g.callTimeout) 135 defer cancel() 136 137 request := &kmsapi.StatusRequest{} 138 response, err := g.kmsClient.Status(ctx, request) 139 if err != nil { 140 return nil, err 141 } 142 return &kmsservice.StatusResponse{Version: response.Version, Healthz: response.Healthz, KeyID: response.KeyId}, nil 143 } 144 145 func recordMetricsInterceptor(providerName string) grpc.UnaryClientInterceptor { 146 return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 147 start := NowFunc() 148 respErr := invoker(ctx, method, req, reply, cc, opts...) 149 elapsed := NowFunc().Sub(start) 150 metrics.RecordKMSOperationLatency(providerName, method, elapsed, respErr) 151 return respErr 152 } 153 }