configurationprovider.go raw

   1  package oraclecloud
   2  
   3  import (
   4  	"crypto/rsa"
   5  	"encoding/base64"
   6  	"errors"
   7  	"fmt"
   8  	"os"
   9  	"slices"
  10  	"strings"
  11  
  12  	"github.com/go-acme/lego/v4/log"
  13  	"github.com/go-acme/lego/v4/platform/config/env"
  14  	"github.com/nrdcg/oci-go-sdk/common/v1065"
  15  )
  16  
  17  type environmentConfigurationProvider struct {
  18  	values map[string]string
  19  }
  20  
  21  func newEnvironmentConfigurationProvider() (*environmentConfigurationProvider, error) {
  22  	values, err := env.GetWithFallback(
  23  		[]string{EnvRegion, altEnvTFVarRegion},
  24  		[]string{EnvUserOCID, altEnvTFVarUserOCID},
  25  		[]string{EnvTenancyOCID, altEnvTFVarTenancyOCID},
  26  		[]string{EnvPubKeyFingerprint, altEnvFingerprint, altEnvTFVarFingerprint},
  27  	)
  28  	if err != nil {
  29  		return nil, err
  30  	}
  31  
  32  	return &environmentConfigurationProvider{
  33  		values: values,
  34  	}, nil
  35  }
  36  
  37  func (p *environmentConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
  38  	privateKey, err := getPrivateKey()
  39  	if err != nil {
  40  		return nil, err
  41  	}
  42  
  43  	return common.PrivateKeyFromBytesWithPassword(privateKey, []byte(p.privateKeyPassword()))
  44  }
  45  
  46  func (p *environmentConfigurationProvider) KeyID() (string, error) {
  47  	tenancy, err := p.TenancyOCID()
  48  	if err != nil {
  49  		return "", err
  50  	}
  51  
  52  	user, err := p.UserOCID()
  53  	if err != nil {
  54  		return "", err
  55  	}
  56  
  57  	fingerprint, err := p.KeyFingerprint()
  58  	if err != nil {
  59  		return "", err
  60  	}
  61  
  62  	return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
  63  }
  64  
  65  func (p *environmentConfigurationProvider) TenancyOCID() (string, error) {
  66  	return p.values[EnvTenancyOCID], nil
  67  }
  68  
  69  func (p *environmentConfigurationProvider) UserOCID() (string, error) {
  70  	return p.values[EnvUserOCID], nil
  71  }
  72  
  73  func (p *environmentConfigurationProvider) KeyFingerprint() (string, error) {
  74  	return p.values[EnvPubKeyFingerprint], nil
  75  }
  76  
  77  func (p *environmentConfigurationProvider) Region() (string, error) {
  78  	return p.values[EnvRegion], nil
  79  }
  80  
  81  func (p *environmentConfigurationProvider) AuthType() (common.AuthConfig, error) {
  82  	// Inspired by https://github.com/oracle/oci-go-sdk/blob/e7635c292e60d0a9dcdd3a1e7de180d7c99b1eee/common/configuration.go#L231-L234
  83  	return common.AuthConfig{AuthType: common.UnknownAuthenticationType}, errors.New("unsupported, keep the interface")
  84  }
  85  
  86  func (p *environmentConfigurationProvider) privateKeyPassword() string {
  87  	return env.GetOneWithFallback(EnvPrivKeyPass, "", env.ParseString, altEnvPrivateKeyPassword, altEnvTFVarPrivateKeyPassword)
  88  }
  89  
  90  func getPrivateKey() ([]byte, error) {
  91  	base64EnvKeys := []string{envPrivKey, altEnvPrivateKey}
  92  
  93  	envVarValue := getEnvWithStrictFallback(base64EnvKeys...)
  94  	if envVarValue != "" {
  95  		bytes, err := base64.StdEncoding.DecodeString(envVarValue)
  96  		if err != nil {
  97  			return nil, fmt.Errorf("failed to read base64 value %s (defined by env vars %s): %w", envVarValue,
  98  				strings.Join(base64EnvKeys, " or "), err)
  99  		}
 100  
 101  		return bytes, nil
 102  	}
 103  
 104  	fileEnvKeys := []string{EnvPrivKeyFile, altEnvPrivateKeyPath, altEnvTFVarPrivateKeyPath}
 105  
 106  	fileVarValue := getEnvFileWithStrictFallback(fileEnvKeys...)
 107  	if len(fileVarValue) == 0 {
 108  		return nil, fmt.Errorf("no value provided for: %s",
 109  			strings.Join(slices.Concat(base64EnvKeys, fileEnvKeys), " or "),
 110  		)
 111  	}
 112  
 113  	return fileVarValue, nil
 114  }
 115  
 116  func getEnvWithStrictFallback(keys ...string) string {
 117  	for _, key := range keys {
 118  		envVarValue := os.Getenv(key)
 119  		if envVarValue != "" {
 120  			return envVarValue
 121  		}
 122  	}
 123  
 124  	return ""
 125  }
 126  
 127  func getEnvFileWithStrictFallback(keys ...string) []byte {
 128  	for _, key := range keys {
 129  		fileVarValue := os.Getenv(key)
 130  		if fileVarValue == "" {
 131  			continue
 132  		}
 133  
 134  		fileContents, err := os.ReadFile(fileVarValue)
 135  		if err != nil {
 136  			log.Printf("Failed to read the file %s (defined by env var %s): %s", fileVarValue, key, err)
 137  			return nil
 138  		}
 139  
 140  		return fileContents
 141  	}
 142  
 143  	return nil
 144  }
 145