1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-15 17:51:01 +00:00

Merge pull request #896 from burak-yuksel/feature/validate-kubernetes-provider

Validate for Kubernetes Provider
This commit is contained in:
paul-the-alien[bot] 2022-03-31 07:20:17 +00:00 committed by GitHub
commit 8527fe1d13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 3 deletions

View file

@ -4,6 +4,8 @@ External Secrets Operator allows to retrieve in-cluster secrets or from a remote
It's possible to authenticate against the Kubernetes API using client certificates, a bearer token or a service account (not implemented yet). The operator enforces that exactly one authentication method is used.
**NOTE:** `SelfSubjectAccessReview` permission is required for the service account in order to validation work properly.
## Example
### In-cluster secrets using Client certificates

View file

@ -18,6 +18,7 @@ import (
"context"
"fmt"
authv1 "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
@ -44,9 +45,15 @@ type KClient interface {
Get(ctx context.Context, name string, opts metav1.GetOptions) (*corev1.Secret, error)
}
type RClient interface {
Create(ctx context.Context, SelfSubjectAccessReview *authv1.SelfSubjectAccessReview, opts metav1.CreateOptions) (*authv1.SelfSubjectAccessReview, error)
}
// ProviderKubernetes is a provider for Kubernetes.
type ProviderKubernetes struct {
Client KClient
ReviewClient RClient
Namespace string
}
var _ esv1beta1.SecretsClient = &ProviderKubernetes{}
@ -104,6 +111,8 @@ func (k *ProviderKubernetes) NewClient(ctx context.Context, store esv1beta1.Gene
}
k.Client = kubeClientSet.CoreV1().Secrets(bStore.store.RemoteNamespace)
k.Namespace = bStore.store.RemoteNamespace
k.ReviewClient = kubeClientSet.AuthorizationV1().SelfSubjectAccessReviews()
return k, nil
}
@ -229,6 +238,26 @@ func (k *BaseClient) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySel
}
func (k *ProviderKubernetes) Validate() error {
ctx := context.Background()
authReview, err := k.ReviewClient.Create(ctx, &authv1.SelfSubjectAccessReview{
Spec: authv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authv1.ResourceAttributes{
Resource: "secrets",
Namespace: k.Namespace,
Verb: "get",
},
},
}, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("could not verify if client is valid: %w", err)
}
if !authReview.Status.Allowed {
return fmt.Errorf("client is not allowed to get secrets")
}
return nil
}

View file

@ -21,6 +21,7 @@ import (
"strings"
"testing"
authv1 "k8s.io/api/authorization/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
@ -32,6 +33,7 @@ import (
const (
errTestFetchCredentialsSecret = "test could not fetch Credentials secret failed"
errTestAuthValue = "test failed key didn't match expected value"
errSomethingWentWrong = "Something went wrong"
)
type fakeClient struct {
@ -42,11 +44,22 @@ func (fk fakeClient) Get(ctx context.Context, name string, opts metav1.GetOption
secret, ok := fk.secretMap[name]
if !ok {
return nil, errors.New("Something went wrong")
return nil, errors.New(errSomethingWentWrong)
}
return &secret, nil
}
type fakeReviewClient struct {
authReview *authv1.SelfSubjectAccessReview
}
func (fk fakeReviewClient) Create(ctx context.Context, selfSubjectAccessReview *authv1.SelfSubjectAccessReview, opts metav1.CreateOptions) (*authv1.SelfSubjectAccessReview, error) {
if fk.authReview == nil {
return nil, errors.New(errSomethingWentWrong)
}
return fk.authReview, nil
}
func TestKubernetesSecretManagerGetSecret(t *testing.T) {
expected := make(map[string][]byte)
value := "bar"
@ -70,7 +83,7 @@ func TestKubernetesSecretManagerGetSecret(t *testing.T) {
ref = esv1beta1.ExternalSecretDataRemoteRef{Key: "Key2", Property: "foo"}
_, err := kp.GetSecret(ctx, ref)
if err.Error() != "Something went wrong" {
if err.Error() != errSomethingWentWrong {
t.Error("test failed")
}
@ -258,3 +271,35 @@ func ErrorContains(out error, want string) bool {
}
return strings.Contains(out.Error(), want)
}
func TestValidate(t *testing.T) {
authReview := authv1.SelfSubjectAccessReview{
Status: authv1.SubjectAccessReviewStatus{
Allowed: true,
},
}
fakeClient := fakeReviewClient{authReview: &authReview}
k := ProviderKubernetes{ReviewClient: fakeClient}
err := k.Validate()
if err != nil {
t.Errorf("Test Failed! %v", err)
}
authReview = authv1.SelfSubjectAccessReview{
Status: authv1.SubjectAccessReviewStatus{
Allowed: false,
},
}
fakeClient = fakeReviewClient{authReview: &authReview}
k = ProviderKubernetes{ReviewClient: fakeClient}
err = k.Validate()
if err.Error() != "client is not allowed to get secrets" {
t.Errorf("Test Failed! Wanted client is not allowed to get secrets got: %v", err)
}
fakeClient = fakeReviewClient{}
k = ProviderKubernetes{ReviewClient: fakeClient}
err = k.Validate()
if err.Error() != "could not verify if client is valid: Something went wrong" {
t.Errorf("Test Failed! Wanted could not verify if client is valid: Something went wrong got: %v", err)
}
}