github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/oracle/oci/client/config.go (about)

     1  package oci
     2  
     3  import (
     4  	"crypto/rand"
     5  	"crypto/rsa"
     6  	"crypto/x509"
     7  	"encoding/pem"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  
    13  	"github.com/go-ini/ini"
    14  	"github.com/mitchellh/go-homedir"
    15  )
    16  
    17  // Config API authentication and target configuration
    18  type Config struct {
    19  	// User OCID e.g. ocid1.user.oc1..aaaaaaaadcshyehbkvxl7arse3lv7z5oknexjgfhnhwidtugsxhlm4247
    20  	User string `ini:"user"`
    21  
    22  	// User's Tenancy OCID e.g. ocid1.tenancy.oc1..aaaaaaaagtgvshv6opxzjyzkupkt64ymd32n6kbomadanpcg43d
    23  	Tenancy string `ini:"tenancy"`
    24  
    25  	// Bare metal region identifier (e.g. us-phoenix-1)
    26  	Region string `ini:"region"`
    27  
    28  	// Hex key fingerprint (e.g. b5:a0:62:57:28:0d:fd:c9:59:16:eb:d4:51:9f:70:e4)
    29  	Fingerprint string `ini:"fingerprint"`
    30  
    31  	// Path to OCI config file (e.g. ~/.oci/config)
    32  	KeyFile string `ini:"key_file"`
    33  
    34  	// Passphrase used for the key, if it is encrypted.
    35  	PassPhrase string `ini:"pass_phrase"`
    36  
    37  	// Private key (loaded via LoadPrivateKey or ParsePrivateKey)
    38  	Key *rsa.PrivateKey
    39  
    40  	// Used to override base API URL.
    41  	baseURL string
    42  }
    43  
    44  // getBaseURL returns either the specified base URL or builds the appropriate
    45  // URL based on service, region, and API version.
    46  func (c *Config) getBaseURL(service string) string {
    47  	if c.baseURL != "" {
    48  		return c.baseURL
    49  	}
    50  	return fmt.Sprintf(baseURLPattern, service, c.Region, apiVersion)
    51  }
    52  
    53  // LoadConfigsFromFile loads all oracle oci configurations from a file
    54  // (generally ~/.oci/config).
    55  func LoadConfigsFromFile(path string) (map[string]*Config, error) {
    56  	if _, err := os.Stat(path); err != nil {
    57  		return nil, fmt.Errorf("Oracle OCI config file is missing: %s", path)
    58  	}
    59  
    60  	cfgFile, err := ini.Load(path)
    61  	if err != nil {
    62  		err := fmt.Errorf("Failed to parse config file %s: %s", path, err.Error())
    63  		return nil, err
    64  	}
    65  
    66  	configs := make(map[string]*Config)
    67  
    68  	// Load DEFAULT section to populate defaults for all other configs
    69  	config, err := loadConfigSection(cfgFile, "DEFAULT", nil)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	configs["DEFAULT"] = config
    74  
    75  	// Load other sections.
    76  	for _, sectionName := range cfgFile.SectionStrings() {
    77  		if sectionName == "DEFAULT" {
    78  			continue
    79  		}
    80  
    81  		// Map to Config struct with defaults from DEFAULT section.
    82  		config, err := loadConfigSection(cfgFile, sectionName, configs["DEFAULT"])
    83  		if err != nil {
    84  			return nil, err
    85  		}
    86  		configs[sectionName] = config
    87  	}
    88  
    89  	return configs, nil
    90  }
    91  
    92  // Loads an individual Config object from a ini.Section in the Oracle OCI config
    93  // file.
    94  func loadConfigSection(f *ini.File, sectionName string, config *Config) (*Config, error) {
    95  	if config == nil {
    96  		config = &Config{}
    97  	}
    98  
    99  	section, err := f.GetSection(sectionName)
   100  	if err != nil {
   101  		return nil, fmt.Errorf("Config file does not contain a %s section", sectionName)
   102  	}
   103  
   104  	if err := section.MapTo(config); err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	config.Key, err = LoadPrivateKey(config)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	return config, err
   114  }
   115  
   116  // LoadPrivateKey loads private key from disk and parses it.
   117  func LoadPrivateKey(config *Config) (*rsa.PrivateKey, error) {
   118  	// Expand '~' to $HOME
   119  	path, err := homedir.Expand(config.KeyFile)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	// Read and parse API signing key
   125  	keyContent, err := ioutil.ReadFile(path)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	key, err := ParsePrivateKey(keyContent, []byte(config.PassPhrase))
   130  
   131  	return key, err
   132  }
   133  
   134  // ParsePrivateKey parses a PEM encoded array of bytes into an rsa.PrivateKey.
   135  // Attempts to decrypt the PEM encoded array of bytes with the given password
   136  // if the PEM encoded byte array is encrypted.
   137  func ParsePrivateKey(content, password []byte) (*rsa.PrivateKey, error) {
   138  	keyBlock, _ := pem.Decode(content)
   139  
   140  	if keyBlock == nil {
   141  		return nil, errors.New("could not decode PEM private key")
   142  	}
   143  
   144  	var der []byte
   145  	var err error
   146  	if x509.IsEncryptedPEMBlock(keyBlock) {
   147  		if len(password) < 1 {
   148  			return nil, errors.New("encrypted private key but no pass phrase provided")
   149  		}
   150  		der, err = x509.DecryptPEMBlock(keyBlock, password)
   151  		if err != nil {
   152  			return nil, err
   153  		}
   154  	} else {
   155  		der = keyBlock.Bytes
   156  	}
   157  
   158  	if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
   159  		return key, nil
   160  	}
   161  
   162  	key, err := x509.ParsePKCS8PrivateKey(der)
   163  	if err == nil {
   164  		switch key := key.(type) {
   165  		case *rsa.PrivateKey:
   166  			return key, nil
   167  		default:
   168  			return nil, errors.New("Private key is not an RSA private key")
   169  		}
   170  	}
   171  	return nil, fmt.Errorf("Failed to parse private key :%s", err)
   172  }
   173  
   174  // BaseTestConfig creates the base (DEFAULT) config including a temporary key
   175  // file.
   176  // NOTE: Caller is responsible for removing temporary key file.
   177  func BaseTestConfig() (*ini.File, *os.File, error) {
   178  	keyFile, err := generateRSAKeyFile()
   179  	if err != nil {
   180  		return nil, keyFile, err
   181  	}
   182  	// Build ini
   183  	cfg := ini.Empty()
   184  	section, _ := cfg.NewSection("DEFAULT")
   185  	section.NewKey("region", "us-phoenix-1")
   186  	section.NewKey("tenancy", "ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
   187  	section.NewKey("user", "ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
   188  	section.NewKey("fingerprint", "3c:b6:44:d7:49:1a:ac:bf:de:7d:76:22:a7:f5:df:55")
   189  	section.NewKey("key_file", keyFile.Name())
   190  
   191  	return cfg, keyFile, nil
   192  }
   193  
   194  // WriteTestConfig writes a ini.File to a temporary file for use in unit tests.
   195  // NOTE: Caller is responsible for removing temporary file.
   196  func WriteTestConfig(cfg *ini.File) (*os.File, error) {
   197  	confFile, err := ioutil.TempFile("", "config_file")
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	_, err = cfg.WriteTo(confFile)
   203  	if err != nil {
   204  		os.Remove(confFile.Name())
   205  		return nil, err
   206  	}
   207  
   208  	return confFile, nil
   209  }
   210  
   211  // generateRSAKeyFile generates an RSA key file for use in unit tests.
   212  // NOTE: The caller is responsible for deleting the temporary file.
   213  func generateRSAKeyFile() (*os.File, error) {
   214  	// Create temporary file for the key
   215  	f, err := ioutil.TempFile("", "key")
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	// Generate key
   221  	priv, err := rsa.GenerateKey(rand.Reader, 2014)
   222  	if err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	// ASN.1 DER encoded form
   227  	privDer := x509.MarshalPKCS1PrivateKey(priv)
   228  	privBlk := pem.Block{
   229  		Type:    "RSA PRIVATE KEY",
   230  		Headers: nil,
   231  		Bytes:   privDer,
   232  	}
   233  
   234  	// Write the key out
   235  	if _, err := f.Write(pem.EncodeToMemory(&privBlk)); err != nil {
   236  		return nil, err
   237  	}
   238  
   239  	return f, nil
   240  }