configuration.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. // Copyright (c) 2016, 2018, 2020, Oracle and/or its affiliates. All rights reserved.
  2. // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
  3. package common
  4. import (
  5. "crypto/rsa"
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path"
  11. "regexp"
  12. "strings"
  13. )
  14. // ConfigurationProvider wraps information about the account owner
  15. type ConfigurationProvider interface {
  16. KeyProvider
  17. TenancyOCID() (string, error)
  18. UserOCID() (string, error)
  19. KeyFingerprint() (string, error)
  20. Region() (string, error)
  21. }
  22. // IsConfigurationProviderValid Tests all parts of the configuration provider do not return an error
  23. func IsConfigurationProviderValid(conf ConfigurationProvider) (ok bool, err error) {
  24. baseFn := []func() (string, error){conf.TenancyOCID, conf.UserOCID, conf.KeyFingerprint, conf.Region, conf.KeyID}
  25. for _, fn := range baseFn {
  26. _, err = fn()
  27. ok = err == nil
  28. if err != nil {
  29. return
  30. }
  31. }
  32. _, err = conf.PrivateRSAKey()
  33. ok = err == nil
  34. if err != nil {
  35. return
  36. }
  37. return true, nil
  38. }
  39. // rawConfigurationProvider allows a user to simply construct a configuration provider from raw values.
  40. type rawConfigurationProvider struct {
  41. tenancy string
  42. user string
  43. region string
  44. fingerprint string
  45. privateKey string
  46. privateKeyPassphrase *string
  47. }
  48. // NewRawConfigurationProvider will create a ConfigurationProvider with the arguments of the function
  49. func NewRawConfigurationProvider(tenancy, user, region, fingerprint, privateKey string, privateKeyPassphrase *string) ConfigurationProvider {
  50. return rawConfigurationProvider{tenancy, user, region, fingerprint, privateKey, privateKeyPassphrase}
  51. }
  52. func (p rawConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
  53. return PrivateKeyFromBytes([]byte(p.privateKey), p.privateKeyPassphrase)
  54. }
  55. func (p rawConfigurationProvider) KeyID() (keyID string, err error) {
  56. tenancy, err := p.TenancyOCID()
  57. if err != nil {
  58. return
  59. }
  60. user, err := p.UserOCID()
  61. if err != nil {
  62. return
  63. }
  64. fingerprint, err := p.KeyFingerprint()
  65. if err != nil {
  66. return
  67. }
  68. return fmt.Sprintf("%s/%s/%s", tenancy, user, fingerprint), nil
  69. }
  70. func (p rawConfigurationProvider) TenancyOCID() (string, error) {
  71. if p.tenancy == "" {
  72. return "", fmt.Errorf("tenancy OCID can not be empty")
  73. }
  74. return p.tenancy, nil
  75. }
  76. func (p rawConfigurationProvider) UserOCID() (string, error) {
  77. if p.user == "" {
  78. return "", fmt.Errorf("user OCID can not be empty")
  79. }
  80. return p.user, nil
  81. }
  82. func (p rawConfigurationProvider) KeyFingerprint() (string, error) {
  83. if p.fingerprint == "" {
  84. return "", fmt.Errorf("fingerprint can not be empty")
  85. }
  86. return p.fingerprint, nil
  87. }
  88. func (p rawConfigurationProvider) Region() (string, error) {
  89. return canStringBeRegion(p.region)
  90. }
  91. // environmentConfigurationProvider reads configuration from environment variables
  92. type environmentConfigurationProvider struct {
  93. PrivateKeyPassword string
  94. EnvironmentVariablePrefix string
  95. }
  96. // ConfigurationProviderEnvironmentVariables creates a ConfigurationProvider from a uniform set of environment variables starting with a prefix
  97. // The env variables should look like: [prefix]_private_key_path, [prefix]_tenancy_ocid, [prefix]_user_ocid, [prefix]_fingerprint
  98. // [prefix]_region
  99. func ConfigurationProviderEnvironmentVariables(environmentVariablePrefix, privateKeyPassword string) ConfigurationProvider {
  100. return environmentConfigurationProvider{EnvironmentVariablePrefix: environmentVariablePrefix,
  101. PrivateKeyPassword: privateKeyPassword}
  102. }
  103. func (p environmentConfigurationProvider) String() string {
  104. return fmt.Sprintf("Configuration provided by environment variables prefixed with: %s", p.EnvironmentVariablePrefix)
  105. }
  106. func (p environmentConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
  107. environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "private_key_path")
  108. var ok bool
  109. var value string
  110. if value, ok = os.LookupEnv(environmentVariable); !ok {
  111. return nil, fmt.Errorf("can not read PrivateKey from env variable: %s", environmentVariable)
  112. }
  113. expandedPath := expandPath(value)
  114. pemFileContent, err := ioutil.ReadFile(expandedPath)
  115. if err != nil {
  116. Debugln("Can not read PrivateKey location from environment variable: " + environmentVariable)
  117. return
  118. }
  119. key, err = PrivateKeyFromBytes(pemFileContent, &p.PrivateKeyPassword)
  120. return
  121. }
  122. func (p environmentConfigurationProvider) KeyID() (keyID string, err error) {
  123. ocid, err := p.TenancyOCID()
  124. if err != nil {
  125. return
  126. }
  127. userocid, err := p.UserOCID()
  128. if err != nil {
  129. return
  130. }
  131. fingerprint, err := p.KeyFingerprint()
  132. if err != nil {
  133. return
  134. }
  135. return fmt.Sprintf("%s/%s/%s", ocid, userocid, fingerprint), nil
  136. }
  137. func (p environmentConfigurationProvider) TenancyOCID() (value string, err error) {
  138. environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "tenancy_ocid")
  139. var ok bool
  140. if value, ok = os.LookupEnv(environmentVariable); !ok {
  141. err = fmt.Errorf("can not read Tenancy from environment variable %s", environmentVariable)
  142. }
  143. return
  144. }
  145. func (p environmentConfigurationProvider) UserOCID() (value string, err error) {
  146. environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "user_ocid")
  147. var ok bool
  148. if value, ok = os.LookupEnv(environmentVariable); !ok {
  149. err = fmt.Errorf("can not read user id from environment variable %s", environmentVariable)
  150. }
  151. return
  152. }
  153. func (p environmentConfigurationProvider) KeyFingerprint() (value string, err error) {
  154. environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "fingerprint")
  155. var ok bool
  156. if value, ok = os.LookupEnv(environmentVariable); !ok {
  157. err = fmt.Errorf("can not read fingerprint from environment variable %s", environmentVariable)
  158. }
  159. return
  160. }
  161. func (p environmentConfigurationProvider) Region() (value string, err error) {
  162. environmentVariable := fmt.Sprintf("%s_%s", p.EnvironmentVariablePrefix, "region")
  163. var ok bool
  164. if value, ok = os.LookupEnv(environmentVariable); !ok {
  165. err = fmt.Errorf("can not read region from environment variable %s", environmentVariable)
  166. return value, err
  167. }
  168. return canStringBeRegion(value)
  169. }
  170. // fileConfigurationProvider. reads configuration information from a file
  171. type fileConfigurationProvider struct {
  172. //The path to the configuration file
  173. ConfigPath string
  174. //The password for the private key
  175. PrivateKeyPassword string
  176. //The profile for the configuration
  177. Profile string
  178. //ConfigFileInfo
  179. FileInfo *configFileInfo
  180. }
  181. // ConfigurationProviderFromFile creates a configuration provider from a configuration file
  182. // by reading the "DEFAULT" profile
  183. func ConfigurationProviderFromFile(configFilePath, privateKeyPassword string) (ConfigurationProvider, error) {
  184. if configFilePath == "" {
  185. return nil, fmt.Errorf("config file path can not be empty")
  186. }
  187. return fileConfigurationProvider{
  188. ConfigPath: configFilePath,
  189. PrivateKeyPassword: privateKeyPassword,
  190. Profile: "DEFAULT"}, nil
  191. }
  192. // ConfigurationProviderFromFileWithProfile creates a configuration provider from a configuration file
  193. // and the given profile
  194. func ConfigurationProviderFromFileWithProfile(configFilePath, profile, privateKeyPassword string) (ConfigurationProvider, error) {
  195. if configFilePath == "" {
  196. return nil, fmt.Errorf("config file path can not be empty")
  197. }
  198. return fileConfigurationProvider{
  199. ConfigPath: configFilePath,
  200. PrivateKeyPassword: privateKeyPassword,
  201. Profile: profile}, nil
  202. }
  203. type configFileInfo struct {
  204. UserOcid, Fingerprint, KeyFilePath, TenancyOcid, Region, Passphrase, SecurityTokenFilePath string
  205. PresentConfiguration byte
  206. }
  207. const (
  208. hasTenancy = 1 << iota
  209. hasUser
  210. hasFingerprint
  211. hasRegion
  212. hasKeyFile
  213. hasPassphrase
  214. hasSecurityTokenFile
  215. none
  216. )
  217. var profileRegex = regexp.MustCompile(`^\[(.*)\]`)
  218. func parseConfigFile(data []byte, profile string) (info *configFileInfo, err error) {
  219. if len(data) == 0 {
  220. return nil, fmt.Errorf("configuration file content is empty")
  221. }
  222. content := string(data)
  223. splitContent := strings.Split(content, "\n")
  224. //Look for profile
  225. for i, line := range splitContent {
  226. if match := profileRegex.FindStringSubmatch(line); match != nil && len(match) > 1 && match[1] == profile {
  227. start := i + 1
  228. return parseConfigAtLine(start, splitContent)
  229. }
  230. }
  231. return nil, fmt.Errorf("configuration file did not contain profile: %s", profile)
  232. }
  233. func parseConfigAtLine(start int, content []string) (info *configFileInfo, err error) {
  234. var configurationPresent byte
  235. info = &configFileInfo{}
  236. for i := start; i < len(content); i++ {
  237. line := content[i]
  238. if profileRegex.MatchString(line) {
  239. break
  240. }
  241. if !strings.Contains(line, "=") {
  242. continue
  243. }
  244. splits := strings.Split(line, "=")
  245. switch key, value := strings.TrimSpace(splits[0]), strings.TrimSpace(splits[1]); strings.ToLower(key) {
  246. case "passphrase", "pass_phrase":
  247. configurationPresent = configurationPresent | hasPassphrase
  248. info.Passphrase = value
  249. case "user":
  250. configurationPresent = configurationPresent | hasUser
  251. info.UserOcid = value
  252. case "fingerprint":
  253. configurationPresent = configurationPresent | hasFingerprint
  254. info.Fingerprint = value
  255. case "key_file":
  256. configurationPresent = configurationPresent | hasKeyFile
  257. info.KeyFilePath = value
  258. case "tenancy":
  259. configurationPresent = configurationPresent | hasTenancy
  260. info.TenancyOcid = value
  261. case "region":
  262. configurationPresent = configurationPresent | hasRegion
  263. info.Region = value
  264. case "security_token_file":
  265. configurationPresent = configurationPresent | hasSecurityTokenFile
  266. info.SecurityTokenFilePath = value
  267. }
  268. }
  269. info.PresentConfiguration = configurationPresent
  270. return
  271. }
  272. // cleans and expands the path if it contains a tilde , returns the expanded path or the input path as is if not expansion
  273. // was performed
  274. func expandPath(filepath string) (expandedPath string) {
  275. cleanedPath := path.Clean(filepath)
  276. expandedPath = cleanedPath
  277. if strings.HasPrefix(cleanedPath, "~") {
  278. rest := cleanedPath[2:]
  279. expandedPath = path.Join(getHomeFolder(), rest)
  280. }
  281. return
  282. }
  283. func openConfigFile(configFilePath string) (data []byte, err error) {
  284. expandedPath := expandPath(configFilePath)
  285. data, err = ioutil.ReadFile(expandedPath)
  286. if err != nil {
  287. err = fmt.Errorf("can not read config file: %s due to: %s", configFilePath, err.Error())
  288. }
  289. return
  290. }
  291. func (p fileConfigurationProvider) String() string {
  292. return fmt.Sprintf("Configuration provided by file: %s", p.ConfigPath)
  293. }
  294. func (p fileConfigurationProvider) readAndParseConfigFile() (info *configFileInfo, err error) {
  295. if p.FileInfo != nil {
  296. return p.FileInfo, nil
  297. }
  298. if p.ConfigPath == "" {
  299. return nil, fmt.Errorf("configuration path can not be empty")
  300. }
  301. data, err := openConfigFile(p.ConfigPath)
  302. if err != nil {
  303. err = fmt.Errorf("error while parsing config file: %s. Due to: %s", p.ConfigPath, err.Error())
  304. return
  305. }
  306. p.FileInfo, err = parseConfigFile(data, p.Profile)
  307. return p.FileInfo, err
  308. }
  309. func presentOrError(value string, expectedConf, presentConf byte, confMissing string) (string, error) {
  310. if presentConf&expectedConf == expectedConf {
  311. return value, nil
  312. }
  313. return "", errors.New(confMissing + " configuration is missing from file")
  314. }
  315. func (p fileConfigurationProvider) TenancyOCID() (value string, err error) {
  316. info, err := p.readAndParseConfigFile()
  317. if err != nil {
  318. err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
  319. return
  320. }
  321. value, err = presentOrError(info.TenancyOcid, hasTenancy, info.PresentConfiguration, "tenancy")
  322. return
  323. }
  324. func (p fileConfigurationProvider) UserOCID() (value string, err error) {
  325. info, err := p.readAndParseConfigFile()
  326. if err != nil {
  327. err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
  328. return
  329. }
  330. if value, err = presentOrError(info.UserOcid, hasUser, info.PresentConfiguration, "user"); err != nil {
  331. // need to check if securityTokenPath is provided, if security token is provided, userOCID can be "".
  332. if _, stErr := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration,
  333. "securityTokenPath"); stErr == nil {
  334. err = nil
  335. }
  336. }
  337. return
  338. }
  339. func (p fileConfigurationProvider) KeyFingerprint() (value string, err error) {
  340. info, err := p.readAndParseConfigFile()
  341. if err != nil {
  342. err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
  343. return
  344. }
  345. value, err = presentOrError(info.Fingerprint, hasFingerprint, info.PresentConfiguration, "fingerprint")
  346. return
  347. }
  348. func (p fileConfigurationProvider) KeyID() (keyID string, err error) {
  349. info, err := p.readAndParseConfigFile()
  350. if err != nil {
  351. err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
  352. return
  353. }
  354. if info.PresentConfiguration&hasUser == hasUser {
  355. return fmt.Sprintf("%s/%s/%s", info.TenancyOcid, info.UserOcid, info.Fingerprint), nil
  356. }
  357. if filePath, err := presentOrError(info.SecurityTokenFilePath, hasSecurityTokenFile, info.PresentConfiguration, "securityTokenFilePath"); err == nil {
  358. return getSecurityToken(filePath)
  359. }
  360. err = fmt.Errorf("can not read SecurityTokenFilePath from configuration file due to: %s", err.Error())
  361. return
  362. }
  363. func (p fileConfigurationProvider) PrivateRSAKey() (key *rsa.PrivateKey, err error) {
  364. info, err := p.readAndParseConfigFile()
  365. if err != nil {
  366. err = fmt.Errorf("can not read tenancy configuration due to: %s", err.Error())
  367. return
  368. }
  369. filePath, err := presentOrError(info.KeyFilePath, hasKeyFile, info.PresentConfiguration, "key file path")
  370. if err != nil {
  371. return
  372. }
  373. expandedPath := expandPath(filePath)
  374. pemFileContent, err := ioutil.ReadFile(expandedPath)
  375. if err != nil {
  376. err = fmt.Errorf("can not read PrivateKey from configuration file due to: %s", err.Error())
  377. return
  378. }
  379. password := p.PrivateKeyPassword
  380. if password == "" && ((info.PresentConfiguration & hasPassphrase) == hasPassphrase) {
  381. password = info.Passphrase
  382. }
  383. key, err = PrivateKeyFromBytes(pemFileContent, &password)
  384. return
  385. }
  386. func (p fileConfigurationProvider) Region() (value string, err error) {
  387. info, err := p.readAndParseConfigFile()
  388. if err != nil {
  389. err = fmt.Errorf("can not read region configuration due to: %s", err.Error())
  390. return
  391. }
  392. value, err = presentOrError(info.Region, hasRegion, info.PresentConfiguration, "region")
  393. if err != nil {
  394. val, error := getRegionFromEnvVar()
  395. if error != nil {
  396. err = fmt.Errorf("region configuration is missing from file, nor for OCI_REGION env var")
  397. return
  398. }
  399. value = val
  400. }
  401. return canStringBeRegion(value)
  402. }
  403. func getSecurityToken(filePath string) (string, error) {
  404. expandedPath := expandPath(filePath)
  405. tokenFileContent, err := ioutil.ReadFile(expandedPath)
  406. if err != nil {
  407. err = fmt.Errorf("can not read PrivateKey from configuration file due to: %s", err.Error())
  408. return "", err
  409. }
  410. return fmt.Sprintf("ST$%s", tokenFileContent), nil
  411. }
  412. // A configuration provider that look for information in multiple configuration providers
  413. type composingConfigurationProvider struct {
  414. Providers []ConfigurationProvider
  415. }
  416. // ComposingConfigurationProvider creates a composing configuration provider with the given slice of configuration providers
  417. // A composing provider will return the configuration of the first provider that has the required property
  418. // if no provider has the property it will return an error.
  419. func ComposingConfigurationProvider(providers []ConfigurationProvider) (ConfigurationProvider, error) {
  420. if len(providers) == 0 {
  421. return nil, fmt.Errorf("providers can not be an empty slice")
  422. }
  423. for i, p := range providers {
  424. if p == nil {
  425. return nil, fmt.Errorf("provider in position: %d is nil. ComposingConfiurationProvider does not support nil values", i)
  426. }
  427. }
  428. return composingConfigurationProvider{Providers: providers}, nil
  429. }
  430. func (c composingConfigurationProvider) TenancyOCID() (string, error) {
  431. for _, p := range c.Providers {
  432. val, err := p.TenancyOCID()
  433. if err == nil {
  434. return val, nil
  435. }
  436. }
  437. return "", fmt.Errorf("did not find a proper configuration for tenancy")
  438. }
  439. func (c composingConfigurationProvider) UserOCID() (string, error) {
  440. for _, p := range c.Providers {
  441. val, err := p.UserOCID()
  442. if err == nil {
  443. return val, nil
  444. }
  445. }
  446. return "", fmt.Errorf("did not find a proper configuration for user")
  447. }
  448. func (c composingConfigurationProvider) KeyFingerprint() (string, error) {
  449. for _, p := range c.Providers {
  450. val, err := p.KeyFingerprint()
  451. if err == nil {
  452. return val, nil
  453. }
  454. }
  455. return "", fmt.Errorf("did not find a proper configuration for keyFingerprint")
  456. }
  457. func (c composingConfigurationProvider) Region() (string, error) {
  458. for _, p := range c.Providers {
  459. val, err := p.Region()
  460. if err == nil {
  461. return val, nil
  462. }
  463. }
  464. if val, err := getRegionFromEnvVar(); err == nil {
  465. return val, nil
  466. }
  467. return "", fmt.Errorf("did not find a proper configuration for region, nor for OCI_REGION env var")
  468. }
  469. func (c composingConfigurationProvider) KeyID() (string, error) {
  470. for _, p := range c.Providers {
  471. val, err := p.KeyID()
  472. if err == nil {
  473. return val, nil
  474. }
  475. }
  476. return "", fmt.Errorf("did not find a proper configuration for key id")
  477. }
  478. func (c composingConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
  479. for _, p := range c.Providers {
  480. val, err := p.PrivateRSAKey()
  481. if err == nil {
  482. return val, nil
  483. }
  484. }
  485. return nil, fmt.Errorf("did not find a proper configuration for private key")
  486. }
  487. func getRegionFromEnvVar() (string, error) {
  488. regionEnvVar := "OCI_REGION"
  489. if region, existed := os.LookupEnv(regionEnvVar); existed {
  490. return region, nil
  491. }
  492. return "", fmt.Errorf("did not find OCI_REGION env var")
  493. }