Browse Source

Merge pull request #700 from EladGabay/elad/oci-secret-by-name

oracle vault: Use instance principal if auth is empty
paul-the-alien[bot] 4 years ago
parent
commit
027f28ec97

+ 16 - 14
apis/externalsecrets/v1alpha1/secretstore_oracle_types.go

@@ -19,31 +19,33 @@ import (
 // Configures an store to sync secrets using a Oracle Vault
 // backend.
 type OracleProvider struct {
-	// Auth configures how secret-manager authenticates with the Oracle Vault.
-	Auth OracleAuth `json:"auth"`
-
-	// User is an access OCID specific to the account.
-	User string `json:"user,omitempty"`
-
-	// Tenancy is the tenancy OCID where secret is located.
-	Tenancy string `json:"tenancy,omitempty"`
-
-	// Region is the region where secret is located.
-	Region string `json:"region,omitempty"`
+	// Region is the region where vault is located.
+	Region string `json:"region"`
 
 	// Vault is the vault's OCID of the specific vault where secret is located.
-	Vault string `json:"vault,omitempty"`
+	Vault string `json:"vault"`
+
+	// Auth configures how secret-manager authenticates with the Oracle Vault.
+	// If empty, use the instance principal, otherwise the user credentials specified in Auth.
+	// +optional
+	Auth *OracleAuth `json:"auth,omitempty"`
 }
 
 type OracleAuth struct {
+	// Tenancy is the tenancy OCID where user is located.
+	Tenancy string `json:"tenancy"`
+
+	// User is an access OCID specific to the account.
+	User string `json:"user"`
+
 	// SecretRef to pass through sensitive information.
 	SecretRef OracleSecretRef `json:"secretRef"`
 }
 
 type OracleSecretRef struct {
 	// PrivateKey is the user's API Signing Key in PEM format, used for authentication.
-	PrivateKey esmeta.SecretKeySelector `json:"privatekey,omitempty"`
+	PrivateKey esmeta.SecretKeySelector `json:"privatekey"`
 
 	// Fingerprint is the fingerprint of the API private key.
-	Fingerprint esmeta.SecretKeySelector `json:"fingerprint,omitempty"`
+	Fingerprint esmeta.SecretKeySelector `json:"fingerprint"`
 }

+ 5 - 1
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -836,7 +836,11 @@ func (in *OracleAuth) DeepCopy() *OracleAuth {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *OracleProvider) DeepCopyInto(out *OracleProvider) {
 	*out = *in
-	in.Auth.DeepCopyInto(&out.Auth)
+	if in.Auth != nil {
+		in, out := &in.Auth, &out.Auth
+		*out = new(OracleAuth)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleProvider.

+ 17 - 9
deploy/crds/external-secrets.io_clustersecretstores.yaml

@@ -558,7 +558,8 @@ spec:
                     properties:
                       auth:
                         description: Auth configures how secret-manager authenticates
-                          with the Oracle Vault.
+                          with the Oracle Vault. If empty, use the instance principal,
+                          otherwise the user credentials specified in Auth.
                         properties:
                           secretRef:
                             description: SecretRef to pass through sensitive information.
@@ -605,25 +606,32 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
+                            required:
+                            - fingerprint
+                            - privatekey
                             type: object
+                          tenancy:
+                            description: Tenancy is the tenancy OCID where user is
+                              located.
+                            type: string
+                          user:
+                            description: User is an access OCID specific to the account.
+                            type: string
                         required:
                         - secretRef
+                        - tenancy
+                        - user
                         type: object
                       region:
-                        description: Region is the region where secret is located.
-                        type: string
-                      tenancy:
-                        description: Tenancy is the tenancy OCID where secret is located.
-                        type: string
-                      user:
-                        description: User is an access OCID specific to the account.
+                        description: Region is the region where vault is located.
                         type: string
                       vault:
                         description: Vault is the vault's OCID of the specific vault
                           where secret is located.
                         type: string
                     required:
-                    - auth
+                    - region
+                    - vault
                     type: object
                   vault:
                     description: Vault configures this store to sync secrets using

+ 17 - 9
deploy/crds/external-secrets.io_secretstores.yaml

@@ -561,7 +561,8 @@ spec:
                     properties:
                       auth:
                         description: Auth configures how secret-manager authenticates
-                          with the Oracle Vault.
+                          with the Oracle Vault. If empty, use the instance principal,
+                          otherwise the user credentials specified in Auth.
                         properties:
                           secretRef:
                             description: SecretRef to pass through sensitive information.
@@ -608,25 +609,32 @@ spec:
                                       the referent.
                                     type: string
                                 type: object
+                            required:
+                            - fingerprint
+                            - privatekey
                             type: object
+                          tenancy:
+                            description: Tenancy is the tenancy OCID where user is
+                              located.
+                            type: string
+                          user:
+                            description: User is an access OCID specific to the account.
+                            type: string
                         required:
                         - secretRef
+                        - tenancy
+                        - user
                         type: object
                       region:
-                        description: Region is the region where secret is located.
-                        type: string
-                      tenancy:
-                        description: Tenancy is the tenancy OCID where secret is located.
-                        type: string
-                      user:
-                        description: User is an access OCID specific to the account.
+                        description: Region is the region where vault is located.
                         type: string
                       vault:
                         description: Vault is the vault's OCID of the specific vault
                           where secret is located.
                         type: string
                     required:
-                    - auth
+                    - region
+                    - vault
                     type: object
                   vault:
                     description: Vault configures this store to sync secrets using

+ 4 - 1
docs/provider-oracle-vault.md

@@ -4,7 +4,10 @@ External Secrets Operator integrates with [OCI API](https://github.com/oracle/oc
 
 ### Authentication
 
-The API requires a userOCID, tenancyOCID, fingerprint, key file and a region. The fingerprint and key file should be supplied in the secret with the rest being provided in the secret store.
+If `auth` is not specified, the operator uses the instance principal.
+
+For using a specific user credentials, userOCID, tenancyOCID, fingerprint and private key are required.
+The fingerprint and key file should be supplied in the secret with the rest being provided in the secret store.
 
 See url for what region you you are accessing.
 ![userOCID-details](./pictures/screenshot_region.png)

+ 18 - 6
docs/snippets/oracle-secret-store.yaml

@@ -1,19 +1,31 @@
 apiVersion: external-secrets.io/v1alpha1
 kind: SecretStore
 metadata:
-  name: example
+  name: example-instance-principal
 spec:
   provider:
-    oracle: #Needs to match value in secretstore_types.go
+    oracle:
       vault: # The vault OCID
-      user:
-      tenancy:
-      region:
+      region: # The vault region
+
+---
+
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: example-auth
+spec:
+  provider:
+    oracle:
+      vault: # The vault OCID
+      region: # The vault region
       auth:
+        user: # A user OCID
+        tenancy: # A user's tenancy
         secretRef:
           privatekey:
             name: oracle-secret
-            key: privateKey #Needs to match stringData val in secret_oracle.yml
+            key: privateKey
           fingerprint:
             name: oracle-secret
             key: fingerprint

+ 7 - 3
e2e/suite/oracle/provider.go

@@ -21,8 +21,8 @@ import (
 
 	// nolint
 	. "github.com/onsi/gomega"
-	"github.com/oracle/oci-go-sdk/v45/common"
-	vault "github.com/oracle/oci-go-sdk/v45/vault"
+	"github.com/oracle/oci-go-sdk/v56/common"
+	vault "github.com/oracle/oci-go-sdk/v56/vault"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilpointer "k8s.io/utils/pointer"
@@ -113,7 +113,11 @@ func (p *oracleProvider) BeforeEach() {
 		Spec: esv1alpha1.SecretStoreSpec{
 			Provider: &esv1alpha1.SecretStoreProvider{
 				Oracle: &esv1alpha1.OracleProvider{
-					Auth: esv1alpha1.OracleAuth{
+					Region: p.region,
+					Vault:  "vaultOCID",
+					Auth: &esv1alpha1.OracleAuth{
+						Tenancy: p.tenancy,
+						User:    p.user,
 						SecretRef: esv1alpha1.OracleSecretRef{
 							Fingerprint: esmeta.SecretKeySelector{
 								Name: "vms-secret",

+ 2 - 1
go.mod

@@ -59,7 +59,7 @@ require (
 	github.com/lestrrat-go/jwx v1.2.1
 	github.com/onsi/ginkgo/v2 v2.1.1
 	github.com/onsi/gomega v1.18.1
-	github.com/oracle/oci-go-sdk/v45 v45.2.0
+	github.com/oracle/oci-go-sdk/v56 v56.1.0
 	github.com/prometheus/client_golang v1.12.1
 	github.com/prometheus/client_model v0.2.0
 	github.com/stretchr/testify v1.7.0
@@ -181,6 +181,7 @@ require (
 	github.com/russross/blackfriday/v2 v2.0.1 // indirect
 	github.com/ryanuber/go-glob v1.0.0 // indirect
 	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
+	github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b // indirect
 	github.com/spf13/cobra v1.2.1 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/stretchr/objx v0.2.0 // indirect

+ 4 - 2
go.sum

@@ -693,8 +693,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
 github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/oracle/oci-go-sdk/v45 v45.2.0 h1:vCPoQlE+DOrM2heJn66rvPU6fbsc/0Cxtzs2jnFut6U=
-github.com/oracle/oci-go-sdk/v45 v45.2.0/go.mod h1:ZM6LGiRO5TPQJxTlrXbcHMbClE775wnGD5U/EerCsRw=
+github.com/oracle/oci-go-sdk/v56 v56.1.0 h1:HOr9P+MkwgrilEGTJCU7a6GMFrUG/RZAzvh/2JeRXvI=
+github.com/oracle/oci-go-sdk/v56 v56.1.0/go.mod h1:kDJAL3HEAF+4oQR8GfaOkY6rz2kU3/kZ6vYJnJXSCkA=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -772,6 +772,8 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
+github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b h1:br+bPNZsJWKicw/5rALEo67QHs5weyD5tf8WST+4sJ0=
+github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=

+ 1 - 1
pkg/provider/oracle/fake/fake.go

@@ -16,7 +16,7 @@ package fake
 import (
 	"context"
 
-	secrets "github.com/oracle/oci-go-sdk/v45/secrets"
+	secrets "github.com/oracle/oci-go-sdk/v56/secrets"
 )
 
 type OracleMockClient struct {

+ 75 - 81
pkg/provider/oracle/oracle.go

@@ -17,14 +17,16 @@ import (
 	"encoding/json"
 	"fmt"
 
-	"github.com/oracle/oci-go-sdk/v45/common"
-	"github.com/oracle/oci-go-sdk/v45/secrets"
+	"github.com/oracle/oci-go-sdk/v56/common"
+	"github.com/oracle/oci-go-sdk/v56/common/auth"
+	"github.com/oracle/oci-go-sdk/v56/secrets"
 	"github.com/tidwall/gjson"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/provider"
 	"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
 	"github.com/external-secrets/external-secrets/pkg/provider/schema"
@@ -52,18 +54,6 @@ const (
 	errUnexpectedContent                     = "unexpected secret bundle content"
 )
 
-type client struct {
-	kube        kclient.Client
-	store       *esv1alpha1.OracleProvider
-	namespace   string
-	storeKind   string
-	tenancy     string
-	user        string
-	region      string
-	fingerprint string
-	privateKey  string
-}
-
 type VaultManagementService struct {
 	Client VMInterface
 	vault  string
@@ -73,58 +63,6 @@ type VMInterface interface {
 	GetSecretBundleByName(ctx context.Context, request secrets.GetSecretBundleByNameRequest) (secrets.GetSecretBundleByNameResponse, error)
 }
 
-func (c *client) setAuth(ctx context.Context) error {
-	credentialsSecret := &corev1.Secret{}
-	credentialsSecretName := c.store.Auth.SecretRef.PrivateKey.Name
-	if credentialsSecretName == "" {
-		return fmt.Errorf(errORACLECredSecretName)
-	}
-	objectKey := types.NamespacedName{
-		Name:      credentialsSecretName,
-		Namespace: c.namespace,
-	}
-
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if c.storeKind == esv1alpha1.ClusterSecretStoreKind {
-		if c.store.Auth.SecretRef.PrivateKey.Namespace == nil {
-			return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
-		}
-		objectKey.Namespace = *c.store.Auth.SecretRef.PrivateKey.Namespace
-	}
-
-	err := c.kube.Get(ctx, objectKey, credentialsSecret)
-	if err != nil {
-		return fmt.Errorf(errFetchSAKSecret, err)
-	}
-
-	c.privateKey = string(credentialsSecret.Data[c.store.Auth.SecretRef.PrivateKey.Key])
-	if c.privateKey == "" {
-		return fmt.Errorf(errMissingPK)
-	}
-
-	c.fingerprint = string(credentialsSecret.Data[c.store.Auth.SecretRef.Fingerprint.Key])
-	if c.fingerprint == "" {
-		return fmt.Errorf(errMissingFingerprint)
-	}
-
-	c.user = c.store.User
-	if c.user == "" {
-		return fmt.Errorf(errMissingUser)
-	}
-
-	c.tenancy = c.store.Tenancy
-	if c.tenancy == "" {
-		return fmt.Errorf(errMissingTenancy)
-	}
-
-	c.region = c.store.Region
-	if c.region == "" {
-		return fmt.Errorf(errMissingRegion)
-	}
-
-	return nil
-}
-
 func (vms *VaultManagementService) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
 	if utils.IsNil(vms.Client) {
 		return nil, fmt.Errorf(errUninitalizedOracleProvider)
@@ -188,35 +126,91 @@ func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1alph
 		return nil, fmt.Errorf(errMissingVault)
 	}
 
-	oracleStore := &client{
-		kube:      kube,
-		store:     oracleSpec,
-		namespace: namespace,
-		storeKind: store.GetObjectKind().GroupVersionKind().Kind,
-	}
-	if err := oracleStore.setAuth(ctx); err != nil {
-		return nil, err
+	if oracleSpec.Region == "" {
+		return nil, fmt.Errorf(errMissingRegion)
 	}
 
-	oracleTenancy := oracleStore.tenancy
-	oracleUser := oracleStore.user
-	oracleRegion := oracleStore.region
-	oracleFingerprint := oracleStore.fingerprint
-	oraclePrivateKey := oracleStore.privateKey
-
-	configurationProvider := common.NewRawConfigurationProvider(oracleTenancy, oracleUser, oracleRegion, oracleFingerprint, oraclePrivateKey, nil)
+	var (
+		err                   error
+		configurationProvider common.ConfigurationProvider
+	)
+	if oracleSpec.Auth == nil {
+		configurationProvider, err = auth.InstancePrincipalConfigurationProvider()
+	} else {
+		configurationProvider, err = getUserAuthConfigurationProvider(ctx, kube, oracleSpec, namespace, store.GetObjectKind().GroupVersionKind().Kind, oracleSpec.Region)
+	}
+	if err != nil {
+		return nil, fmt.Errorf(errOracleClient, err)
+	}
 
 	secretManagementService, err := secrets.NewSecretsClientWithConfigurationProvider(configurationProvider)
 	if err != nil {
 		return nil, fmt.Errorf(errOracleClient, err)
 	}
 
+	secretManagementService.SetRegion(oracleSpec.Region)
+
 	return &VaultManagementService{
 		Client: secretManagementService,
 		vault:  oracleSpec.Vault,
 	}, nil
 }
 
+func getSecretData(ctx context.Context, kube kclient.Client, namespace, storeKind string, secretRef esmeta.SecretKeySelector) (string, error) {
+	if secretRef.Name == "" {
+		return "", fmt.Errorf(errORACLECredSecretName)
+	}
+
+	objectKey := types.NamespacedName{
+		Name:      secretRef.Name,
+		Namespace: namespace,
+	}
+
+	// only ClusterStore is allowed to set namespace (and then it's required)
+	if storeKind == esv1alpha1.ClusterSecretStoreKind {
+		if secretRef.Namespace == nil {
+			return "", fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
+		}
+		objectKey.Namespace = *secretRef.Namespace
+	}
+
+	secret := corev1.Secret{}
+	err := kube.Get(ctx, objectKey, &secret)
+	if err != nil {
+		return "", fmt.Errorf(errFetchSAKSecret, err)
+	}
+
+	return string(secret.Data[secretRef.Key]), nil
+}
+
+func getUserAuthConfigurationProvider(ctx context.Context, kube kclient.Client, store *esv1alpha1.OracleProvider, namespace, storeKind, region string) (common.ConfigurationProvider, error) {
+	privateKey, err := getSecretData(ctx, kube, namespace, storeKind, store.Auth.SecretRef.PrivateKey)
+	if err != nil {
+		return nil, err
+	}
+	if privateKey == "" {
+		return nil, fmt.Errorf(errMissingPK)
+	}
+
+	fingerprint, err := getSecretData(ctx, kube, namespace, storeKind, store.Auth.SecretRef.Fingerprint)
+	if err != nil {
+		return nil, err
+	}
+	if fingerprint == "" {
+		return nil, fmt.Errorf(errMissingFingerprint)
+	}
+
+	if store.Auth.User == "" {
+		return nil, fmt.Errorf(errMissingUser)
+	}
+
+	if store.Auth.Tenancy == "" {
+		return nil, fmt.Errorf(errMissingTenancy)
+	}
+
+	return common.NewRawConfigurationProvider(store.Auth.Tenancy, store.Auth.User, region, fingerprint, privateKey, nil), nil
+}
+
 func (vms *VaultManagementService) Close(ctx context.Context) error {
 	return nil
 }

+ 1 - 1
pkg/provider/oracle/oracle_test.go

@@ -19,7 +19,7 @@ import (
 	"strings"
 	"testing"
 
-	secrets "github.com/oracle/oci-go-sdk/v45/secrets"
+	secrets "github.com/oracle/oci-go-sdk/v56/secrets"
 	utilpointer "k8s.io/utils/pointer"
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"

+ 1 - 1
pkg/utils/utils_test.go

@@ -17,7 +17,7 @@ package utils
 import (
 	"testing"
 
-	vault "github.com/oracle/oci-go-sdk/v45/vault"
+	vault "github.com/oracle/oci-go-sdk/v56/vault"
 	v1 "k8s.io/api/core/v1"
 )