...

Source file src/google.golang.org/grpc/internal/xds/bootstrap/tlscreds/bundle.go

Documentation: google.golang.org/grpc/internal/xds/bootstrap/tlscreds

     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  }
   139  

View as plain text