k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/envelope/grpc_service.go (about) 1 /* 2 Copyright 2017 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 envelope transforms values for storage at rest using a Envelope provider 18 package envelope 19 20 import ( 21 "context" 22 "fmt" 23 "net" 24 "sync" 25 "time" 26 27 "google.golang.org/grpc" 28 "google.golang.org/grpc/credentials/insecure" 29 30 utilruntime "k8s.io/apimachinery/pkg/util/runtime" 31 "k8s.io/klog/v2" 32 kmsapi "k8s.io/kms/apis/v1beta1" 33 "k8s.io/kms/pkg/util" 34 ) 35 36 const ( 37 // unixProtocol is the only supported protocol for remote KMS provider. 38 unixProtocol = "unix" 39 // Current version for the protocol interface definition. 40 kmsapiVersion = "v1beta1" 41 42 versionErrorf = "KMS provider api version %s is not supported, only %s is supported now" 43 ) 44 45 // The gRPC implementation for envelope.Service. 46 type gRPCService struct { 47 kmsClient kmsapi.KeyManagementServiceClient 48 connection *grpc.ClientConn 49 callTimeout time.Duration 50 mux sync.RWMutex 51 versionChecked bool 52 } 53 54 // NewGRPCService returns an envelope.Service which use gRPC to communicate the remote KMS provider. 55 func NewGRPCService(ctx context.Context, endpoint string, callTimeout time.Duration) (Service, error) { 56 klog.V(4).InfoS("Configure KMS provider", "endpoint", endpoint) 57 58 addr, err := util.ParseEndpoint(endpoint) 59 if err != nil { 60 return nil, err 61 } 62 63 s := &gRPCService{callTimeout: callTimeout} 64 s.connection, err = grpc.Dial( 65 addr, 66 grpc.WithTransportCredentials(insecure.NewCredentials()), 67 grpc.WithUnaryInterceptor(s.interceptor), 68 grpc.WithDefaultCallOptions(grpc.WaitForReady(true)), 69 grpc.WithContextDialer( 70 func(context.Context, string) (net.Conn, error) { 71 // Ignoring addr and timeout arguments: 72 // addr - comes from the closure 73 c, err := net.DialUnix(unixProtocol, nil, &net.UnixAddr{Name: addr}) 74 if err != nil { 75 klog.ErrorS(err, "failed to create connection to unix socket", "addr", addr) 76 } else { 77 klog.V(4).InfoS("Successfully dialed Unix socket", "addr", addr) 78 } 79 return c, err 80 })) 81 82 if err != nil { 83 return nil, fmt.Errorf("failed to create connection to %s, error: %v", endpoint, err) 84 } 85 86 s.kmsClient = kmsapi.NewKeyManagementServiceClient(s.connection) 87 88 go func() { 89 defer utilruntime.HandleCrash() 90 91 <-ctx.Done() 92 _ = s.connection.Close() 93 }() 94 95 return s, nil 96 } 97 98 func (g *gRPCService) checkAPIVersion(ctx context.Context) error { 99 g.mux.Lock() 100 defer g.mux.Unlock() 101 102 if g.versionChecked { 103 return nil 104 } 105 106 request := &kmsapi.VersionRequest{Version: kmsapiVersion} 107 response, err := g.kmsClient.Version(ctx, request) 108 if err != nil { 109 return fmt.Errorf("failed get version from remote KMS provider: %v", err) 110 } 111 if response.Version != kmsapiVersion { 112 return fmt.Errorf(versionErrorf, response.Version, kmsapiVersion) 113 } 114 g.versionChecked = true 115 116 klog.V(4).InfoS("KMS provider api version verified", "version", response.Version) 117 return nil 118 } 119 120 // Decrypt a given data string to obtain the original byte data. 121 func (g *gRPCService) Decrypt(cipher []byte) ([]byte, error) { 122 ctx, cancel := context.WithTimeout(context.Background(), g.callTimeout) 123 defer cancel() 124 125 request := &kmsapi.DecryptRequest{Cipher: cipher, Version: kmsapiVersion} 126 response, err := g.kmsClient.Decrypt(ctx, request) 127 if err != nil { 128 return nil, err 129 } 130 return response.Plain, nil 131 } 132 133 // Encrypt bytes to a string ciphertext. 134 func (g *gRPCService) Encrypt(plain []byte) ([]byte, error) { 135 ctx, cancel := context.WithTimeout(context.Background(), g.callTimeout) 136 defer cancel() 137 138 request := &kmsapi.EncryptRequest{Plain: plain, Version: kmsapiVersion} 139 response, err := g.kmsClient.Encrypt(ctx, request) 140 if err != nil { 141 return nil, err 142 } 143 return response.Cipher, nil 144 } 145 146 func (g *gRPCService) interceptor( 147 ctx context.Context, 148 method string, 149 req interface{}, 150 reply interface{}, 151 cc *grpc.ClientConn, 152 invoker grpc.UnaryInvoker, 153 opts ...grpc.CallOption, 154 ) error { 155 if !kmsapi.IsVersionCheckMethod(method) { 156 if err := g.checkAPIVersion(ctx); err != nil { 157 return err 158 } 159 } 160 161 return invoker(ctx, method, req, reply, cc, opts...) 162 }