Browse Source

feat(charts): make serviceaccounts/token create RBAC rule conditional (#6295)

Co-authored-by: Gergely Bräutigam <gergely.brautigam@sap.com>
krisztian.kern 1 month ago
parent
commit
93ee3bc5d9

+ 1 - 0
deploy/charts/external-secrets/README.md

@@ -197,6 +197,7 @@ The command removes all the Kubernetes components associated with the chart and
 | rbac.aggregateToEdit | bool | `true` | Specifies whether permissions are aggregated to the edit ClusterRole |
 | rbac.aggregateToEdit | bool | `true` | Specifies whether permissions are aggregated to the edit ClusterRole |
 | rbac.aggregateToView | bool | `true` | Specifies whether permissions are aggregated to the view ClusterRole |
 | rbac.aggregateToView | bool | `true` | Specifies whether permissions are aggregated to the view ClusterRole |
 | rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. |
 | rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. |
+| rbac.serviceAccountTokenCreate | bool | `true` | Specifies whether the serviceaccounts/token create permission is included in the controller RBAC. When set to false, users must create per-ServiceAccount Role/RoleBinding with resourceNames constraint to grant ESO token creation for specific ServiceAccounts referenced in SecretStore specs. |
 | rbac.servicebindings.create | bool | `true` | Specifies whether a clusterrole to give servicebindings read access should be created. |
 | rbac.servicebindings.create | bool | `true` | Specifies whether a clusterrole to give servicebindings read access should be created. |
 | readinessProbe.enabled | bool | `false` | Determines whether the readiness probe is enabled. Disabled by default. Enabling this will auto-start the health server (--live-addr) even if livenessProbe is disabled. Health server address/port are configured via livenessProbe.spec.address and livenessProbe.spec.port. |
 | readinessProbe.enabled | bool | `false` | Determines whether the readiness probe is enabled. Disabled by default. Enabling this will auto-start the health server (--live-addr) even if livenessProbe is disabled. Health server address/port are configured via livenessProbe.spec.address and livenessProbe.spec.port. |
 | readinessProbe.spec | object | `{"failureThreshold":3,"httpGet":{"path":"/readyz","port":"live"},"initialDelaySeconds":10,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5}` | The body of the readiness probe settings (standard Kubernetes probe spec). |
 | readinessProbe.spec | object | `{"failureThreshold":3,"httpGet":{"path":"/readyz","port":"live"},"initialDelaySeconds":10,"periodSeconds":10,"successThreshold":1,"timeoutSeconds":5}` | The body of the readiness probe settings (standard Kubernetes probe spec). |

+ 2 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -180,12 +180,14 @@ rules:
     {{- end }}
     {{- end }}
   {{- end }}
   {{- end }}
   {{- end }}
   {{- end }}
+  {{- if .Values.rbac.serviceAccountTokenCreate }}
   - apiGroups:
   - apiGroups:
     - ""
     - ""
     resources:
     resources:
     - "serviceaccounts/token"
     - "serviceaccounts/token"
     verbs:
     verbs:
     - "create"
     - "create"
+  {{- end }}
   - apiGroups:
   - apiGroups:
     - ""
     - ""
     resources:
     resources:

+ 38 - 0
deploy/charts/external-secrets/tests/rbac_test.yaml

@@ -87,3 +87,41 @@ tests:
           path: metadata.name
           path: metadata.name
           value: RELEASE-NAME-external-secrets-leaderelection
           value: RELEASE-NAME-external-secrets-leaderelection
 
 
+  - it: should include serviceaccounts/token create by default
+    documentIndex: 0
+    asserts:
+      - isKind:
+          of: ClusterRole
+      - equal:
+          path: metadata.name
+          value: RELEASE-NAME-external-secrets-controller
+      - contains:
+          path: rules
+          content:
+            apiGroups:
+            - ""
+            resources:
+            - "serviceaccounts/token"
+            verbs:
+            - "create"
+
+  - it: should not include serviceaccounts/token create when serviceAccountTokenCreate is false
+    set:
+      rbac:
+        serviceAccountTokenCreate: false
+    documentIndex: 0
+    asserts:
+      - isKind:
+          of: ClusterRole
+      - equal:
+          path: metadata.name
+          value: RELEASE-NAME-external-secrets-controller
+      - notContains:
+          path: rules
+          content:
+            apiGroups:
+            - ""
+            resources:
+            - "serviceaccounts/token"
+            verbs:
+            - "create"

+ 3 - 0
deploy/charts/external-secrets/values.schema.json

@@ -702,6 +702,9 @@
                 "create": {
                 "create": {
                     "type": "boolean"
                     "type": "boolean"
                 },
                 },
+                "serviceAccountTokenCreate": {
+                    "type": "boolean"
+                },
                 "servicebindings": {
                 "servicebindings": {
                     "type": "object",
                     "type": "object",
                     "properties": {
                     "properties": {

+ 5 - 0
deploy/charts/external-secrets/values.yaml

@@ -197,6 +197,11 @@ rbac:
   # -- Specifies whether role and rolebinding resources should be created.
   # -- Specifies whether role and rolebinding resources should be created.
   create: true
   create: true
 
 
+  # -- Specifies whether the serviceaccounts/token create permission is included in the controller RBAC.
+  # When set to false, users must create per-ServiceAccount Role/RoleBinding with resourceNames constraint
+  # to grant ESO token creation for specific ServiceAccounts referenced in SecretStore specs.
+  serviceAccountTokenCreate: true
+
   servicebindings:
   servicebindings:
     # -- Specifies whether a clusterrole to give servicebindings read access should be created.
     # -- Specifies whether a clusterrole to give servicebindings read access should be created.
     create: true
     create: true

+ 40 - 1
docs/guides/security-best-practices.md

@@ -98,7 +98,7 @@ The provided Helm chart is designed for ease of use and may not meet your organi
 
 
 Here are some examples of how you can harden the Helm chart:
 Here are some examples of how you can harden the Helm chart:
 
 
-* **Scope RBAC Permissions**: The default chart grants permissions to create service account tokens for any service account. You can restrict this by modifying the `ClusterRole` to only allow token creation for specific, known service accounts. This limits the operator's ability to impersonate other service accounts.
+* **Scope RBAC Permissions**: The default chart grants permissions to create service account tokens for any service account. You can disable this by setting `rbac.serviceAccountTokenCreate: false`. When disabled, you must grant ESO token creation for specific ServiceAccounts via dedicated Role/RoleBinding with a `resourceNames` constraint. This limits the operator's ability to impersonate other service accounts.
 
 
 * **Use Tightly Scoped Deployments**: If you don't need certain features, disable them. For example, you can prevent the injection of sidecar containers by using a custom appArmor profile, or an admission controller like Kyverno to enforce restrictions on your deployment.
 * **Use Tightly Scoped Deployments**: If you don't need certain features, disable them. For example, you can prevent the injection of sidecar containers by using a custom appArmor profile, or an admission controller like Kyverno to enforce restrictions on your deployment.
 
 
@@ -125,6 +125,45 @@ To ensure a secure RBAC configuration, consider the following checklist:
 
 
 By carefully managing RBAC permissions and scoping the External Secrets Operator appropriately, you can enhance the security of your Kubernetes cluster.
 By carefully managing RBAC permissions and scoping the External Secrets Operator appropriately, you can enhance the security of your Kubernetes cluster.
 
 
+### Scoping ServiceAccount Token Creation
+
+By default, the ESO controller Role/ClusterRole includes a blanket `serviceaccounts/token: create` permission. This permission is necessary for authentication methods that rely on `serviceAccountRef` — such as Vault Kubernetes auth or Conjur JWT auth — where ESO creates a short-lived token for the referenced ServiceAccount and uses it to authenticate with the external secret provider.
+
+However, this means ESO can create tokens for *any* ServiceAccount within its scope. If the ESO service account is compromised, an attacker could leverage this permission to create tokens for other ServiceAccounts in the cluster (or in the namespace, when using scoped RBAC), effectively impersonating them and escalating privileges beyond ESO's intended scope.
+
+To mitigate this risk, you can set `rbac.serviceAccountTokenCreate: false` in the Helm chart values. This removes the blanket permission from ESO's Role/ClusterRole entirely.
+
+When this permission is disabled, you must explicitly delegate token creation to ESO on a per-ServiceAccount basis. For each ServiceAccount referenced in a SecretStore's `serviceAccountRef`, create a Role and RoleBinding that grants ESO token creation scoped to that specific ServiceAccount using the `resourceNames` constraint:
+
+```yaml
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: eso-token-<sa-name>
+  namespace: <namespace>
+rules:
+  - apiGroups: ['']
+    resources: ['serviceaccounts/token']
+    resourceNames: ['<sa-name>']
+    verbs: ['create']
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: eso-token-<sa-name>
+  namespace: <namespace>
+subjects:
+  - kind: ServiceAccount
+    name: <eso-service-account>
+    namespace: <eso-namespace>
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: eso-token-<sa-name>
+```
+
+With this configuration, ESO can only create tokens for ServiceAccounts that have been explicitly granted by the cluster administrator. This follows the principle of least privilege and ensures that a compromised ESO instance cannot be used to impersonate arbitrary ServiceAccounts.
+
 ## Network Traffic and Security
 ## Network Traffic and Security
 
 
 To ensure a secure network environment, it is recommended to restrict network traffic to and from the External Secrets Operator using `NetworkPolicies` or similar mechanisms. By default, the External Secrets Operator does not include pre-defined Network Policies.
 To ensure a secure network environment, it is recommended to restrict network traffic to and from the External Secrets Operator using `NetworkPolicies` or similar mechanisms. By default, the External Secrets Operator does not include pre-defined Network Policies.