google.golang.org/grpc@v1.62.1/xds/internal/xdsclient/tlscreds/bundle.go (about) 1 /* 2 * 3 * Copyright 2023 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package tlscreds implements mTLS Credentials in xDS Bootstrap File. 20 // See gRFC A65: github.com/grpc/proposal/blob/master/A65-xds-mtls-creds-in-bootstrap.md. 21 package tlscreds 22 23 import ( 24 "context" 25 "crypto/tls" 26 "encoding/json" 27 "errors" 28 "fmt" 29 "net" 30 31 "google.golang.org/grpc/credentials" 32 "google.golang.org/grpc/credentials/tls/certprovider" 33 "google.golang.org/grpc/credentials/tls/certprovider/pemfile" 34 "google.golang.org/grpc/internal/grpcsync" 35 ) 36 37 // bundle is an implementation of credentials.Bundle which implements mTLS 38 // Credentials in xDS Bootstrap File. 39 type bundle struct { 40 transportCredentials credentials.TransportCredentials 41 } 42 43 // NewBundle returns a credentials.Bundle which implements mTLS Credentials in xDS 44 // Bootstrap File. It delegates certificate loading to a file_watcher provider 45 // if either client certificates or server root CA is specified. The second 46 // return value is a close func that should be called when the caller no longer 47 // needs this bundle. 48 // See gRFC A65: github.com/grpc/proposal/blob/master/A65-xds-mtls-creds-in-bootstrap.md 49 func NewBundle(jd json.RawMessage) (credentials.Bundle, func(), error) { 50 cfg := &struct { 51 CertificateFile string `json:"certificate_file"` 52 CACertificateFile string `json:"ca_certificate_file"` 53 PrivateKeyFile string `json:"private_key_file"` 54 }{} 55 56 if jd != nil { 57 if err := json.Unmarshal(jd, cfg); err != nil { 58 return nil, nil, fmt.Errorf("failed to unmarshal config: %v", err) 59 } 60 } // Else the config field is absent. Treat it as an empty config. 61 62 if cfg.CACertificateFile == "" && cfg.CertificateFile == "" && cfg.PrivateKeyFile == "" { 63 // We cannot use (and do not need) a file_watcher provider in this case, 64 // and can simply directly use the TLS transport credentials. 65 // Quoting A65: 66 // 67 // > The only difference between the file-watcher certificate provider 68 // > config and this one is that in the file-watcher certificate 69 // > provider, at least one of the "certificate_file" or 70 // > "ca_certificate_file" fields must be specified, whereas in this 71 // > configuration, it is acceptable to specify neither one. 72 return &bundle{transportCredentials: credentials.NewTLS(&tls.Config{})}, func() {}, nil 73 } 74 // Otherwise we need to use a file_watcher provider to watch the CA, 75 // private and public keys. 76 77 // The pemfile plugin (file_watcher) currently ignores BuildOptions. 78 provider, err := certprovider.GetProvider(pemfile.PluginName, jd, certprovider.BuildOptions{}) 79 if err != nil { 80 return nil, nil, err 81 } 82 return &bundle{ 83 transportCredentials: &reloadingCreds{provider: provider}, 84 }, grpcsync.OnceFunc(func() { provider.Close() }), nil 85 } 86 87 func (t *bundle) TransportCredentials() credentials.TransportCredentials { 88 return t.transportCredentials 89 } 90 91 func (t *bundle) PerRPCCredentials() credentials.PerRPCCredentials { 92 // mTLS provides transport credentials only. There are no per-RPC 93 // credentials. 94 return nil 95 } 96 97 func (t *bundle) NewWithMode(string) (credentials.Bundle, error) { 98 // This bundle has a single mode which only uses TLS transport credentials, 99 // so there is no legitimate case where callers would call NewWithMode. 100 return nil, fmt.Errorf("xDS TLS credentials only support one mode") 101 } 102 103 // reloadingCreds is a credentials.TransportCredentials for client 104 // side mTLS that reloads the server root CA certificate and the client 105 // certificates from the provider on every client handshake. This is necessary 106 // because the standard TLS credentials do not support reloading CA 107 // certificates. 108 type reloadingCreds struct { 109 provider certprovider.Provider 110 } 111 112 func (c *reloadingCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { 113 km, err := c.provider.KeyMaterial(ctx) 114 if err != nil { 115 return nil, nil, err 116 } 117 config := &tls.Config{ 118 RootCAs: km.Roots, 119 Certificates: km.Certs, 120 } 121 return credentials.NewTLS(config).ClientHandshake(ctx, authority, rawConn) 122 } 123 124 func (c *reloadingCreds) Info() credentials.ProtocolInfo { 125 return credentials.ProtocolInfo{SecurityProtocol: "tls"} 126 } 127 128 func (c *reloadingCreds) Clone() credentials.TransportCredentials { 129 return &reloadingCreds{provider: c.provider} 130 } 131 132 func (c *reloadingCreds) OverrideServerName(string) error { 133 return errors.New("overriding server name is not supported by xDS client TLS credentials") 134 } 135 136 func (c *reloadingCreds) ServerHandshake(net.Conn) (net.Conn, credentials.AuthInfo, error) { 137 return nil, nil, errors.New("server handshake is not supported by xDS client TLS credentials") 138 }