Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions charts/pulsar-resources-operator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,84 @@ $ helm -n <namespace> upgrade my-release streamnative/pulsar-resources-operator

Uninstalling the release does not remove CRDs or existing custom resources.

## Hardened Configuration

The chart leaves ServiceAccount token automount and security contexts unchanged by default. Set
`serviceAccount.automountServiceAccountToken` to `true` or `false` to configure the chart-created
ServiceAccount explicitly; omit it or use `null` to leave the field unset. When `serviceAccount.create=false`,
configure automount on the externally managed ServiceAccount instead. Changing only the ServiceAccount
setting does not trigger a Deployment rollout; recreate the operator Pods for the change to take effect.

The operator needs Kubernetes API credentials for watches, status updates, and leader election.
Setting automount to `false` without supplying credentials breaks the standard in-cluster configuration.
To disable automatic mounting while retaining API access, save the following as `values-hardened.yaml`.
It explicitly projects a short-lived ServiceAccount token at client-go's default path and provides
writable temporary storage while keeping the container root filesystem read-only:

```yaml
serviceAccount:
automountServiceAccountToken: false

securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL

extraVolumes:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: tmp
emptyDir: {}

extraVolumeMounts:
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
- name: tmp
mountPath: /tmp
```

Apply the override alongside your existing release values:

```console
$ helm -n <namespace> upgrade --install my-release streamnative/pulsar-resources-operator -f values.yaml -f values-hardened.yaml
```

- Merge any existing `extraVolumes` and `extraVolumeMounts` into the override before applying it: Helm
replaces lists rather than appending to them. Preserve existing authentication and cloud storage mounts.
- The cluster must support ServiceAccount token projection and provide the `kube-root-ca.crt` ConfigMap
in the release namespace. With no explicit audience, the token uses the API server's default audience.
The kubelet rotates the token, and client-go refreshes credentials from the mounted file. Do not use a
`subPath` mount for the token because it would prevent projected updates from reaching the container.
- Keep `/tmp` writable by the container's runtime UID: inline OAuth2 credentials and PulsarPackage
downloads create temporary files there. Verify these operations, not just operator startup, when
enabling a read-only root filesystem. Size temporary storage according to package sizes and concurrency.
- This example does not pin a UID or GID, so the image or cluster can supply the appropriate non-root
identity. Validate the actual image and any additional security requirements in your environment.
- Explicit token projection does not remove API credentials or reduce the ServiceAccount's RBAC
permissions. Check the rendered ServiceAccount and Deployment, the admitted Pod, and your policy reports.
A policy forbidding all API tokens still requires an approved exception or another authentication method.
- Before rollout, verify leader election, resource reconciliation, token rotation, and the OAuth2 and
package operations you use in a test environment. Rendering this example is not a runtime or policy check.

## Values

| Key | Type | Default | Description |
Expand Down Expand Up @@ -89,6 +167,7 @@ Uninstalling the release does not remove CRDs or existing custom resources.
| resources | object | `{}` | Add resource limits and requests |
| securityContext | object | `{}` | Add security context for container |
| serviceAccount.annotations | object | `{}` | Annotations to add to the service account |
| serviceAccount.automountServiceAccountToken | bool/null | `nil` | Optional token automount setting for the chart-created ServiceAccount. Null preserves Kubernetes' default behavior. When false, explicitly mount Kubernetes API credentials; see the hardened configuration example in the README. |
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created |
| serviceAccount.name | string | `""` | Name of the service account to use. When empty and create=true, the chart generates a name. |
| terminationGracePeriodSeconds | int | `10` | Graceful termination period in seconds. |
Expand Down
78 changes: 78 additions & 0 deletions charts/pulsar-resources-operator/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,82 @@ $ helm -n <namespace> upgrade my-release streamnative/pulsar-resources-operator

Uninstalling the release does not remove CRDs or existing custom resources.

## Hardened Configuration

The chart leaves ServiceAccount token automount and security contexts unchanged by default. Set
`serviceAccount.automountServiceAccountToken` to `true` or `false` to configure the chart-created
ServiceAccount explicitly; omit it or use `null` to leave the field unset. When `serviceAccount.create=false`,
configure automount on the externally managed ServiceAccount instead. Changing only the ServiceAccount
setting does not trigger a Deployment rollout; recreate the operator Pods for the change to take effect.

The operator needs Kubernetes API credentials for watches, status updates, and leader election.
Setting automount to `false` without supplying credentials breaks the standard in-cluster configuration.
To disable automatic mounting while retaining API access, save the following as `values-hardened.yaml`.
It explicitly projects a short-lived ServiceAccount token at client-go's default path and provides
writable temporary storage while keeping the container root filesystem read-only:

```yaml
serviceAccount:
automountServiceAccountToken: false

securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL

extraVolumes:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: tmp
emptyDir: {}

extraVolumeMounts:
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
- name: tmp
mountPath: /tmp
```

Apply the override alongside your existing release values:

```console
$ helm -n <namespace> upgrade --install my-release streamnative/pulsar-resources-operator -f values.yaml -f values-hardened.yaml
```

- Merge any existing `extraVolumes` and `extraVolumeMounts` into the override before applying it: Helm
replaces lists rather than appending to them. Preserve existing authentication and cloud storage mounts.
- The cluster must support ServiceAccount token projection and provide the `kube-root-ca.crt` ConfigMap
in the release namespace. With no explicit audience, the token uses the API server's default audience.
The kubelet rotates the token, and client-go refreshes credentials from the mounted file. Do not use a
`subPath` mount for the token because it would prevent projected updates from reaching the container.
- Keep `/tmp` writable by the container's runtime UID: inline OAuth2 credentials and PulsarPackage
downloads create temporary files there. Verify these operations, not just operator startup, when
enabling a read-only root filesystem. Size temporary storage according to package sizes and concurrency.
- This example does not pin a UID or GID, so the image or cluster can supply the appropriate non-root
identity. Validate the actual image and any additional security requirements in your environment.
- Explicit token projection does not remove API credentials or reduce the ServiceAccount's RBAC
permissions. Check the rendered ServiceAccount and Deployment, the admitted Pod, and your policy reports.
A policy forbidding all API tokens still requires an approved exception or another authentication method.
- Before rollout, verify leader election, resource reconciliation, token rotation, and the OAuth2 and
package operations you use in a test environment. Rendering this example is not a runtime or policy check.

{{ template "chart.valuesSection" . }}
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ metadata:
{{- with .Values.serviceAccount.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- if kindIs "bool" .Values.serviceAccount.automountServiceAccountToken }}
automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }}
{{- end }}
{{- end }}
93 changes: 92 additions & 1 deletion charts/pulsar-resources-operator/tests/deployment_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,17 @@ tests:
- isNull:
path: spec.template.spec.containers[0].resources.limits
- isNull:
path: spec.template.spec.containers[1].volumeMounts
path: spec.template.spec.containers[0].volumeMounts
- isNull:
path: spec.template.spec.volumes
- notExists:
path: spec.template.spec.automountServiceAccountToken
- equal:
path: spec.template.spec.securityContext
value: {}
- equal:
path: spec.template.spec.containers[0].securityContext
value: {}

- it: should update the image version successfully
set:
Expand Down Expand Up @@ -119,3 +127,86 @@ tests:
- name: connection-credentials
mountPath: /var/secrets/connection
readOnly: true

- it: should support explicit API credentials and writable tmp with a read-only root filesystem
set:
serviceAccount.automountServiceAccountToken: false
serviceAccount.name: operator
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
extraVolumes:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: tmp
emptyDir: {}
extraVolumeMounts:
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
- name: tmp
mountPath: /tmp
asserts:
- equal:
path: spec.template.spec.serviceAccountName
value: operator
- notExists:
path: spec.template.spec.automountServiceAccountToken
- equal:
path: spec.template.spec.containers[0].securityContext
value:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
- equal:
path: spec.template.spec.volumes
value:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
- configMap:
name: kube-root-ca.crt
items:
- key: ca.crt
path: ca.crt
- downwardAPI:
items:
- path: namespace
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: tmp
emptyDir: {}
- equal:
path: spec.template.spec.containers[0].volumeMounts
value:
- name: kube-api-access
mountPath: /var/run/secrets/kubernetes.io/serviceaccount
readOnly: true
- name: tmp
mountPath: /tmp
44 changes: 44 additions & 0 deletions charts/pulsar-resources-operator/tests/serviceaccount_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,50 @@ tests:
- matchRegex:
path: metadata.name
pattern: -pulsar-resources-operator$
- notExists:
path: automountServiceAccountToken

- it: should omit token automount when explicitly null
set:
serviceAccount.automountServiceAccountToken: null
asserts:
- notExists:
path: automountServiceAccountToken

- it: should enable token automount when explicitly true
set:
serviceAccount.automountServiceAccountToken: true
asserts:
- equal:
path: automountServiceAccountToken
value: true

- it: should disable token automount without changing service account metadata
set:
serviceAccount.automountServiceAccountToken: false
serviceAccount.name: operator
serviceAccount.annotations:
example.com/identity: operator
asserts:
- equal:
path: automountServiceAccountToken
value: false
- equal:
path: metadata.name
value: operator
- equal:
path: metadata.annotations
value:
example.com/identity: operator

- it: should not create an externally managed service account
set:
serviceAccount.create: false
serviceAccount.name: external-operator
serviceAccount.automountServiceAccountToken: false
asserts:
- hasDocuments:
count: 0

- it: should be the specified name
set:
Expand Down
3 changes: 3 additions & 0 deletions charts/pulsar-resources-operator/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,9 @@
"annotations": {
"type": "object"
},
"automountServiceAccountToken": {
"type": ["boolean", "null"]
},
"create": {
"type": "boolean"
},
Expand Down
2 changes: 2 additions & 0 deletions charts/pulsar-resources-operator/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ nameOverride: ""
fullnameOverride: ""

serviceAccount:
# -- (bool/null) Optional token automount setting for the chart-created ServiceAccount. Null preserves Kubernetes' default behavior. When false, explicitly mount Kubernetes API credentials; see the hardened configuration example in the README.
automountServiceAccountToken: null
# -- Specifies whether a service account should be created
create: true
# -- Annotations to add to the service account
Expand Down
Loading