mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
aws secretsmanager/parameterstore referent auth (#1884)
* feat: implement referentAuth for aws Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> * feat: e2e tests Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> * Update pkg/provider/aws/provider.go Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com> Signed-off-by: Moritz Johner <moolen@users.noreply.github.com> * Update pkg/provider/aws/provider.go Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com> Signed-off-by: Moritz Johner <moolen@users.noreply.github.com> * feat: allow each credential to be referent Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> Signed-off-by: Moritz Johner <beller.moritz@googlemail.com> Signed-off-by: Moritz Johner <moolen@users.noreply.github.com> Co-authored-by: Gustavo Fernandes de Carvalho <gusfcarvalho@gmail.com>
This commit is contained in:
parent
f4e70ddfed
commit
5384954f46
16 changed files with 320 additions and 145 deletions
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
|
@ -16,8 +16,8 @@ env:
|
||||||
GO_VERSION: '1.19'
|
GO_VERSION: '1.19'
|
||||||
GINKGO_VERSION: 'v2.1.6'
|
GINKGO_VERSION: 'v2.1.6'
|
||||||
DOCKER_BUILDX_VERSION: 'v0.4.2'
|
DOCKER_BUILDX_VERSION: 'v0.4.2'
|
||||||
KIND_VERSION: 'v0.14.0'
|
KIND_VERSION: 'v0.17.0'
|
||||||
KIND_IMAGE: 'kindest/node:v1.24.2'
|
KIND_IMAGE: 'kindest/node:v1.26.0'
|
||||||
|
|
||||||
# Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
|
# Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
|
||||||
# a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether
|
# a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether
|
||||||
|
|
|
@ -46,8 +46,8 @@ The following table show the support for features across different providers.
|
||||||
|
|
||||||
| Provider | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret |
|
| Provider | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret |
|
||||||
|---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: |
|
|---------------------------|:------------:|:------------:| :------------------: | :---------------------: | :--------------: | :---------: |
|
||||||
| AWS Secrets Manager | x | x | | | x | |
|
| AWS Secrets Manager | x | x | | x | x | |
|
||||||
| AWS Parameter Store | x | x | | | x | |
|
| AWS Parameter Store | x | x | | x | x | |
|
||||||
| Hashicorp Vault | x | x | | | x | |
|
| Hashicorp Vault | x | x | | | x | |
|
||||||
| GCP Secret Manager | x | x | | x | x | |
|
| GCP Secret Manager | x | x | | x | x | |
|
||||||
| Azure Keyvault | x | x | x | x | x | |
|
| Azure Keyvault | x | x | x | x | x | |
|
||||||
|
|
|
@ -33,9 +33,7 @@ ginkgo_args=(
|
||||||
"--randomize-all"
|
"--randomize-all"
|
||||||
"--flake-attempts=2"
|
"--flake-attempts=2"
|
||||||
"-p"
|
"-p"
|
||||||
"-progress"
|
|
||||||
"-trace"
|
"-trace"
|
||||||
"--slow-spec-threshold=5m"
|
|
||||||
"-r"
|
"-r"
|
||||||
"-v"
|
"-v"
|
||||||
"-timeout=45m"
|
"-timeout=45m"
|
||||||
|
|
|
@ -27,9 +27,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
WithReferencedIRSA = "with referenced IRSA"
|
WithReferencedIRSA = "with referenced IRSA"
|
||||||
WithMountedIRSA = "with mounted IRSA"
|
WithMountedIRSA = "with mounted IRSA"
|
||||||
StaticCredentialsSecretName = "provider-secret"
|
StaticCredentialsSecretName = "provider-secret"
|
||||||
|
StaticReferentCredentialsSecretName = "referent-provider-secret"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ReferencedIRSAStoreName(f *framework.Framework) string {
|
func ReferencedIRSAStoreName(f *framework.Framework) string {
|
||||||
|
@ -50,6 +51,38 @@ func UseMountedIRSAStore(tc *framework.TestCase) {
|
||||||
tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework)
|
tc.ExternalSecret.Spec.SecretStoreRef.Name = MountedIRSAStoreName(tc.Framework)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
StaticStoreName = "aws-static-creds"
|
||||||
|
staticKeyID = "kid"
|
||||||
|
staticSecretAccessKey = "sak"
|
||||||
|
staticySessionToken = "st"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStaticStoreProvider(serviceType esv1beta1.AWSServiceType, region, secretName string) *esv1beta1.SecretStoreProvider {
|
||||||
|
return &esv1beta1.SecretStoreProvider{
|
||||||
|
AWS: &esv1beta1.AWSProvider{
|
||||||
|
Service: serviceType,
|
||||||
|
Region: region,
|
||||||
|
Auth: esv1beta1.AWSAuth{
|
||||||
|
SecretRef: &esv1beta1.AWSAuthSecretRef{
|
||||||
|
AccessKeyID: esmetav1.SecretKeySelector{
|
||||||
|
Name: StaticReferentCredentialsSecretName,
|
||||||
|
Key: staticKeyID,
|
||||||
|
},
|
||||||
|
SecretAccessKey: esmetav1.SecretKeySelector{
|
||||||
|
Name: StaticReferentCredentialsSecretName,
|
||||||
|
Key: staticSecretAccessKey,
|
||||||
|
},
|
||||||
|
SessionToken: &esmetav1.SecretKeySelector{
|
||||||
|
Name: StaticReferentCredentialsSecretName,
|
||||||
|
Key: staticySessionToken,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StaticStore is namespaced and references
|
// StaticStore is namespaced and references
|
||||||
// static credentials from a secret.
|
// static credentials from a secret.
|
||||||
func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
|
func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
|
||||||
|
@ -59,9 +92,9 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
|
||||||
Namespace: f.Namespace.Name,
|
Namespace: f.Namespace.Name,
|
||||||
},
|
},
|
||||||
StringData: map[string]string{
|
StringData: map[string]string{
|
||||||
"kid": kid,
|
staticKeyID: kid,
|
||||||
"sak": sak,
|
staticSecretAccessKey: sak,
|
||||||
"st": st,
|
staticySessionToken: st,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := f.CRClient.Create(context.Background(), awsCreds)
|
err := f.CRClient.Create(context.Background(), awsCreds)
|
||||||
|
@ -69,34 +102,48 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
|
||||||
|
|
||||||
secretStore := &esv1beta1.SecretStore{
|
secretStore := &esv1beta1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: f.Namespace.Name,
|
Name: StaticStoreName,
|
||||||
Namespace: f.Namespace.Name,
|
Namespace: f.Namespace.Name,
|
||||||
},
|
},
|
||||||
Spec: esv1beta1.SecretStoreSpec{
|
Spec: esv1beta1.SecretStoreSpec{
|
||||||
Provider: &esv1beta1.SecretStoreProvider{
|
Provider: newStaticStoreProvider(serviceType, region, StaticCredentialsSecretName),
|
||||||
AWS: &esv1beta1.AWSProvider{
|
|
||||||
Service: serviceType,
|
|
||||||
Region: region,
|
|
||||||
Auth: esv1beta1.AWSAuth{
|
|
||||||
SecretRef: &esv1beta1.AWSAuthSecretRef{
|
|
||||||
AccessKeyID: esmetav1.SecretKeySelector{
|
|
||||||
Name: StaticCredentialsSecretName,
|
|
||||||
Key: "kid",
|
|
||||||
},
|
|
||||||
SecretAccessKey: esmetav1.SecretKeySelector{
|
|
||||||
Name: StaticCredentialsSecretName,
|
|
||||||
Key: "sak",
|
|
||||||
},
|
|
||||||
SessionToken: &esmetav1.SecretKeySelector{
|
|
||||||
Name: StaticCredentialsSecretName,
|
|
||||||
Key: "st",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = f.CRClient.Create(context.Background(), secretStore)
|
err = f.CRClient.Create(context.Background(), secretStore)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateReferentStaticStore creates a CSS with referent auth and
|
||||||
|
// creates a secret with static authentication credentials in the ExternalSecret namespace.
|
||||||
|
func CreateReferentStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
|
||||||
|
ns := f.Namespace.Name
|
||||||
|
|
||||||
|
awsCreds := &corev1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: StaticReferentCredentialsSecretName,
|
||||||
|
Namespace: ns,
|
||||||
|
},
|
||||||
|
StringData: map[string]string{
|
||||||
|
staticKeyID: kid,
|
||||||
|
staticSecretAccessKey: sak,
|
||||||
|
staticySessionToken: st,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := f.CRClient.Create(context.Background(), awsCreds)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
secretStore := &esv1beta1.ClusterSecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: ReferentSecretStoreName(f),
|
||||||
|
},
|
||||||
|
Spec: esv1beta1.SecretStoreSpec{
|
||||||
|
Provider: newStaticStoreProvider(serviceType, region, StaticReferentCredentialsSecretName),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err = f.CRClient.Create(context.Background(), secretStore)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReferentSecretStoreName(f *framework.Framework) string {
|
||||||
|
return "referent-auth" + f.Namespace.Name
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,14 @@ import (
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework"
|
"github.com/external-secrets/external-secrets-e2e/framework"
|
||||||
|
awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
||||||
"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
|
"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
|
||||||
|
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
withStaticAuth = "with static auth"
|
||||||
|
withReferentStaticAuth = "with static referent auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
|
var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
|
||||||
|
@ -30,25 +37,41 @@ var _ = Describe("[aws] ", Label("aws", "parameterstore"), func() {
|
||||||
DescribeTable("sync secrets",
|
DescribeTable("sync secrets",
|
||||||
framework.TableFunc(f,
|
framework.TableFunc(f,
|
||||||
prov),
|
prov),
|
||||||
Entry(common.SimpleDataSync(f)),
|
framework.Compose(withStaticAuth, f, common.SimpleDataSync, useStaticAuth),
|
||||||
Entry(common.NestedJSONWithGJSON(f)),
|
framework.Compose(withStaticAuth, f, common.NestedJSONWithGJSON, useStaticAuth),
|
||||||
Entry(common.JSONDataFromSync(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataFromSync, useStaticAuth),
|
||||||
Entry(common.JSONDataFromRewrite(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataFromRewrite, useStaticAuth),
|
||||||
Entry(common.JSONDataWithProperty(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithProperty, useStaticAuth),
|
||||||
Entry(common.JSONDataWithTemplate(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithTemplate, useStaticAuth),
|
||||||
Entry(common.DockerJSONConfig(f)),
|
framework.Compose(withStaticAuth, f, common.DockerJSONConfig, useStaticAuth),
|
||||||
Entry(common.DataPropertyDockerconfigJSON(f)),
|
framework.Compose(withStaticAuth, f, common.DataPropertyDockerconfigJSON, useStaticAuth),
|
||||||
Entry(common.SSHKeySync(f)),
|
framework.Compose(withStaticAuth, f, common.SSHKeySync, useStaticAuth),
|
||||||
Entry(common.SSHKeySyncDataProperty(f)),
|
framework.Compose(withStaticAuth, f, common.SSHKeySyncDataProperty, useStaticAuth),
|
||||||
Entry(common.SyncWithoutTargetName(f)),
|
framework.Compose(withStaticAuth, f, common.SyncWithoutTargetName, useStaticAuth),
|
||||||
Entry(common.JSONDataWithoutTargetName(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithoutTargetName, useStaticAuth),
|
||||||
Entry(common.SyncV1Alpha1(f)),
|
|
||||||
Entry(common.DeletionPolicyDelete(f)),
|
framework.Compose(withStaticAuth, f, common.SyncV1Alpha1, useStaticAuth),
|
||||||
|
framework.Compose(withStaticAuth, f, common.DeletionPolicyDelete, useStaticAuth),
|
||||||
|
|
||||||
|
// referent auth
|
||||||
|
framework.Compose(withReferentStaticAuth, f, common.SimpleDataSync, useReferentStaticAuth),
|
||||||
|
|
||||||
// These are specific to parameterstore
|
// These are specific to parameterstore
|
||||||
Entry(FindByName(f)),
|
framework.Compose(withStaticAuth, f, FindByName, useStaticAuth),
|
||||||
Entry(FindByNameWithPath(f)),
|
framework.Compose(withStaticAuth, f, FindByNameWithPath, useStaticAuth),
|
||||||
Entry(FindByTag(f)),
|
framework.Compose(withStaticAuth, f, FindByTag, useStaticAuth),
|
||||||
Entry(FindByTagWithPath(f)),
|
framework.Compose(withStaticAuth, f, FindByTagWithPath, useStaticAuth),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func useStaticAuth(tc *framework.TestCase) {
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
|
||||||
|
if tc.ExternalSecretV1Alpha1 != nil {
|
||||||
|
tc.ExternalSecretV1Alpha1.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func useReferentStaticAuth(tc *framework.TestCase) {
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.ReferentSecretStoreName(tc.Framework)
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
|
||||||
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import (
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework"
|
"github.com/external-secrets/external-secrets-e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework/log"
|
"github.com/external-secrets/external-secrets-e2e/framework/log"
|
||||||
common "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
||||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||||
esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
|
esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -68,19 +68,15 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
|
||||||
}
|
}
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
common.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
|
awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
|
||||||
|
awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
|
||||||
prov.SetupReferencedIRSAStore()
|
prov.SetupReferencedIRSAStore()
|
||||||
prov.SetupMountedIRSAStore()
|
prov.SetupMountedIRSAStore()
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
// Cleanup ClusterSecretStore
|
prov.TeardownReferencedIRSAStore()
|
||||||
err := prov.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
prov.TeardownMountedIRSAStore()
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: common.ReferencedIRSAStoreName(f),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return prov
|
return prov
|
||||||
|
@ -132,7 +128,7 @@ func (s *Provider) DeleteSecret(key string) {
|
||||||
func (s *Provider) SetupMountedIRSAStore() {
|
func (s *Provider) SetupMountedIRSAStore() {
|
||||||
secretStore := &esv1beta1.SecretStore{
|
secretStore := &esv1beta1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: common.MountedIRSAStoreName(s.framework),
|
Name: awscommon.MountedIRSAStoreName(s.framework),
|
||||||
Namespace: s.framework.Namespace.Name,
|
Namespace: s.framework.Namespace.Name,
|
||||||
},
|
},
|
||||||
Spec: esv1beta1.SecretStoreSpec{
|
Spec: esv1beta1.SecretStoreSpec{
|
||||||
|
@ -149,13 +145,21 @@ func (s *Provider) SetupMountedIRSAStore() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Provider) TeardownMountedIRSAStore() {
|
||||||
|
s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: awscommon.MountedIRSAStoreName(s.framework),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ReferncedIRSAStore is a ClusterStore
|
// ReferncedIRSAStore is a ClusterStore
|
||||||
// that references a (IRSA-) ServiceAccount in the default namespace.
|
// that references a (IRSA-) ServiceAccount in the default namespace.
|
||||||
func (s *Provider) SetupReferencedIRSAStore() {
|
func (s *Provider) SetupReferencedIRSAStore() {
|
||||||
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
|
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
|
||||||
secretStore := &esv1beta1.ClusterSecretStore{
|
secretStore := &esv1beta1.ClusterSecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: common.ReferencedIRSAStoreName(s.framework),
|
Name: awscommon.ReferencedIRSAStoreName(s.framework),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
|
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
|
||||||
|
@ -177,3 +181,11 @@ func (s *Provider) SetupReferencedIRSAStore() {
|
||||||
})
|
})
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Provider) TeardownReferencedIRSAStore() {
|
||||||
|
s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: awscommon.ReferencedIRSAStoreName(s.framework),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -35,7 +35,7 @@ import (
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework"
|
"github.com/external-secrets/external-secrets-e2e/framework"
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework/log"
|
"github.com/external-secrets/external-secrets-e2e/framework/log"
|
||||||
common "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
||||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||||
esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
|
esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
@ -69,19 +69,16 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
|
||||||
}
|
}
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
common.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
|
awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
|
||||||
|
awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
|
||||||
prov.SetupReferencedIRSAStore()
|
prov.SetupReferencedIRSAStore()
|
||||||
prov.SetupMountedIRSAStore()
|
prov.SetupMountedIRSAStore()
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
// Cleanup ClusterSecretStore
|
prov.TeardownReferencedIRSAStore()
|
||||||
err := prov.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
prov.TeardownMountedIRSAStore()
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: common.ReferencedIRSAStoreName(f),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return prov
|
return prov
|
||||||
|
@ -150,7 +147,7 @@ func (s *Provider) DeleteSecret(key string) {
|
||||||
func (s *Provider) SetupMountedIRSAStore() {
|
func (s *Provider) SetupMountedIRSAStore() {
|
||||||
secretStore := &esv1beta1.SecretStore{
|
secretStore := &esv1beta1.SecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: common.MountedIRSAStoreName(s.framework),
|
Name: awscommon.MountedIRSAStoreName(s.framework),
|
||||||
Namespace: s.framework.Namespace.Name,
|
Namespace: s.framework.Namespace.Name,
|
||||||
},
|
},
|
||||||
Spec: esv1beta1.SecretStoreSpec{
|
Spec: esv1beta1.SecretStoreSpec{
|
||||||
|
@ -167,13 +164,21 @@ func (s *Provider) SetupMountedIRSAStore() {
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Provider) TeardownMountedIRSAStore() {
|
||||||
|
s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: awscommon.MountedIRSAStoreName(s.framework),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ReferncedIRSAStore is a ClusterStore
|
// ReferncedIRSAStore is a ClusterStore
|
||||||
// that references a (IRSA-) ServiceAccount in the default namespace.
|
// that references a (IRSA-) ServiceAccount in the default namespace.
|
||||||
func (s *Provider) SetupReferencedIRSAStore() {
|
func (s *Provider) SetupReferencedIRSAStore() {
|
||||||
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
|
log.Logf("creating IRSA ClusterSecretStore %s", s.framework.Namespace.Name)
|
||||||
secretStore := &esv1beta1.ClusterSecretStore{
|
secretStore := &esv1beta1.ClusterSecretStore{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: common.ReferencedIRSAStoreName(s.framework),
|
Name: awscommon.ReferencedIRSAStoreName(s.framework),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
|
_, err := controllerutil.CreateOrUpdate(context.Background(), s.framework.CRClient, secretStore, func() error {
|
||||||
|
@ -195,3 +200,11 @@ func (s *Provider) SetupReferencedIRSAStore() {
|
||||||
})
|
})
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Provider) TeardownReferencedIRSAStore() {
|
||||||
|
s.framework.CRClient.Delete(context.Background(), &esv1beta1.ClusterSecretStore{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: awscommon.ReferencedIRSAStoreName(s.framework),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -20,7 +20,14 @@ import (
|
||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
|
||||||
"github.com/external-secrets/external-secrets-e2e/framework"
|
"github.com/external-secrets/external-secrets-e2e/framework"
|
||||||
|
awscommon "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/aws"
|
||||||
"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
|
"github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
|
||||||
|
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
withStaticAuth = "with static auth"
|
||||||
|
withReferentStaticAuth = "with static referent auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
|
var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
|
||||||
|
@ -30,23 +37,38 @@ var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
|
||||||
DescribeTable("sync secrets",
|
DescribeTable("sync secrets",
|
||||||
framework.TableFunc(f,
|
framework.TableFunc(f,
|
||||||
prov),
|
prov),
|
||||||
Entry(common.SimpleDataSync(f)),
|
framework.Compose(withStaticAuth, f, common.SimpleDataSync, useStaticAuth),
|
||||||
Entry(common.NestedJSONWithGJSON(f)),
|
framework.Compose(withStaticAuth, f, common.NestedJSONWithGJSON, useStaticAuth),
|
||||||
Entry(common.JSONDataFromSync(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataFromSync, useStaticAuth),
|
||||||
Entry(common.JSONDataFromRewrite(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataFromRewrite, useStaticAuth),
|
||||||
Entry(common.JSONDataWithProperty(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithProperty, useStaticAuth),
|
||||||
Entry(common.JSONDataWithTemplate(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithTemplate, useStaticAuth),
|
||||||
Entry(common.DockerJSONConfig(f)),
|
framework.Compose(withStaticAuth, f, common.DockerJSONConfig, useStaticAuth),
|
||||||
Entry(common.DataPropertyDockerconfigJSON(f)),
|
framework.Compose(withStaticAuth, f, common.DataPropertyDockerconfigJSON, useStaticAuth),
|
||||||
Entry(common.SSHKeySync(f)),
|
framework.Compose(withStaticAuth, f, common.SSHKeySync, useStaticAuth),
|
||||||
Entry(common.SSHKeySyncDataProperty(f)),
|
framework.Compose(withStaticAuth, f, common.SSHKeySyncDataProperty, useStaticAuth),
|
||||||
Entry(common.SyncWithoutTargetName(f)),
|
framework.Compose(withStaticAuth, f, common.SyncWithoutTargetName, useStaticAuth),
|
||||||
Entry(common.JSONDataWithoutTargetName(f)),
|
framework.Compose(withStaticAuth, f, common.JSONDataWithoutTargetName, useStaticAuth),
|
||||||
Entry(common.FindByName(f)),
|
framework.Compose(withStaticAuth, f, common.FindByName, useStaticAuth),
|
||||||
Entry(common.FindByNameWithPath(f)),
|
framework.Compose(withStaticAuth, f, common.FindByNameWithPath, useStaticAuth),
|
||||||
Entry(common.FindByTag(f)),
|
framework.Compose(withStaticAuth, f, common.FindByTag, useStaticAuth),
|
||||||
Entry(common.FindByTagWithPath(f)),
|
framework.Compose(withStaticAuth, f, common.FindByTagWithPath, useStaticAuth),
|
||||||
Entry(common.SyncV1Alpha1(f)),
|
framework.Compose(withStaticAuth, f, common.SyncV1Alpha1, useStaticAuth),
|
||||||
Entry(common.DeletionPolicyDelete(f)),
|
framework.Compose(withStaticAuth, f, common.DeletionPolicyDelete, useStaticAuth),
|
||||||
|
|
||||||
|
// referent auth
|
||||||
|
framework.Compose(withStaticAuth, f, common.SimpleDataSync, useReferentStaticAuth),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func useStaticAuth(tc *framework.TestCase) {
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
|
||||||
|
if tc.ExternalSecretV1Alpha1 != nil {
|
||||||
|
tc.ExternalSecretV1Alpha1.Spec.SecretStoreRef.Name = awscommon.StaticStoreName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func useReferentStaticAuth(tc *framework.TestCase) {
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Name = awscommon.ReferentSecretStoreName(tc.Framework)
|
||||||
|
tc.ExternalSecret.Spec.SecretStoreRef.Kind = esapi.ClusterSecretStoreKind
|
||||||
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
|
||||||
// use credentials via service account token
|
// use credentials via service account token
|
||||||
jwtAuth := prov.Auth.JWTAuth
|
jwtAuth := prov.Auth.JWTAuth
|
||||||
if jwtAuth != nil {
|
if jwtAuth != nil {
|
||||||
creds, err = sessionFromServiceAccount(ctx, prov.Auth, prov.Region, isClusterKind, kube, namespace, jwtProvider)
|
creds, err = credsFromServiceAccount(ctx, prov.Auth, prov.Region, isClusterKind, kube, namespace, jwtProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
|
||||||
secretRef := prov.Auth.SecretRef
|
secretRef := prov.Auth.SecretRef
|
||||||
if secretRef != nil {
|
if secretRef != nil {
|
||||||
log.V(1).Info("using credentials from secretRef")
|
log.V(1).Info("using credentials from secretRef")
|
||||||
creds, err = sessionFromSecretRef(ctx, prov.Auth, isClusterKind, kube, namespace)
|
creds, err = credsFromSecretRef(ctx, prov.Auth, isClusterKind, kube, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
|
||||||
// use credentials via service account token
|
// use credentials via service account token
|
||||||
jwtAuth := auth.JWTAuth
|
jwtAuth := auth.JWTAuth
|
||||||
if jwtAuth != nil {
|
if jwtAuth != nil {
|
||||||
creds, err = sessionFromServiceAccount(ctx, auth, region, false, kube, namespace, jwtProvider)
|
creds, err = credsFromServiceAccount(ctx, auth, region, false, kube, namespace, jwtProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
|
||||||
secretRef := auth.SecretRef
|
secretRef := auth.SecretRef
|
||||||
if secretRef != nil {
|
if secretRef != nil {
|
||||||
log.V(1).Info("using credentials from secretRef")
|
log.V(1).Info("using credentials from secretRef")
|
||||||
creds, err = sessionFromSecretRef(ctx, auth, false, kube, namespace)
|
creds, err = credsFromSecretRef(ctx, auth, false, kube, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -178,16 +178,16 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isClusterKind bool, kube client.Client, namespace string) (*credentials.Credentials, error) {
|
// credsFromSecretRef pulls access-key / secret-access-key from a secretRef to
|
||||||
|
// construct a aws.Credentials object
|
||||||
|
// The namespace of the external secret is used if the ClusterSecretStore does not specify a namespace (referentAuth)
|
||||||
|
// If the ClusterSecretStore defines a namespace it will take precedence.
|
||||||
|
func credsFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isClusterKind bool, kube client.Client, namespace string) (*credentials.Credentials, error) {
|
||||||
ke := client.ObjectKey{
|
ke := client.ObjectKey{
|
||||||
Name: auth.SecretRef.AccessKeyID.Name,
|
Name: auth.SecretRef.AccessKeyID.Name,
|
||||||
Namespace: namespace, // default to ExternalSecret namespace
|
Namespace: namespace,
|
||||||
}
|
}
|
||||||
// only ClusterStore is allowed to set namespace (and then it's required)
|
if isClusterKind && auth.SecretRef.AccessKeyID.Namespace != nil {
|
||||||
if isClusterKind {
|
|
||||||
if auth.SecretRef.AccessKeyID.Namespace == nil {
|
|
||||||
return nil, fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace)
|
|
||||||
}
|
|
||||||
ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace
|
ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace
|
||||||
}
|
}
|
||||||
akSecret := v1.Secret{}
|
akSecret := v1.Secret{}
|
||||||
|
@ -197,13 +197,9 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
|
||||||
}
|
}
|
||||||
ke = client.ObjectKey{
|
ke = client.ObjectKey{
|
||||||
Name: auth.SecretRef.SecretAccessKey.Name,
|
Name: auth.SecretRef.SecretAccessKey.Name,
|
||||||
Namespace: namespace, // default to ExternalSecret namespace
|
Namespace: namespace,
|
||||||
}
|
}
|
||||||
// only ClusterStore is allowed to set namespace (and then it's required)
|
if isClusterKind && auth.SecretRef.SecretAccessKey.Namespace != nil {
|
||||||
if isClusterKind {
|
|
||||||
if auth.SecretRef.SecretAccessKey.Namespace == nil {
|
|
||||||
return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
|
|
||||||
}
|
|
||||||
ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace
|
ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace
|
||||||
}
|
}
|
||||||
sakSecret := v1.Secret{}
|
sakSecret := v1.Secret{}
|
||||||
|
@ -224,13 +220,9 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
|
||||||
if auth.SecretRef.SessionToken != nil {
|
if auth.SecretRef.SessionToken != nil {
|
||||||
ke = client.ObjectKey{
|
ke = client.ObjectKey{
|
||||||
Name: auth.SecretRef.SessionToken.Name,
|
Name: auth.SecretRef.SessionToken.Name,
|
||||||
Namespace: namespace, // default to ExternalSecret namespace
|
Namespace: namespace,
|
||||||
}
|
}
|
||||||
// only ClusterStore is allowed to set namespace (and then it's required)
|
if isClusterKind && auth.SecretRef.SessionToken.Namespace != nil {
|
||||||
if isClusterKind {
|
|
||||||
if auth.SecretRef.SessionToken.Namespace == nil {
|
|
||||||
return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
|
|
||||||
}
|
|
||||||
ke.Namespace = *auth.SecretRef.SessionToken.Namespace
|
ke.Namespace = *auth.SecretRef.SessionToken.Namespace
|
||||||
}
|
}
|
||||||
stSecret := v1.Secret{}
|
stSecret := v1.Secret{}
|
||||||
|
@ -244,9 +236,14 @@ func sessionFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isCluster
|
||||||
return credentials.NewStaticCredentials(aks, sak, sessionToken), err
|
return credentials.NewStaticCredentials(aks, sak, sessionToken), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func sessionFromServiceAccount(ctx context.Context, auth esv1beta1.AWSAuth, region string, isClusterKind bool, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
|
// credsFromServiceAccount uses a Kubernetes Service Account to acquire temporary
|
||||||
|
// credentials using aws.AssumeRoleWithWebIdentity. It will assume the role defined
|
||||||
|
// in the ServiceAccount annotation.
|
||||||
|
// If the ClusterSecretStore does not define a namespace it will use the namespace from the ExternalSecret (referentAuth).
|
||||||
|
// If the ClusterSecretStore defines the namespace it will take precedence.
|
||||||
|
func credsFromServiceAccount(ctx context.Context, auth esv1beta1.AWSAuth, region string, isClusterKind bool, kube client.Client, namespace string, jwtProvider jwtProviderFactory) (*credentials.Credentials, error) {
|
||||||
name := auth.JWTAuth.ServiceAccountRef.Name
|
name := auth.JWTAuth.ServiceAccountRef.Name
|
||||||
if isClusterKind {
|
if isClusterKind && auth.JWTAuth.ServiceAccountRef.Namespace != nil {
|
||||||
namespace = *auth.JWTAuth.ServiceAccountRef.Namespace
|
namespace = *auth.JWTAuth.ServiceAccountRef.Namespace
|
||||||
}
|
}
|
||||||
sa := v1.ServiceAccount{}
|
sa := v1.ServiceAccount{}
|
||||||
|
@ -327,7 +324,7 @@ func DefaultSTSProvider(sess *session.Session) stsiface.STSAPI {
|
||||||
return sts.New(sess)
|
return sts.New(sess)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAWSSession check if an AWS session should be reused
|
// getAWSSession checks if an AWS session should be reused
|
||||||
// it returns the aws session or an error.
|
// it returns the aws session or an error.
|
||||||
func getAWSSession(config *aws.Config, enableCache bool, name, kind, namespace, resourceVersion string) (*session.Session, error) {
|
func getAWSSession(config *aws.Config, enableCache bool, name, kind, namespace, resourceVersion string) (*session.Session, error) {
|
||||||
tmpSession := SessionCache{
|
tmpSession := SessionCache{
|
||||||
|
|
|
@ -311,7 +311,7 @@ func TestNewSession(t *testing.T) {
|
||||||
expectedSecretKey: "2222",
|
expectedSecretKey: "2222",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "namespace is mandatory when using ClusterStore with SecretKeySelector",
|
name: "ClusterStore should use credentials from a ExternalSecret namespace (referentAuth)",
|
||||||
namespace: esNamespaceKey,
|
namespace: esNamespaceKey,
|
||||||
store: &esv1beta1.ClusterSecretStore{
|
store: &esv1beta1.ClusterSecretStore{
|
||||||
TypeMeta: metav1.TypeMeta{
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
@ -337,7 +337,21 @@ func TestNewSession(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expectErr: "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace",
|
secrets: []v1.Secret{
|
||||||
|
{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "onesecret",
|
||||||
|
Namespace: esNamespaceKey,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"one": []byte("7777"),
|
||||||
|
"two": []byte("4444"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectProvider: true,
|
||||||
|
expectedKeyID: "7777",
|
||||||
|
expectedSecretKey: "4444",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jwt auth via cluster secret store",
|
name: "jwt auth via cluster secret store",
|
||||||
|
|
|
@ -42,8 +42,9 @@ var (
|
||||||
|
|
||||||
// ParameterStore is a provider for AWS ParameterStore.
|
// ParameterStore is a provider for AWS ParameterStore.
|
||||||
type ParameterStore struct {
|
type ParameterStore struct {
|
||||||
sess *session.Session
|
sess *session.Session
|
||||||
client PMInterface
|
client PMInterface
|
||||||
|
referentAuth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// PMInterface is a subset of the parameterstore api.
|
// PMInterface is a subset of the parameterstore api.
|
||||||
|
@ -61,10 +62,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// New constructs a ParameterStore Provider that is specific to a store.
|
// New constructs a ParameterStore Provider that is specific to a store.
|
||||||
func New(sess *session.Session, cfg *aws.Config) (*ParameterStore, error) {
|
func New(sess *session.Session, cfg *aws.Config, referentAuth bool) (*ParameterStore, error) {
|
||||||
return &ParameterStore{
|
return &ParameterStore{
|
||||||
sess: sess,
|
sess: sess,
|
||||||
client: ssm.New(sess, cfg),
|
referentAuth: referentAuth,
|
||||||
|
client: ssm.New(sess, cfg),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,6 +379,11 @@ func (pm *ParameterStore) Close(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
|
func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
|
// skip validation stack because it depends on the namespace
|
||||||
|
// of the ExternalSecret
|
||||||
|
if pm.referentAuth {
|
||||||
|
return esv1beta1.ValidationResultUnknown, nil
|
||||||
|
}
|
||||||
_, err := pm.sess.Config.Credentials.Get()
|
_, err := pm.sess.Config.Credentials.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return esv1beta1.ValidationResultError, err
|
return esv1beta1.ValidationResultError, err
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
awsclient "github.com/aws/aws-sdk-go/aws/client"
|
awsclient "github.com/aws/aws-sdk-go/aws/client"
|
||||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||||
"github.com/aws/aws-sdk-go/aws/request"
|
"github.com/aws/aws-sdk-go/aws/request"
|
||||||
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
|
||||||
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
|
||||||
|
@ -68,17 +69,22 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
|
||||||
|
|
||||||
// case: static credentials
|
// case: static credentials
|
||||||
if prov.Auth.SecretRef != nil {
|
if prov.Auth.SecretRef != nil {
|
||||||
if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.AccessKeyID); err != nil {
|
if err := utils.ValidateReferentSecretSelector(store, prov.Auth.SecretRef.AccessKeyID); err != nil {
|
||||||
return fmt.Errorf("invalid Auth.SecretRef.AccessKeyID: %w", err)
|
return fmt.Errorf("invalid Auth.SecretRef.AccessKeyID: %w", err)
|
||||||
}
|
}
|
||||||
if err := utils.ValidateSecretSelector(store, prov.Auth.SecretRef.SecretAccessKey); err != nil {
|
if err := utils.ValidateReferentSecretSelector(store, prov.Auth.SecretRef.SecretAccessKey); err != nil {
|
||||||
return fmt.Errorf("invalid Auth.SecretRef.SecretAccessKey: %w", err)
|
return fmt.Errorf("invalid Auth.SecretRef.SecretAccessKey: %w", err)
|
||||||
}
|
}
|
||||||
|
if prov.Auth.SecretRef.SessionToken != nil {
|
||||||
|
if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.SecretRef.SessionToken); err != nil {
|
||||||
|
return fmt.Errorf("invalid Auth.SecretRef.SessionToken: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// case: jwt credentials
|
// case: jwt credentials
|
||||||
if prov.Auth.JWTAuth != nil && prov.Auth.JWTAuth.ServiceAccountRef != nil {
|
if prov.Auth.JWTAuth != nil && prov.Auth.JWTAuth.ServiceAccountRef != nil {
|
||||||
if err := utils.ValidateServiceAccountSelector(store, *prov.Auth.JWTAuth.ServiceAccountRef); err != nil {
|
if err := utils.ValidateReferentServiceAccountSelector(store, *prov.Auth.JWTAuth.ServiceAccountRef); err != nil {
|
||||||
return fmt.Errorf("invalid Auth.JWT.ServiceAccountRef: %w", err)
|
return fmt.Errorf("invalid Auth.JWT.ServiceAccountRef: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,6 +120,21 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
|
||||||
storeSpec := store.GetSpec()
|
storeSpec := store.GetSpec()
|
||||||
var cfg *aws.Config
|
var cfg *aws.Config
|
||||||
|
|
||||||
|
// allow SecretStore controller validation to pass
|
||||||
|
// when using referent namespace.
|
||||||
|
if util.IsReferentSpec(prov.Auth) && namespace == "" &&
|
||||||
|
store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind {
|
||||||
|
cfg = aws.NewConfig().WithRegion("eu-west-1").WithEndpointResolver(awsauth.ResolveEndpoint())
|
||||||
|
sess := &session.Session{Config: cfg}
|
||||||
|
switch prov.Service {
|
||||||
|
case esv1beta1.AWSServiceSecretsManager:
|
||||||
|
return secretsmanager.New(sess, cfg, true)
|
||||||
|
case esv1beta1.AWSServiceParameterStore:
|
||||||
|
return parameterstore.New(sess, cfg, true)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
|
||||||
|
}
|
||||||
|
|
||||||
sess, err := awsauth.New(ctx, store, kube, namespace, assumeRoler, awsauth.DefaultJWTProvider)
|
sess, err := awsauth.New(ctx, store, kube, namespace, assumeRoler, awsauth.DefaultJWTProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(errUnableCreateSession, err)
|
return nil, fmt.Errorf(errUnableCreateSession, err)
|
||||||
|
@ -146,9 +167,9 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
|
||||||
|
|
||||||
switch prov.Service {
|
switch prov.Service {
|
||||||
case esv1beta1.AWSServiceSecretsManager:
|
case esv1beta1.AWSServiceSecretsManager:
|
||||||
return secretsmanager.New(sess, cfg)
|
return secretsmanager.New(sess, cfg, false)
|
||||||
case esv1beta1.AWSServiceParameterStore:
|
case esv1beta1.AWSServiceParameterStore:
|
||||||
return parameterstore.New(sess, cfg)
|
return parameterstore.New(sess, cfg, false)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
|
return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
|
||||||
}
|
}
|
||||||
|
|
|
@ -238,8 +238,8 @@ func TestValidateStore(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid static creds auth / SecretAccessKey missing namespace",
|
name: "referentAuth static creds / SecretAccessKey without namespace",
|
||||||
wantErr: true,
|
wantErr: false,
|
||||||
args: args{
|
args: args{
|
||||||
store: &esv1beta1.ClusterSecretStore{
|
store: &esv1beta1.ClusterSecretStore{
|
||||||
TypeMeta: v1.TypeMeta{
|
TypeMeta: v1.TypeMeta{
|
||||||
|
@ -263,8 +263,8 @@ func TestValidateStore(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid static creds auth / AccessKeyID missing namespace",
|
name: "referentAuth static creds / AccessKeyID without namespace",
|
||||||
wantErr: true,
|
wantErr: false,
|
||||||
args: args{
|
args: args{
|
||||||
store: &esv1beta1.ClusterSecretStore{
|
store: &esv1beta1.ClusterSecretStore{
|
||||||
TypeMeta: v1.TypeMeta{
|
TypeMeta: v1.TypeMeta{
|
||||||
|
@ -288,8 +288,8 @@ func TestValidateStore(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid jwt auth: missing sa selector namespace",
|
name: "referentAuth jwt: sa selector without namespace",
|
||||||
wantErr: true,
|
wantErr: false,
|
||||||
args: args{
|
args: args{
|
||||||
store: &esv1beta1.ClusterSecretStore{
|
store: &esv1beta1.ClusterSecretStore{
|
||||||
TypeMeta: v1.TypeMeta{
|
TypeMeta: v1.TypeMeta{
|
||||||
|
|
|
@ -41,9 +41,10 @@ var _ esv1beta1.SecretsClient = &SecretsManager{}
|
||||||
|
|
||||||
// SecretsManager is a provider for AWS SecretsManager.
|
// SecretsManager is a provider for AWS SecretsManager.
|
||||||
type SecretsManager struct {
|
type SecretsManager struct {
|
||||||
sess *session.Session
|
sess *session.Session
|
||||||
client SMInterface
|
client SMInterface
|
||||||
cache map[string]*awssm.GetSecretValueOutput
|
referentAuth bool
|
||||||
|
cache map[string]*awssm.GetSecretValueOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMInterface is a subset of the smiface api.
|
// SMInterface is a subset of the smiface api.
|
||||||
|
@ -67,11 +68,12 @@ const (
|
||||||
var log = ctrl.Log.WithName("provider").WithName("aws").WithName("secretsmanager")
|
var log = ctrl.Log.WithName("provider").WithName("aws").WithName("secretsmanager")
|
||||||
|
|
||||||
// New creates a new SecretsManager client.
|
// New creates a new SecretsManager client.
|
||||||
func New(sess *session.Session, cfg *aws.Config) (*SecretsManager, error) {
|
func New(sess *session.Session, cfg *aws.Config, referentAuth bool) (*SecretsManager, error) {
|
||||||
return &SecretsManager{
|
return &SecretsManager{
|
||||||
sess: sess,
|
sess: sess,
|
||||||
client: awssm.New(sess, cfg),
|
client: awssm.New(sess, cfg),
|
||||||
cache: make(map[string]*awssm.GetSecretValueOutput),
|
referentAuth: referentAuth,
|
||||||
|
cache: make(map[string]*awssm.GetSecretValueOutput),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,6 +409,11 @@ func (sm *SecretsManager) Close(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *SecretsManager) Validate() (esv1beta1.ValidationResult, error) {
|
func (sm *SecretsManager) Validate() (esv1beta1.ValidationResult, error) {
|
||||||
|
// skip validation stack because it depends on the namespace
|
||||||
|
// of the ExternalSecret
|
||||||
|
if sm.referentAuth {
|
||||||
|
return esv1beta1.ValidationResultUnknown, nil
|
||||||
|
}
|
||||||
_, err := sm.sess.Config.Credentials.Get()
|
_, err := sm.sess.Config.Credentials.Get()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return esv1beta1.ValidationResultError, err
|
return esv1beta1.ValidationResultError, err
|
||||||
|
|
|
@ -45,3 +45,17 @@ func GetAWSProvider(store esv1beta1.GenericStore) (*esv1beta1.AWSProvider, error
|
||||||
}
|
}
|
||||||
return prov, nil
|
return prov, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsReferentSpec(prov esv1beta1.AWSAuth) bool {
|
||||||
|
if prov.JWTAuth != nil && prov.JWTAuth.ServiceAccountRef != nil && prov.JWTAuth.ServiceAccountRef.Namespace == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if prov.SecretRef != nil &&
|
||||||
|
(prov.SecretRef.AccessKeyID.Namespace == nil ||
|
||||||
|
prov.SecretRef.SecretAccessKey.Namespace == nil ||
|
||||||
|
(prov.SecretRef.SessionToken != nil && prov.SecretRef.SessionToken.Namespace == nil)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ provider "aws" {
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
name = var.cluster_name
|
name = var.cluster_name
|
||||||
cluster_version = "1.22"
|
cluster_version = "1.24"
|
||||||
region = var.cluster_region
|
region = var.cluster_region
|
||||||
|
|
||||||
serviceaccount_name = var.irsa_sa_name
|
serviceaccount_name = var.irsa_sa_name
|
||||||
|
|
Loading…
Reference in a new issue