* Secrets: changes to allow a 3rd party keeper / secret references * fix test * make gofmt * lint * fix tests * assign aws secrets manager to @grafana/grafana-operator-experience-squad * rename Keeper.Reference to Keeper.RetrieveReference * rename ModelSecretsManager to ModelAWSSecretsManager * validator: ensure that only one of keeper.Spec.Aws.AccessKey or keeper.Spec.Aws.AssumeRole are set * move secrets manager dep / go mod tidy * move secrets manager dep * keeper validator: move 3rd party secret stores validation to their own functions * add github.com/aws/aws-sdk-go-v2/service/secretsmanager pkg/extensions/enterprise_imports * make update-workspace * undo go.mod changes in /apps * make update-workspace * fix test * add github.com/aws/aws-sdk-go-v2/service/secretsmanager to enterprise_imports * make update-workspace * gcworker: handle refs * make update-workspace * create toggle: FeatureStageExperimental * allow features.IsEnabled for now * format
370 lines
12 KiB
Go
370 lines
12 KiB
Go
package validator
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apiserver/pkg/admission"
|
|
"k8s.io/utils/ptr"
|
|
|
|
secretv1beta1 "github.com/grafana/grafana/apps/secret/pkg/apis/secret/v1beta1"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
)
|
|
|
|
func TestValidateKeeper(t *testing.T) {
|
|
objectMeta := metav1.ObjectMeta{Name: "test", Namespace: "test"}
|
|
validator := ProvideKeeperValidator(featuremgmt.WithFeatures(featuremgmt.FlagSecretsManagementAppPlatformAwsKeeper))
|
|
|
|
t.Run("when creating a new keeper", func(t *testing.T) {
|
|
t.Run("the `description` must be present", func(t *testing.T) {
|
|
keeper := &secretv1beta1.Keeper{
|
|
ObjectMeta: objectMeta,
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Aws: &secretv1beta1.KeeperAWSConfig{
|
|
Region: "us-east-1",
|
|
AccessKey: &secretv1beta1.KeeperAWSAccessKey{
|
|
AccessKeyID: secretv1beta1.KeeperCredentialValue{ValueFromEnv: "some-value"},
|
|
SecretAccessKey: secretv1beta1.KeeperCredentialValue{ValueFromEnv: "some-value"},
|
|
},
|
|
KmsKeyID: ptr.To("kms-key-id"),
|
|
},
|
|
},
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.description", errs[0].Field)
|
|
})
|
|
})
|
|
|
|
t.Run("aws keeper validation", func(t *testing.T) {
|
|
validKeeperAWS := &secretv1beta1.Keeper{
|
|
ObjectMeta: objectMeta,
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
Aws: &secretv1beta1.KeeperAWSConfig{
|
|
Region: "us-east-1",
|
|
AccessKey: &secretv1beta1.KeeperAWSAccessKey{
|
|
AccessKeyID: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromEnv: "some-value",
|
|
},
|
|
SecretAccessKey: secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "some-value",
|
|
},
|
|
},
|
|
KmsKeyID: ptr.To("optional"),
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("aws keeper feature flag must be enabled", func(t *testing.T) {
|
|
// Validator with feature disabled
|
|
validator := ProvideKeeperValidator(featuremgmt.WithFeatures())
|
|
errs := validator.Validate(validKeeperAWS.DeepCopy(), nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws", errs[0].Field)
|
|
require.Contains(t, errs[0].Detail, "secretsManagementAppPlatformAwsKeeper")
|
|
})
|
|
|
|
t.Run("`accessKeyID` must be present", func(t *testing.T) {
|
|
t.Run("at least one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAWS.DeepCopy()
|
|
keeper.Spec.Aws.AccessKey.AccessKeyID = secretv1beta1.KeeperCredentialValue{}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws.accessKey.accessKeyID", errs[0].Field)
|
|
})
|
|
|
|
t.Run("at most one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAWS.DeepCopy()
|
|
keeper.Spec.Aws.AccessKey.AccessKeyID = secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "a",
|
|
ValueFromEnv: "b",
|
|
ValueFromConfig: "c",
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws.accessKey.accessKeyID", errs[0].Field)
|
|
})
|
|
})
|
|
|
|
t.Run("`secretAccessKey` must be present", func(t *testing.T) {
|
|
t.Run("at least one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAWS.DeepCopy()
|
|
keeper.Spec.Aws.AccessKey.SecretAccessKey = secretv1beta1.KeeperCredentialValue{}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws.accessKey.secretAccessKey", errs[0].Field)
|
|
})
|
|
|
|
t.Run("at most one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAWS.DeepCopy()
|
|
keeper.Spec.Aws.AccessKey.SecretAccessKey = secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "a",
|
|
ValueFromEnv: "b",
|
|
ValueFromConfig: "c",
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws.accessKey.secretAccessKey", errs[0].Field)
|
|
})
|
|
|
|
t.Run("only one of accessKey or assumeRole can be present", func(t *testing.T) {
|
|
keeper := validKeeperAWS.DeepCopy()
|
|
keeper.Spec.Aws.AccessKey.SecretAccessKey = secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "a",
|
|
}
|
|
keeper.Spec.Aws.AssumeRole = &secretv1beta1.KeeperAWSAssumeRole{
|
|
AssumeRoleArn: "arn",
|
|
ExternalID: "id",
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.aws", errs[0].Field)
|
|
require.Equal(t, "only one of `accessKey` or `assumeRole` can be present", errs[0].Detail)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("azure keeper validation", func(t *testing.T) {
|
|
validKeeperAzure := &secretv1beta1.Keeper{
|
|
ObjectMeta: objectMeta,
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
Azure: &secretv1beta1.KeeperAzureConfig{
|
|
KeyVaultName: "kv-name",
|
|
TenantID: "tenant-id",
|
|
ClientID: "client-id",
|
|
ClientSecret: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromConfig: "config.path.value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("`keyVaultName` must be present", func(t *testing.T) {
|
|
keeper := validKeeperAzure.DeepCopy()
|
|
keeper.Spec.Azure.KeyVaultName = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.azure.keyVaultName", errs[0].Field)
|
|
})
|
|
|
|
t.Run("`tenantID` must be present", func(t *testing.T) {
|
|
keeper := validKeeperAzure.DeepCopy()
|
|
keeper.Spec.Azure.TenantID = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.azure.tenantID", errs[0].Field)
|
|
})
|
|
|
|
t.Run("`clientID` must be present", func(t *testing.T) {
|
|
keeper := validKeeperAzure.DeepCopy()
|
|
keeper.Spec.Azure.ClientID = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.azure.clientID", errs[0].Field)
|
|
})
|
|
|
|
t.Run("`clientSecret` must be present", func(t *testing.T) {
|
|
t.Run("at least one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAzure.DeepCopy()
|
|
keeper.Spec.Azure.ClientSecret = secretv1beta1.KeeperCredentialValue{}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
|
|
})
|
|
|
|
t.Run("at most one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperAzure.DeepCopy()
|
|
keeper.Spec.Azure.ClientSecret = secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "a",
|
|
ValueFromEnv: "b",
|
|
ValueFromConfig: "c",
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.azure.clientSecret", errs[0].Field)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("gcp keeper validation", func(t *testing.T) {
|
|
validKeeperGCP := &secretv1beta1.Keeper{
|
|
ObjectMeta: objectMeta,
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
Gcp: &secretv1beta1.KeeperGCPConfig{
|
|
ProjectID: "project-id",
|
|
CredentialsFile: "/path/to/credentials/file.json",
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("`projectID` must be present", func(t *testing.T) {
|
|
keeper := validKeeperGCP.DeepCopy()
|
|
keeper.Spec.Gcp.ProjectID = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.gcp.projectID", errs[0].Field)
|
|
})
|
|
|
|
t.Run("`credentialsFile` must be present", func(t *testing.T) {
|
|
keeper := validKeeperGCP.DeepCopy()
|
|
keeper.Spec.Gcp.CredentialsFile = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.gcp.credentialsFile", errs[0].Field)
|
|
})
|
|
})
|
|
|
|
t.Run("hashicorp keeper validation", func(t *testing.T) {
|
|
validKeeperHashiCorp := &secretv1beta1.Keeper{
|
|
ObjectMeta: objectMeta,
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
HashiCorpVault: &secretv1beta1.KeeperHashiCorpConfig{
|
|
Address: "http://address",
|
|
Token: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromConfig: "config.path.value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
t.Run("`address` must be present", func(t *testing.T) {
|
|
keeper := validKeeperHashiCorp.DeepCopy()
|
|
keeper.Spec.HashiCorpVault.Address = ""
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.hashiCorpVault.address", errs[0].Field)
|
|
})
|
|
|
|
t.Run("`token` must be present", func(t *testing.T) {
|
|
t.Run("at least one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperHashiCorp.DeepCopy()
|
|
keeper.Spec.HashiCorpVault.Token = secretv1beta1.KeeperCredentialValue{}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field)
|
|
})
|
|
|
|
t.Run("at most one of the credential value must be present", func(t *testing.T) {
|
|
keeper := validKeeperHashiCorp.DeepCopy()
|
|
keeper.Spec.HashiCorpVault.Token = secretv1beta1.KeeperCredentialValue{
|
|
SecureValueName: "a",
|
|
ValueFromEnv: "b",
|
|
ValueFromConfig: "c",
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "spec.hashiCorpVault.token", errs[0].Field)
|
|
})
|
|
})
|
|
})
|
|
|
|
t.Run("invalid name", func(t *testing.T) {
|
|
keeper := &secretv1beta1.Keeper{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Namespace: objectMeta.Namespace,
|
|
},
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
HashiCorpVault: &secretv1beta1.KeeperHashiCorpConfig{
|
|
Address: "http://address",
|
|
Token: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromConfig: "config.path.value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
keeper.Name = ""
|
|
errs := validator.Validate(keeper, nil, admission.Delete)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.name", errs[0].Field)
|
|
|
|
keeper.Name = "invalid/name-"
|
|
errs = validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.name", errs[0].Field)
|
|
|
|
keeper.Name = strings.Repeat("a", 253+1)
|
|
errs = validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.name", errs[0].Field)
|
|
})
|
|
|
|
t.Run("invalid namespace", func(t *testing.T) {
|
|
keeper := &secretv1beta1.Keeper{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: objectMeta.Name,
|
|
},
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
HashiCorpVault: &secretv1beta1.KeeperHashiCorpConfig{
|
|
Address: "http://address",
|
|
Token: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromConfig: "config.path.value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
keeper.Namespace = ""
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.namespace", errs[0].Field)
|
|
|
|
keeper.Namespace = "invalid/namespace-"
|
|
errs = validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.namespace", errs[0].Field)
|
|
|
|
keeper.Namespace = strings.Repeat("a", 253+1)
|
|
errs = validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "metadata.namespace", errs[0].Field)
|
|
})
|
|
|
|
t.Run("keeper name `system` is reserved", func(t *testing.T) {
|
|
keeper := &secretv1beta1.Keeper{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "system",
|
|
Namespace: "ns1",
|
|
},
|
|
Spec: secretv1beta1.KeeperSpec{
|
|
Description: "description",
|
|
HashiCorpVault: &secretv1beta1.KeeperHashiCorpConfig{
|
|
Address: "http://address",
|
|
Token: secretv1beta1.KeeperCredentialValue{
|
|
ValueFromConfig: "config.path.value",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
errs := validator.Validate(keeper, nil, admission.Create)
|
|
require.Len(t, errs, 1)
|
|
require.Equal(t, "name", errs[0].Field)
|
|
require.Equal(t, "the keeper name `system` is reserved", errs[0].Detail)
|
|
})
|
|
}
|