...

Source file src/google.golang.org/api/internal/s2a.go

Documentation: google.golang.org/api/internal

     1  // Copyright 2023 Google LLC.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package internal
     6  
     7  import (
     8  	"encoding/json"
     9  	"log"
    10  	"sync"
    11  	"time"
    12  
    13  	"cloud.google.com/go/compute/metadata"
    14  )
    15  
    16  const configEndpointSuffix = "instance/platform-security/auto-mtls-configuration"
    17  
    18  // The period an MTLS config can be reused before needing refresh.
    19  var configExpiry = time.Hour
    20  
    21  // GetS2AAddress returns the S2A address to be reached via plaintext connection.
    22  func GetS2AAddress() string {
    23  	c, err := getMetadataMTLSAutoConfig().Config()
    24  	if err != nil {
    25  		return ""
    26  	}
    27  	if !c.Valid() {
    28  		return ""
    29  	}
    30  	return c.S2A.PlaintextAddress
    31  }
    32  
    33  type mtlsConfigSource interface {
    34  	Config() (*mtlsConfig, error)
    35  }
    36  
    37  // mdsMTLSAutoConfigSource is an instance of reuseMTLSConfigSource, with metadataMTLSAutoConfig as its config source.
    38  var (
    39  	mdsMTLSAutoConfigSource mtlsConfigSource
    40  	once                    sync.Once
    41  )
    42  
    43  // getMetadataMTLSAutoConfig returns mdsMTLSAutoConfigSource, which is backed by config from MDS with auto-refresh.
    44  func getMetadataMTLSAutoConfig() mtlsConfigSource {
    45  	once.Do(func() {
    46  		mdsMTLSAutoConfigSource = &reuseMTLSConfigSource{
    47  			src: &metadataMTLSAutoConfig{},
    48  		}
    49  	})
    50  	return mdsMTLSAutoConfigSource
    51  }
    52  
    53  // reuseMTLSConfigSource caches a valid version of mtlsConfig, and uses `src` to refresh upon config expiry.
    54  // It implements the mtlsConfigSource interface, so calling Config() on it returns an mtlsConfig.
    55  type reuseMTLSConfigSource struct {
    56  	src    mtlsConfigSource // src.Config() is called when config is expired
    57  	mu     sync.Mutex       // mutex guards config
    58  	config *mtlsConfig      // cached config
    59  }
    60  
    61  func (cs *reuseMTLSConfigSource) Config() (*mtlsConfig, error) {
    62  	cs.mu.Lock()
    63  	defer cs.mu.Unlock()
    64  
    65  	if cs.config.Valid() {
    66  		return cs.config, nil
    67  	}
    68  	c, err := cs.src.Config()
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	cs.config = c
    73  	return c, nil
    74  }
    75  
    76  // metadataMTLSAutoConfig is an implementation of the interface mtlsConfigSource
    77  // It has the logic to query MDS and return an mtlsConfig
    78  type metadataMTLSAutoConfig struct{}
    79  
    80  var httpGetMetadataMTLSConfig = func() (string, error) {
    81  	return metadata.Get(configEndpointSuffix)
    82  }
    83  
    84  func (cs *metadataMTLSAutoConfig) Config() (*mtlsConfig, error) {
    85  	resp, err := httpGetMetadataMTLSConfig()
    86  	if err != nil {
    87  		log.Printf("querying MTLS config from MDS endpoint failed: %v", err)
    88  		return defaultMTLSConfig(), nil
    89  	}
    90  	var config mtlsConfig
    91  	err = json.Unmarshal([]byte(resp), &config)
    92  	if err != nil {
    93  		log.Printf("unmarshalling MTLS config from MDS endpoint failed: %v", err)
    94  		return defaultMTLSConfig(), nil
    95  	}
    96  
    97  	if config.S2A == nil {
    98  		log.Printf("returned MTLS config from MDS endpoint is invalid: %v", config)
    99  		return defaultMTLSConfig(), nil
   100  	}
   101  
   102  	// set new expiry
   103  	config.Expiry = time.Now().Add(configExpiry)
   104  	return &config, nil
   105  }
   106  
   107  func defaultMTLSConfig() *mtlsConfig {
   108  	return &mtlsConfig{
   109  		S2A: &s2aAddresses{
   110  			PlaintextAddress: "",
   111  			MTLSAddress:      "",
   112  		},
   113  		Expiry: time.Now().Add(configExpiry),
   114  	}
   115  }
   116  
   117  // s2aAddresses contains the plaintext and/or MTLS S2A addresses.
   118  type s2aAddresses struct {
   119  	// PlaintextAddress is the plaintext address to reach S2A
   120  	PlaintextAddress string `json:"plaintext_address"`
   121  	// MTLSAddress is the MTLS address to reach S2A
   122  	MTLSAddress string `json:"mtls_address"`
   123  }
   124  
   125  // mtlsConfig contains the configuration for establishing MTLS connections with Google APIs.
   126  type mtlsConfig struct {
   127  	S2A    *s2aAddresses `json:"s2a"`
   128  	Expiry time.Time
   129  }
   130  
   131  func (c *mtlsConfig) Valid() bool {
   132  	return c != nil && c.S2A != nil && !c.expired()
   133  }
   134  func (c *mtlsConfig) expired() bool {
   135  	return c.Expiry.Before(time.Now())
   136  }
   137  

View as plain text