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 }