added instance principals support for oci builder
This commit is contained in:
parent
1c27d9d04b
commit
0f2cb45fc6
|
@ -19,6 +19,7 @@ import (
|
||||||
"github.com/hashicorp/packer/packer"
|
"github.com/hashicorp/packer/packer"
|
||||||
"github.com/hashicorp/packer/template/interpolate"
|
"github.com/hashicorp/packer/template/interpolate"
|
||||||
ocicommon "github.com/oracle/oci-go-sdk/common"
|
ocicommon "github.com/oracle/oci-go-sdk/common"
|
||||||
|
ociauth "github.com/oracle/oci-go-sdk/common/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -27,6 +28,18 @@ type Config struct {
|
||||||
|
|
||||||
configProvider ocicommon.ConfigurationProvider
|
configProvider ocicommon.ConfigurationProvider
|
||||||
|
|
||||||
|
// Instance Principals (OPTIONAL)
|
||||||
|
// If set to true the following can't have non empty values
|
||||||
|
// - AccessCfgFile
|
||||||
|
// - AccessCfgFileAccount
|
||||||
|
// - UserID
|
||||||
|
// - TenancyID
|
||||||
|
// - Region
|
||||||
|
// - Fingerprint
|
||||||
|
// - KeyFile
|
||||||
|
// - PassPhrase
|
||||||
|
InstancePrincipals bool `mapstructure:"use_instance_principals"`
|
||||||
|
|
||||||
AccessCfgFile string `mapstructure:"access_cfg_file"`
|
AccessCfgFile string `mapstructure:"access_cfg_file"`
|
||||||
AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"`
|
AccessCfgFileAccount string `mapstructure:"access_cfg_file_account"`
|
||||||
|
|
||||||
|
@ -86,6 +99,60 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||||
return fmt.Errorf("Failed to mapstructure Config: %+v", err)
|
return fmt.Errorf("Failed to mapstructure Config: %+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errs *packer.MultiError
|
||||||
|
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
||||||
|
errs = packer.MultiErrorAppend(errs, es...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenancyOCID string
|
||||||
|
|
||||||
|
if c.InstancePrincipals {
|
||||||
|
// We could go through all keys in one go and report that the below set
|
||||||
|
// of keys cannot coexist with use_instance_principals but decided to
|
||||||
|
// split them and report them seperately so that the user sees the specific
|
||||||
|
// key involved.
|
||||||
|
var message string = " cannot be present when use_instance_principals is set to true."
|
||||||
|
if c.AccessCfgFile != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file"+message))
|
||||||
|
}
|
||||||
|
if c.AccessCfgFileAccount != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("access_cfg_file_account"+message))
|
||||||
|
}
|
||||||
|
if c.UserID != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("user_ocid"+message))
|
||||||
|
}
|
||||||
|
if c.TenancyID != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("tenancy_ocid"+message))
|
||||||
|
}
|
||||||
|
if c.Region != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("region"+message))
|
||||||
|
}
|
||||||
|
if c.Fingerprint != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("fingerprint"+message))
|
||||||
|
}
|
||||||
|
if c.KeyFile != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("key_file"+message))
|
||||||
|
}
|
||||||
|
if c.PassPhrase != "" {
|
||||||
|
errs = packer.MultiErrorAppend(errs, fmt.Errorf("pass_phrase"+message))
|
||||||
|
}
|
||||||
|
// This check is used to facilitate testing. During testing a Mock struct
|
||||||
|
// is assigned to c.configProvider otherwise testing fails because Instance
|
||||||
|
// Principals cannot be obtained.
|
||||||
|
if c.configProvider == nil {
|
||||||
|
// Even though the previous configuraion checks might fail we don't want
|
||||||
|
// to skip this step. It seems that the logic behind the checks in this
|
||||||
|
// file is to check everything even getting the configProvider.
|
||||||
|
c.configProvider, err = ociauth.InstancePrincipalConfigurationProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tenancyOCID, err = c.configProvider.TenancyOCID()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Determine where the SDK config is located
|
// Determine where the SDK config is located
|
||||||
if c.AccessCfgFile == "" {
|
if c.AccessCfgFile == "" {
|
||||||
c.AccessCfgFile, err = getDefaultOCISettingsPath()
|
c.AccessCfgFile, err = getDefaultOCISettingsPath()
|
||||||
|
@ -137,17 +204,12 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var errs *packer.MultiError
|
|
||||||
if es := c.Comm.Prepare(&c.ctx); len(es) > 0 {
|
|
||||||
errs = packer.MultiErrorAppend(errs, es...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if userOCID, _ := configProvider.UserOCID(); userOCID == "" {
|
if userOCID, _ := configProvider.UserOCID(); userOCID == "" {
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
errs, errors.New("'user_ocid' must be specified"))
|
errs, errors.New("'user_ocid' must be specified"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tenancyOCID, _ := configProvider.TenancyOCID()
|
tenancyOCID, _ = configProvider.TenancyOCID()
|
||||||
if tenancyOCID == "" {
|
if tenancyOCID == "" {
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
errs, errors.New("'tenancy_ocid' must be specified"))
|
errs, errors.New("'tenancy_ocid' must be specified"))
|
||||||
|
@ -164,6 +226,7 @@ func (c *Config) Prepare(raws ...interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.configProvider = configProvider
|
c.configProvider = configProvider
|
||||||
|
}
|
||||||
|
|
||||||
if c.AvailabilityDomain == "" {
|
if c.AvailabilityDomain == "" {
|
||||||
errs = packer.MultiErrorAppend(
|
errs = packer.MultiErrorAppend(
|
||||||
|
|
|
@ -56,6 +56,7 @@ type FlatConfig struct {
|
||||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl"`
|
||||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure"`
|
||||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm"`
|
||||||
|
InstancePrincipals *bool `mapstructure:"use_instance_principals" cty:"use_instance_principals"`
|
||||||
AccessCfgFile *string `mapstructure:"access_cfg_file" cty:"access_cfg_file"`
|
AccessCfgFile *string `mapstructure:"access_cfg_file" cty:"access_cfg_file"`
|
||||||
AccessCfgFileAccount *string `mapstructure:"access_cfg_file_account" cty:"access_cfg_file_account"`
|
AccessCfgFileAccount *string `mapstructure:"access_cfg_file_account" cty:"access_cfg_file_account"`
|
||||||
UserID *string `mapstructure:"user_ocid" cty:"user_ocid"`
|
UserID *string `mapstructure:"user_ocid" cty:"user_ocid"`
|
||||||
|
@ -138,6 +139,7 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||||
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||||
|
"use_instance_principals": &hcldec.AttrSpec{Name: "use_instance_principals", Type: cty.Bool, Required: false},
|
||||||
"access_cfg_file": &hcldec.AttrSpec{Name: "access_cfg_file", Type: cty.String, Required: false},
|
"access_cfg_file": &hcldec.AttrSpec{Name: "access_cfg_file", Type: cty.String, Required: false},
|
||||||
"access_cfg_file_account": &hcldec.AttrSpec{Name: "access_cfg_file_account", Type: cty.String, Required: false},
|
"access_cfg_file_account": &hcldec.AttrSpec{Name: "access_cfg_file_account", Type: cty.String, Required: false},
|
||||||
"user_ocid": &hcldec.AttrSpec{Name: "user_ocid", Type: cty.String, Required: false},
|
"user_ocid": &hcldec.AttrSpec{Name: "user_ocid", Type: cty.String, Required: false},
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
func testConfig(accessConfFile *os.File) map[string]interface{} {
|
func testConfig(accessConfFile *os.File) map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
|
|
||||||
"availability_domain": "aaaa:PHX-AD-3",
|
"availability_domain": "aaaa:PHX-AD-3",
|
||||||
"access_cfg_file": accessConfFile.Name(),
|
"access_cfg_file": accessConfFile.Name(),
|
||||||
|
|
||||||
|
@ -252,6 +253,36 @@ func TestConfig(t *testing.T) {
|
||||||
t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
|
t.Errorf("Expected ConfigProvider.KeyFingerprint: %s, got %s", expected, fingerprint)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Test the correct errors are produced when certain template keys
|
||||||
|
// are present alongside use_instance_principals key.
|
||||||
|
invalidKeys := []string{
|
||||||
|
"access_cfg_file",
|
||||||
|
"access_cfg_file_account",
|
||||||
|
"user_ocid",
|
||||||
|
"tenancy_ocid",
|
||||||
|
"region",
|
||||||
|
"fingerprint",
|
||||||
|
"key_file",
|
||||||
|
"pass_phrase",
|
||||||
|
}
|
||||||
|
for _, k := range invalidKeys {
|
||||||
|
t.Run(k+"_mixed_with_use_instance_principals", func(t *testing.T) {
|
||||||
|
raw := testConfig(cfgFile)
|
||||||
|
raw["use_instance_principals"] = "true"
|
||||||
|
raw[k] = "some_random_value"
|
||||||
|
|
||||||
|
var c Config
|
||||||
|
|
||||||
|
c.configProvider = instancePrincipalConfigurationProviderMock{}
|
||||||
|
|
||||||
|
errs := c.Prepare(raw)
|
||||||
|
|
||||||
|
if !strings.Contains(errs.Error(), k) {
|
||||||
|
t.Errorf("Expected '%s' to contain '%s'", errs.Error(), k)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BaseTestConfig creates the base (DEFAULT) config including a temporary key
|
// BaseTestConfig creates the base (DEFAULT) config including a temporary key
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package oci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mock struct to be used during testing to obtain Instance Principals.
|
||||||
|
type instancePrincipalConfigurationProviderMock struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) PrivateRSAKey() (*rsa.PrivateKey, error) {
|
||||||
|
return rsa.GenerateKey(rand.Reader, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) KeyID() (string, error) {
|
||||||
|
return "some_random_key_id", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) TenancyOCID() (string, error) {
|
||||||
|
return "some_random_tenancy", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) UserOCID() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) KeyFingerprint() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProviderMock) Region() (string, error) {
|
||||||
|
return "some_random_region", nil
|
||||||
|
}
|
158
vendor/github.com/oracle/oci-go-sdk/common/auth/certificate_retriever.go
generated
vendored
Normal file
158
vendor/github.com/oracle/oci-go-sdk/common/auth/certificate_retriever.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/oracle/oci-go-sdk/common"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// x509CertificateRetriever provides an X509 certificate with the RSA private key
|
||||||
|
type x509CertificateRetriever interface {
|
||||||
|
Refresh() error
|
||||||
|
CertificatePemRaw() []byte
|
||||||
|
Certificate() *x509.Certificate
|
||||||
|
PrivateKeyPemRaw() []byte
|
||||||
|
PrivateKey() *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// urlBasedX509CertificateRetriever retrieves PEM-encoded X509 certificates from the given URLs.
|
||||||
|
type urlBasedX509CertificateRetriever struct {
|
||||||
|
certURL string
|
||||||
|
privateKeyURL string
|
||||||
|
passphrase string
|
||||||
|
certificatePemRaw []byte
|
||||||
|
certificate *x509.Certificate
|
||||||
|
privateKeyPemRaw []byte
|
||||||
|
privateKey *rsa.PrivateKey
|
||||||
|
mux sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newURLBasedX509CertificateRetriever(certURL, privateKeyURL, passphrase string) x509CertificateRetriever {
|
||||||
|
return &urlBasedX509CertificateRetriever{
|
||||||
|
certURL: certURL,
|
||||||
|
privateKeyURL: privateKeyURL,
|
||||||
|
passphrase: passphrase,
|
||||||
|
mux: sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh() is failure atomic, i.e., CertificatePemRaw(), Certificate(), PrivateKeyPemRaw(), and PrivateKey() would
|
||||||
|
// return their previous values if Refresh() fails.
|
||||||
|
func (r *urlBasedX509CertificateRetriever) Refresh() error {
|
||||||
|
common.Debugln("Refreshing certificate")
|
||||||
|
|
||||||
|
r.mux.Lock()
|
||||||
|
defer r.mux.Unlock()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
var certificatePemRaw []byte
|
||||||
|
var certificate *x509.Certificate
|
||||||
|
if certificatePemRaw, certificate, err = r.renewCertificate(r.certURL); err != nil {
|
||||||
|
return fmt.Errorf("failed to renew certificate: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var privateKeyPemRaw []byte
|
||||||
|
var privateKey *rsa.PrivateKey
|
||||||
|
if r.privateKeyURL != "" {
|
||||||
|
if privateKeyPemRaw, privateKey, err = r.renewPrivateKey(r.privateKeyURL, r.passphrase); err != nil {
|
||||||
|
return fmt.Errorf("failed to renew private key: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r.certificatePemRaw = certificatePemRaw
|
||||||
|
r.certificate = certificate
|
||||||
|
r.privateKeyPemRaw = privateKeyPemRaw
|
||||||
|
r.privateKey = privateKey
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) renewCertificate(url string) (certificatePemRaw []byte, certificate *x509.Certificate, err error) {
|
||||||
|
var body bytes.Buffer
|
||||||
|
if body, err = httpGet(url); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to get certificate from %s: %s", url, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
certificatePemRaw = body.Bytes()
|
||||||
|
var block *pem.Block
|
||||||
|
block, _ = pem.Decode(certificatePemRaw)
|
||||||
|
if block == nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to parse the new certificate, not valid pem data")
|
||||||
|
}
|
||||||
|
|
||||||
|
if certificate, err = x509.ParseCertificate(block.Bytes); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to parse the new certificate: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificatePemRaw, certificate, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) renewPrivateKey(url, passphrase string) (privateKeyPemRaw []byte, privateKey *rsa.PrivateKey, err error) {
|
||||||
|
var body bytes.Buffer
|
||||||
|
if body, err = httpGet(url); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to get private key from %s: %s", url, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
privateKeyPemRaw = body.Bytes()
|
||||||
|
if privateKey, err = common.PrivateKeyFromBytes(privateKeyPemRaw, &passphrase); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failed to parse the new private key: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKeyPemRaw, privateKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) CertificatePemRaw() []byte {
|
||||||
|
r.mux.Lock()
|
||||||
|
defer r.mux.Unlock()
|
||||||
|
|
||||||
|
if r.certificatePemRaw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make([]byte, len(r.certificatePemRaw))
|
||||||
|
copy(c, r.certificatePemRaw)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) Certificate() *x509.Certificate {
|
||||||
|
r.mux.Lock()
|
||||||
|
defer r.mux.Unlock()
|
||||||
|
|
||||||
|
if r.certificate == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := *r.certificate
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) PrivateKeyPemRaw() []byte {
|
||||||
|
r.mux.Lock()
|
||||||
|
defer r.mux.Unlock()
|
||||||
|
|
||||||
|
if r.privateKeyPemRaw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make([]byte, len(r.privateKeyPemRaw))
|
||||||
|
copy(c, r.privateKeyPemRaw)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *urlBasedX509CertificateRetriever) PrivateKey() *rsa.PrivateKey {
|
||||||
|
r.mux.Lock()
|
||||||
|
defer r.mux.Unlock()
|
||||||
|
|
||||||
|
if r.privateKey == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := *r.privateKey
|
||||||
|
return &c
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"github.com/oracle/oci-go-sdk/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type instancePrincipalConfigurationProvider struct {
|
||||||
|
keyProvider *instancePrincipalKeyProvider
|
||||||
|
region *common.Region
|
||||||
|
}
|
||||||
|
|
||||||
|
//InstancePrincipalConfigurationProvider returns a configuration for instance principals
|
||||||
|
func InstancePrincipalConfigurationProvider() (common.ConfigurationProvider, error) {
|
||||||
|
var err error
|
||||||
|
var keyProvider *instancePrincipalKeyProvider
|
||||||
|
if keyProvider, err = newInstancePrincipalKeyProvider(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create a new key provider for instance principal: %s", err.Error())
|
||||||
|
}
|
||||||
|
return instancePrincipalConfigurationProvider{keyProvider: keyProvider, region: nil}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//InstancePrincipalConfigurationProviderForRegion returns a configuration for instance principals with a given region
|
||||||
|
func InstancePrincipalConfigurationProviderForRegion(region common.Region) (common.ConfigurationProvider, error) {
|
||||||
|
var err error
|
||||||
|
var keyProvider *instancePrincipalKeyProvider
|
||||||
|
if keyProvider, err = newInstancePrincipalKeyProvider(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create a new key provider for instance principal: %s", err.Error())
|
||||||
|
}
|
||||||
|
return instancePrincipalConfigurationProvider{keyProvider: keyProvider, region: ®ion}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) PrivateRSAKey() (*rsa.PrivateKey, error) {
|
||||||
|
return p.keyProvider.PrivateRSAKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) KeyID() (string, error) {
|
||||||
|
return p.keyProvider.KeyID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) TenancyOCID() (string, error) {
|
||||||
|
return p.keyProvider.TenancyOCID()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) UserOCID() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) KeyFingerprint() (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p instancePrincipalConfigurationProvider) Region() (string, error) {
|
||||||
|
if p.region == nil {
|
||||||
|
return string(p.keyProvider.RegionForFederationClient()), nil
|
||||||
|
}
|
||||||
|
return string(*p.region), nil
|
||||||
|
}
|
292
vendor/github.com/oracle/oci-go-sdk/common/auth/federation_client.go
generated
vendored
Normal file
292
vendor/github.com/oracle/oci-go-sdk/common/auth/federation_client.go
generated
vendored
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
// Package auth provides supporting functions and structs for authentication
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/oracle/oci-go-sdk/common"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// federationClient is a client to retrieve the security token for an instance principal necessary to sign a request.
|
||||||
|
// It also provides the private key whose corresponding public key is used to retrieve the security token.
|
||||||
|
type federationClient interface {
|
||||||
|
PrivateKey() (*rsa.PrivateKey, error)
|
||||||
|
SecurityToken() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// x509FederationClient retrieves a security token from Auth service.
|
||||||
|
type x509FederationClient struct {
|
||||||
|
tenancyID string
|
||||||
|
sessionKeySupplier sessionKeySupplier
|
||||||
|
leafCertificateRetriever x509CertificateRetriever
|
||||||
|
intermediateCertificateRetrievers []x509CertificateRetriever
|
||||||
|
securityToken securityToken
|
||||||
|
authClient *common.BaseClient
|
||||||
|
mux sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func newX509FederationClient(region common.Region, tenancyID string, leafCertificateRetriever x509CertificateRetriever, intermediateCertificateRetrievers []x509CertificateRetriever) federationClient {
|
||||||
|
client := &x509FederationClient{
|
||||||
|
tenancyID: tenancyID,
|
||||||
|
leafCertificateRetriever: leafCertificateRetriever,
|
||||||
|
intermediateCertificateRetrievers: intermediateCertificateRetrievers,
|
||||||
|
}
|
||||||
|
client.sessionKeySupplier = newSessionKeySupplier()
|
||||||
|
client.authClient = newAuthClient(region, client)
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
genericHeaders = []string{"date", "(request-target)"} // "host" is not needed for the federation endpoint. Don't ask me why.
|
||||||
|
bodyHeaders = []string{"content-length", "content-type", "x-content-sha256"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func newAuthClient(region common.Region, provider common.KeyProvider) *common.BaseClient {
|
||||||
|
signer := common.RequestSigner(provider, genericHeaders, bodyHeaders)
|
||||||
|
client := common.DefaultBaseClientWithSigner(signer)
|
||||||
|
if region == common.RegionSEA {
|
||||||
|
client.Host = "https://auth.r1.oracleiaas.com"
|
||||||
|
} else {
|
||||||
|
client.Host = fmt.Sprintf(common.DefaultHostURLTemplate, "auth", string(region))
|
||||||
|
}
|
||||||
|
client.BasePath = "v1/x509"
|
||||||
|
return &client
|
||||||
|
}
|
||||||
|
|
||||||
|
// For authClient to sign requests to X509 Federation Endpoint
|
||||||
|
func (c *x509FederationClient) KeyID() (string, error) {
|
||||||
|
tenancy := c.tenancyID
|
||||||
|
fingerprint := fingerprint(c.leafCertificateRetriever.Certificate())
|
||||||
|
return fmt.Sprintf("%s/fed-x509/%s", tenancy, fingerprint), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// For authClient to sign requests to X509 Federation Endpoint
|
||||||
|
func (c *x509FederationClient) PrivateRSAKey() (*rsa.PrivateKey, error) {
|
||||||
|
return c.leafCertificateRetriever.PrivateKey(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) PrivateKey() (*rsa.PrivateKey, error) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
if err := c.renewSecurityTokenIfNotValid(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.sessionKeySupplier.PrivateKey(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) SecurityToken() (token string, err error) {
|
||||||
|
c.mux.Lock()
|
||||||
|
defer c.mux.Unlock()
|
||||||
|
|
||||||
|
if err = c.renewSecurityTokenIfNotValid(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return c.securityToken.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) renewSecurityTokenIfNotValid() (err error) {
|
||||||
|
if c.securityToken == nil || !c.securityToken.Valid() {
|
||||||
|
if err = c.renewSecurityToken(); err != nil {
|
||||||
|
return fmt.Errorf("failed to renew security token: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) renewSecurityToken() (err error) {
|
||||||
|
if err = c.sessionKeySupplier.Refresh(); err != nil {
|
||||||
|
return fmt.Errorf("failed to refresh session key: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.leafCertificateRetriever.Refresh(); err != nil {
|
||||||
|
return fmt.Errorf("failed to refresh leaf certificate: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedTenancyID := extractTenancyIDFromCertificate(c.leafCertificateRetriever.Certificate())
|
||||||
|
if c.tenancyID != updatedTenancyID {
|
||||||
|
err = fmt.Errorf("unexpected update of tenancy OCID in the leaf certificate. Previous tenancy: %s, Updated: %s", c.tenancyID, updatedTenancyID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, retriever := range c.intermediateCertificateRetrievers {
|
||||||
|
if err = retriever.Refresh(); err != nil {
|
||||||
|
return fmt.Errorf("failed to refresh intermediate certificate: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.securityToken, err = c.getSecurityToken(); err != nil {
|
||||||
|
return fmt.Errorf("failed to get security token: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) getSecurityToken() (securityToken, error) {
|
||||||
|
request := c.makeX509FederationRequest()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var httpRequest http.Request
|
||||||
|
if httpRequest, err = common.MakeDefaultHTTPRequestWithTaggedStruct(http.MethodPost, "", request); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to make http request: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpResponse *http.Response
|
||||||
|
defer common.CloseBodyIfValid(httpResponse)
|
||||||
|
if httpResponse, err = c.authClient.Call(context.Background(), &httpRequest); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to call: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
response := x509FederationResponse{}
|
||||||
|
if err = common.UnmarshalResponse(httpResponse, &response); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal the response: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return newInstancePrincipalToken(response.Token.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
type x509FederationRequest struct {
|
||||||
|
X509FederationDetails `contributesTo:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// X509FederationDetails x509 federation details
|
||||||
|
type X509FederationDetails struct {
|
||||||
|
Certificate string `mandatory:"true" json:"certificate,omitempty"`
|
||||||
|
PublicKey string `mandatory:"true" json:"publicKey,omitempty"`
|
||||||
|
IntermediateCertificates []string `mandatory:"false" json:"intermediateCertificates,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type x509FederationResponse struct {
|
||||||
|
Token `presentIn:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token token
|
||||||
|
type Token struct {
|
||||||
|
Token string `mandatory:"true" json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) makeX509FederationRequest() *x509FederationRequest {
|
||||||
|
certificate := c.sanitizeCertificateString(string(c.leafCertificateRetriever.CertificatePemRaw()))
|
||||||
|
publicKey := c.sanitizeCertificateString(string(c.sessionKeySupplier.PublicKeyPemRaw()))
|
||||||
|
var intermediateCertificates []string
|
||||||
|
for _, retriever := range c.intermediateCertificateRetrievers {
|
||||||
|
intermediateCertificates = append(intermediateCertificates, c.sanitizeCertificateString(string(retriever.CertificatePemRaw())))
|
||||||
|
}
|
||||||
|
|
||||||
|
details := X509FederationDetails{
|
||||||
|
Certificate: certificate,
|
||||||
|
PublicKey: publicKey,
|
||||||
|
IntermediateCertificates: intermediateCertificates,
|
||||||
|
}
|
||||||
|
return &x509FederationRequest{details}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *x509FederationClient) sanitizeCertificateString(certString string) string {
|
||||||
|
certString = strings.Replace(certString, "-----BEGIN CERTIFICATE-----", "", -1)
|
||||||
|
certString = strings.Replace(certString, "-----END CERTIFICATE-----", "", -1)
|
||||||
|
certString = strings.Replace(certString, "-----BEGIN PUBLIC KEY-----", "", -1)
|
||||||
|
certString = strings.Replace(certString, "-----END PUBLIC KEY-----", "", -1)
|
||||||
|
certString = strings.Replace(certString, "\n", "", -1)
|
||||||
|
return certString
|
||||||
|
}
|
||||||
|
|
||||||
|
// sessionKeySupplier provides an RSA keypair which can be re-generated by calling Refresh().
|
||||||
|
type sessionKeySupplier interface {
|
||||||
|
Refresh() error
|
||||||
|
PrivateKey() *rsa.PrivateKey
|
||||||
|
PublicKeyPemRaw() []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// inMemorySessionKeySupplier implements sessionKeySupplier to vend an RSA keypair.
|
||||||
|
// Refresh() generates a new RSA keypair with a random source, and keeps it in memory.
|
||||||
|
//
|
||||||
|
// inMemorySessionKeySupplier is not thread-safe.
|
||||||
|
type inMemorySessionKeySupplier struct {
|
||||||
|
keySize int
|
||||||
|
privateKey *rsa.PrivateKey
|
||||||
|
publicKeyPemRaw []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// newSessionKeySupplier creates and returns a sessionKeySupplier instance which generates key pairs of size 2048.
|
||||||
|
func newSessionKeySupplier() sessionKeySupplier {
|
||||||
|
return &inMemorySessionKeySupplier{keySize: 2048}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh() is failure atomic, i.e., PrivateKey() and PublicKeyPemRaw() would return their previous values
|
||||||
|
// if Refresh() fails.
|
||||||
|
func (s *inMemorySessionKeySupplier) Refresh() (err error) {
|
||||||
|
common.Debugln("Refreshing session key")
|
||||||
|
|
||||||
|
var privateKey *rsa.PrivateKey
|
||||||
|
privateKey, err = rsa.GenerateKey(rand.Reader, s.keySize)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to generate a new keypair: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicKeyAsnBytes []byte
|
||||||
|
if publicKeyAsnBytes, err = x509.MarshalPKIXPublicKey(privateKey.Public()); err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal the public part of the new keypair: %s", err.Error())
|
||||||
|
}
|
||||||
|
publicKeyPemRaw := pem.EncodeToMemory(&pem.Block{
|
||||||
|
Type: "PUBLIC KEY",
|
||||||
|
Bytes: publicKeyAsnBytes,
|
||||||
|
})
|
||||||
|
|
||||||
|
s.privateKey = privateKey
|
||||||
|
s.publicKeyPemRaw = publicKeyPemRaw
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inMemorySessionKeySupplier) PrivateKey() *rsa.PrivateKey {
|
||||||
|
if s.privateKey == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := *s.privateKey
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *inMemorySessionKeySupplier) PublicKeyPemRaw() []byte {
|
||||||
|
if s.publicKeyPemRaw == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c := make([]byte, len(s.publicKeyPemRaw))
|
||||||
|
copy(c, s.publicKeyPemRaw)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type securityToken interface {
|
||||||
|
fmt.Stringer
|
||||||
|
Valid() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type instancePrincipalToken struct {
|
||||||
|
tokenString string
|
||||||
|
jwtToken *jwtToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInstancePrincipalToken(tokenString string) (newToken securityToken, err error) {
|
||||||
|
var jwtToken *jwtToken
|
||||||
|
if jwtToken, err = parseJwt(tokenString); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse the token string \"%s\": %s", tokenString, err.Error())
|
||||||
|
}
|
||||||
|
return &instancePrincipalToken{tokenString, jwtToken}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *instancePrincipalToken) String() string {
|
||||||
|
return t.tokenString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *instancePrincipalToken) Valid() bool {
|
||||||
|
return !t.jwtToken.expired()
|
||||||
|
}
|
100
vendor/github.com/oracle/oci-go-sdk/common/auth/instance_principal_key_provider.go
generated
vendored
Normal file
100
vendor/github.com/oracle/oci-go-sdk/common/auth/instance_principal_key_provider.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rsa"
|
||||||
|
"fmt"
|
||||||
|
"github.com/oracle/oci-go-sdk/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
regionURL = `http://169.254.169.254/opc/v1/instance/region`
|
||||||
|
leafCertificateURL = `http://169.254.169.254/opc/v1/identity/cert.pem`
|
||||||
|
leafCertificateKeyURL = `http://169.254.169.254/opc/v1/identity/key.pem`
|
||||||
|
leafCertificateKeyPassphrase = `` // No passphrase for the private key for Compute instances
|
||||||
|
intermediateCertificateURL = `http://169.254.169.254/opc/v1/identity/intermediate.pem`
|
||||||
|
intermediateCertificateKeyURL = ``
|
||||||
|
intermediateCertificateKeyPassphrase = `` // No passphrase for the private key for Compute instances
|
||||||
|
)
|
||||||
|
|
||||||
|
// instancePrincipalKeyProvider implements KeyProvider to provide a key ID and its corresponding private key
|
||||||
|
// for an instance principal by getting a security token via x509FederationClient.
|
||||||
|
//
|
||||||
|
// The region name of the endpoint for x509FederationClient is obtained from the metadata service on the compute
|
||||||
|
// instance.
|
||||||
|
type instancePrincipalKeyProvider struct {
|
||||||
|
regionForFederationClient common.Region
|
||||||
|
federationClient federationClient
|
||||||
|
tenancyID string
|
||||||
|
}
|
||||||
|
|
||||||
|
// newInstancePrincipalKeyProvider creates and returns an instancePrincipalKeyProvider instance based on
|
||||||
|
// x509FederationClient.
|
||||||
|
//
|
||||||
|
// NOTE: There is a race condition between PrivateRSAKey() and KeyID(). These two pieces are tightly coupled; KeyID
|
||||||
|
// includes a security token obtained from Auth service by giving a public key which is paired with PrivateRSAKey.
|
||||||
|
// The x509FederationClient caches the security token in memory until it is expired. Thus, even if a client obtains a
|
||||||
|
// KeyID that is not expired at the moment, the PrivateRSAKey that the client acquires at a next moment could be
|
||||||
|
// invalid because the KeyID could be already expired.
|
||||||
|
func newInstancePrincipalKeyProvider() (provider *instancePrincipalKeyProvider, err error) {
|
||||||
|
var region common.Region
|
||||||
|
if region, err = getRegionForFederationClient(regionURL); err != nil {
|
||||||
|
err = fmt.Errorf("failed to get the region name from %s: %s", regionURL, err.Error())
|
||||||
|
common.Logln(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
leafCertificateRetriever := newURLBasedX509CertificateRetriever(
|
||||||
|
leafCertificateURL, leafCertificateKeyURL, leafCertificateKeyPassphrase)
|
||||||
|
intermediateCertificateRetrievers := []x509CertificateRetriever{
|
||||||
|
newURLBasedX509CertificateRetriever(
|
||||||
|
intermediateCertificateURL, intermediateCertificateKeyURL, intermediateCertificateKeyPassphrase),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = leafCertificateRetriever.Refresh(); err != nil {
|
||||||
|
err = fmt.Errorf("failed to refresh the leaf certificate: %s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tenancyID := extractTenancyIDFromCertificate(leafCertificateRetriever.Certificate())
|
||||||
|
|
||||||
|
federationClient := newX509FederationClient(
|
||||||
|
region, tenancyID, leafCertificateRetriever, intermediateCertificateRetrievers)
|
||||||
|
|
||||||
|
provider = &instancePrincipalKeyProvider{regionForFederationClient: region, federationClient: federationClient, tenancyID: tenancyID}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRegionForFederationClient(url string) (r common.Region, err error) {
|
||||||
|
var body bytes.Buffer
|
||||||
|
if body, err = httpGet(url); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return common.StringToRegion(body.String()), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *instancePrincipalKeyProvider) RegionForFederationClient() common.Region {
|
||||||
|
return p.regionForFederationClient
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *instancePrincipalKeyProvider) PrivateRSAKey() (privateKey *rsa.PrivateKey, err error) {
|
||||||
|
if privateKey, err = p.federationClient.PrivateKey(); err != nil {
|
||||||
|
err = fmt.Errorf("failed to get private key: %s", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return privateKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *instancePrincipalKeyProvider) KeyID() (string, error) {
|
||||||
|
var securityToken string
|
||||||
|
var err error
|
||||||
|
if securityToken, err = p.federationClient.SecurityToken(); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get security token: %s", err.Error())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("ST$%s", securityToken), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *instancePrincipalKeyProvider) TenancyOCID() (string, error) {
|
||||||
|
return p.tenancyID, nil
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jwtToken struct {
|
||||||
|
raw string
|
||||||
|
header map[string]interface{}
|
||||||
|
payload map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *jwtToken) expired() bool {
|
||||||
|
exp := int64(t.payload["exp"].(float64))
|
||||||
|
return exp <= time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJwt(tokenString string) (*jwtToken, error) {
|
||||||
|
parts := strings.Split(tokenString, ".")
|
||||||
|
if len(parts) != 3 {
|
||||||
|
return nil, fmt.Errorf("the given token string contains an invalid number of parts")
|
||||||
|
}
|
||||||
|
|
||||||
|
token := &jwtToken{raw: tokenString}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse Header part
|
||||||
|
var headerBytes []byte
|
||||||
|
if headerBytes, err = decodePart(parts[0]); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode the header bytes: %s", err.Error())
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(headerBytes, &token.header); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse Payload part
|
||||||
|
var payloadBytes []byte
|
||||||
|
if payloadBytes, err = decodePart(parts[1]); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode the payload bytes: %s", err.Error())
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(bytes.NewBuffer(payloadBytes))
|
||||||
|
if err = decoder.Decode(&token.payload); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode the payload json: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodePart(partString string) ([]byte, error) {
|
||||||
|
if l := len(partString) % 4; 0 < l {
|
||||||
|
partString += strings.Repeat("=", 4-l)
|
||||||
|
}
|
||||||
|
return base64.URLEncoding.DecodeString(partString)
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"github.com/oracle/oci-go-sdk/common"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// httpGet makes a simple HTTP GET request to the given URL, expecting only "200 OK" status code.
|
||||||
|
// This is basically for the Instance Metadata Service.
|
||||||
|
func httpGet(url string) (body bytes.Buffer, err error) {
|
||||||
|
var response *http.Response
|
||||||
|
if response, err = http.Get(url); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
common.IfDebug(func() {
|
||||||
|
if dump, e := httputil.DumpResponse(response, true); e == nil {
|
||||||
|
common.Logf("Dump Response %v", string(dump))
|
||||||
|
} else {
|
||||||
|
common.Debugln(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
if _, err = body.ReadFrom(response.Body); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
err = fmt.Errorf("HTTP Get failed: URL: %s, Status: %s, Message: %s",
|
||||||
|
url, response.Status, body.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractTenancyIDFromCertificate(cert *x509.Certificate) string {
|
||||||
|
for _, nameAttr := range cert.Subject.Names {
|
||||||
|
value := nameAttr.Value.(string)
|
||||||
|
if strings.HasPrefix(value, "opc-tenant:") {
|
||||||
|
return value[len("opc-tenant:"):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func fingerprint(certificate *x509.Certificate) string {
|
||||||
|
fingerprint := sha1.Sum(certificate.Raw)
|
||||||
|
return colonSeparatedString(fingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func colonSeparatedString(fingerprint [sha1.Size]byte) string {
|
||||||
|
spaceSeparated := fmt.Sprintf("% x", fingerprint)
|
||||||
|
return strings.Replace(spaceSeparated, " ", ":", -1)
|
||||||
|
}
|
|
@ -484,6 +484,7 @@ github.com/nu7hatch/gouuid
|
||||||
github.com/olekukonko/tablewriter
|
github.com/olekukonko/tablewriter
|
||||||
# github.com/oracle/oci-go-sdk v1.8.0
|
# github.com/oracle/oci-go-sdk v1.8.0
|
||||||
github.com/oracle/oci-go-sdk/common
|
github.com/oracle/oci-go-sdk/common
|
||||||
|
github.com/oracle/oci-go-sdk/common/auth
|
||||||
github.com/oracle/oci-go-sdk/core
|
github.com/oracle/oci-go-sdk/core
|
||||||
# github.com/outscale/osc-go v0.0.1
|
# github.com/outscale/osc-go v0.0.1
|
||||||
github.com/outscale/osc-go/oapi
|
github.com/outscale/osc-go/oapi
|
||||||
|
|
Loading…
Reference in New Issue