From 39f3f2e4cefd230ce47238a6fb7785299467e371 Mon Sep 17 00:00:00 2001 From: Celene Date: Tue, 5 Apr 2022 11:54:23 -0400 Subject: [PATCH] update rbac to support secret backend multiple providers script (#459) * update rbac to support secret backend multiple providers script Co-authored-by: Ursula Chen <58821586+urseberry@users.noreply.github.com> --- apis/datadoghq/v1alpha1/const.go | 1 + controllers/datadogagent/clusteragent.go | 22 ++++++ .../clusteragent_metricsserver.go | 40 +++++++---- controllers/datadogagent/common_rbac.go | 11 +++ controllers/datadogagent/common_rbac_test.go | 72 +++++++++++++++++++ docs/secret_management.md | 19 +++++ 6 files changed, 152 insertions(+), 13 deletions(-) diff --git a/apis/datadoghq/v1alpha1/const.go b/apis/datadoghq/v1alpha1/const.go index e342163133..55ad4e6948 100644 --- a/apis/datadoghq/v1alpha1/const.go +++ b/apis/datadoghq/v1alpha1/const.go @@ -45,6 +45,7 @@ const ( const ( DatadogHost = "DATADOG_HOST" DDAPIKey = "DD_API_KEY" + DDSecretBackendCommand = "DD_SECRET_BACKEND_COMMAND" DDClusterName = "DD_CLUSTER_NAME" DDSite = "DD_SITE" DDddURL = "DD_DD_URL" diff --git a/controllers/datadogagent/clusteragent.go b/controllers/datadogagent/clusteragent.go index b43781fcac..1579eadebf 100644 --- a/controllers/datadogagent/clusteragent.go +++ b/controllers/datadogagent/clusteragent.go @@ -1090,6 +1090,18 @@ func buildClusterRole(dda *datadoghqv1alpha1.DatadogAgent, needClusterLevelRBAC }, } + // If the secret backend uses the provided `/readsecret_multiple_providers.sh` script, then we need to add secrets GET permissions + if *dda.Spec.Credentials.UseSecretBackend && + (checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.Env) || checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.Config.Env) || + checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.Apm.Env) || checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.Process.Env) || + checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.SystemProbe.Env) || checkSecretBackendMultipleProvidersUsed(dda.Spec.Agent.Security.Env)) { + rbacRules = append(rbacRules, rbacv1.PolicyRule{ + APIGroups: []string{datadoghqv1alpha1.CoreAPIGroup}, + Resources: []string{datadoghqv1alpha1.SecretsResource}, + Verbs: []string{datadoghqv1alpha1.GetVerb}, + }) + } + if needClusterLevelRBAC { // Cluster Agent is disabled, the Agent needs extra permissions // to collect cluster level metrics and events @@ -1198,6 +1210,16 @@ func buildClusterAgentClusterRole(dda *datadoghqv1alpha1.DatadogAgent, name, age rbacRules = append(rbacRules, getEventCollectionPolicyRule()) } + // If the secret backend uses the provided `/readsecret_multiple_providers.sh` script, then we need to add secrets GET permissions + if *dda.Spec.Credentials.UseSecretBackend && + checkSecretBackendMultipleProvidersUsed(dda.Spec.ClusterAgent.Config.Env) { + rbacRules = append(rbacRules, rbacv1.PolicyRule{ + APIGroups: []string{datadoghqv1alpha1.CoreAPIGroup}, + Resources: []string{datadoghqv1alpha1.SecretsResource}, + Verbs: []string{datadoghqv1alpha1.GetVerb}, + }) + } + if isMetricsProviderEnabled(dda.Spec.ClusterAgent) { rbacRules = append(rbacRules, rbacv1.PolicyRule{ diff --git a/controllers/datadogagent/clusteragent_metricsserver.go b/controllers/datadogagent/clusteragent_metricsserver.go index f1bac85fd3..43c2ff78e3 100644 --- a/controllers/datadogagent/clusteragent_metricsserver.go +++ b/controllers/datadogagent/clusteragent_metricsserver.go @@ -119,25 +119,39 @@ func (r *Reconciler) updateIfNeededExternalMetricsReaderClusterRole(logger logr. // buildExternalMetricsReaderClusterRole creates a ClusterRole object for access to external metrics resources func buildExternalMetricsReaderClusterRole(dda *datadoghqv1alpha1.DatadogAgent, name, agentVersion string) *rbacv1.ClusterRole { if isMetricsProviderEnabled(dda.Spec.ClusterAgent) { - return &rbacv1.ClusterRole{ + clusterRole := &rbacv1.ClusterRole{ ObjectMeta: metav1.ObjectMeta{ - Labels: getDefaultLabels(dda, name, agentVersion), + Labels: getDefaultLabels(dda, NewPartOfLabelValue(dda).String(), agentVersion), Name: name, }, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{ - "external.metrics.k8s.io", - }, - Resources: []string{"*"}, - Verbs: []string{ - datadoghqv1alpha1.GetVerb, - datadoghqv1alpha1.ListVerb, - datadoghqv1alpha1.WatchVerb, - }, + } + + rbacRules := []rbacv1.PolicyRule{ + { + APIGroups: []string{ + "external.metrics.k8s.io", + }, + Resources: []string{"*"}, + Verbs: []string{ + datadoghqv1alpha1.GetVerb, + datadoghqv1alpha1.ListVerb, + datadoghqv1alpha1.WatchVerb, }, }, } + + // If the secret backend uses the provided `/readsecret_multiple_providers.sh` script, then we need to add secrets GET permissions + if *dda.Spec.Credentials.UseSecretBackend && + checkSecretBackendMultipleProvidersUsed(dda.Spec.ClusterAgent.Config.Env) { + rbacRules = append(rbacRules, rbacv1.PolicyRule{ + APIGroups: []string{datadoghqv1alpha1.CoreAPIGroup}, + Resources: []string{datadoghqv1alpha1.SecretsResource}, + Verbs: []string{datadoghqv1alpha1.GetVerb}, + }) + } + + clusterRole.Rules = rbacRules + return clusterRole } return nil } diff --git a/controllers/datadogagent/common_rbac.go b/controllers/datadogagent/common_rbac.go index 5c47262c5d..392f0a7370 100644 --- a/controllers/datadogagent/common_rbac.go +++ b/controllers/datadogagent/common_rbac.go @@ -20,6 +20,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +const secretBackendMultipleProvidersScript = "/readsecret_multiple_providers.sh" + // roleBindingInfo contains the required information to build a Cluster Role Binding type roleBindingInfo struct { name string @@ -308,3 +310,12 @@ func isClusterRolesBindingEqual(a, b *rbacv1.ClusterRoleBinding) bool { // by a previous Operator version. return apiequality.Semantic.DeepEqual(a.RoleRef, b.RoleRef) && apiequality.Semantic.DeepEqual(a.Subjects, b.Subjects) && apiequality.Semantic.DeepEqual(a.ObjectMeta.OwnerReferences, b.ObjectMeta.OwnerReferences) } + +func checkSecretBackendMultipleProvidersUsed(envVarList []corev1.EnvVar) bool { + for _, envVar := range envVarList { + if envVar.Name == datadoghqv1alpha1.DDSecretBackendCommand && envVar.Value == secretBackendMultipleProvidersScript { + return true + } + } + return false +} diff --git a/controllers/datadogagent/common_rbac_test.go b/controllers/datadogagent/common_rbac_test.go index 0607c52116..2ec8ba02b7 100644 --- a/controllers/datadogagent/common_rbac_test.go +++ b/controllers/datadogagent/common_rbac_test.go @@ -161,3 +161,75 @@ func newReconcilerForRbacTests(client client.Client) *Reconciler { recorder: recorder, } } + +func Test_checkSecretBackendMultipleProvidersUsed(t *testing.T) { + + tests := []struct { + name string + envVarList []corev1.EnvVar + want bool + }{ + { + name: "Secret backend multiple providers script is found", + envVarList: []corev1.EnvVar{ + { + Name: "EnvVar1", + Value: "Value1", + }, + { + Name: "EnvVar2", + Value: "Value2", + }, + { + Name: "DD_SECRET_BACKEND_COMMAND", + Value: "/readsecret_multiple_providers.sh", + }, + }, + want: true, + }, + { + name: "Script does not match", + envVarList: []corev1.EnvVar{ + { + Name: "EnvVar1", + Value: "Value1", + }, + { + Name: "EnvVar2", + Value: "Value2", + }, + { + Name: "DD_SECRET_BACKEND_COMMAND", + Value: "/readsecret.sh", + }, + }, + want: false, + }, + { + name: "EnvVar name does not match", + envVarList: []corev1.EnvVar{ + { + Name: "EnvVar1", + Value: "Value1", + }, + { + Name: "EnvVar2", + Value: "Value2", + }, + { + Name: "DD_SECRET_BACKEND_COMMANDD", + Value: "/readsecret_multiple_providers.sh", + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := checkSecretBackendMultipleProvidersUsed(tt.envVarList) + if result != tt.want { + t.Errorf("checkEnvVarMatchesValue() result is %v but want %v", result, tt.want) + } + }) + } +} diff --git a/docs/secret_management.md b/docs/secret_management.md index 0eb026e1dc..5ef212cc64 100644 --- a/docs/secret_management.md +++ b/docs/secret_management.md @@ -219,6 +219,24 @@ spec: mountPath: /etc/secret-volume ``` +The Datadog Agent also includes a script that can be used to read secrets from files mounted from Kubernetes secrets, or directly from Kubernetes secrets. This script can be used by setting `DD_SECRET_BACKEND_COMMAND` to `/readsecret_multiple_providers.sh`. An example of how to configure the DatadogAgent spec is provided below. For more details, see [Secrets Management][2]. + +```yaml +apiVersion: datadoghq.com/v1alpha1 +kind: DatadogAgent +metadata: + name: datadog +spec: + credentials: + apiKey: ENC[k8s_secret@default/test-secret/api_key] + appKey: ENC[k8s_secret@default/test-secret/app_key] + useSecretBackend: true + agent: + env: + - name: DD_SECRET_BACKEND_COMMAND + value: "/readsecret_multiple_providers.sh" +``` + **Remarks:** * For the "Agent" and "Cluster Agent", others options exist to configure secret backend command: @@ -228,3 +246,4 @@ spec: * **DD_SECRET_BACKEND_TIMEOUT**: secret backend execution timeout in second. The default value is 5 seconds. [1]: https://docs.datadoghq.com/agent/guide/secrets-management +[2]: https://docs.datadoghq.com/agent/guide/secrets-management/?tab=linux#script-for-reading-from-multiple-secret-providers