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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,10 @@ helm-template-test:
helm-update-template-version:
charts/headlamp/tests/update-version.sh

.PHONY: helm-test-pre-upgrade-hook
helm-test-pre-upgrade-hook:
charts/headlamp/tests/test-pre-upgrade-hook.sh

# TODO: add windows compatibility
.PHONY: run-jaeger
run-jaeger:
Expand Down
5 changes: 5 additions & 0 deletions charts/headlamp/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ annotations:
url: https://keys.openpgp.org/vks/v1/by-fingerprint/2956B7F7167769370C93730C7264DA7B85D08A37
artifacthub.io/category: monitoring-logging
artifacthub.io/license: Apache-2.0
artifacthub.io/changes: |
- kind: changed
description: The default ClusterRoleBinding is no longer created by default (ClusterRoleBinding creation is now disabled). Users who want a ClusterRoleBinding must now explicitly enable it and provide a clusterRoleName value, as the default clusterRoleName is now empty.
- kind: added
description: Pre-upgrade hook automatically removes old ClusterRoleBinding during upgrades
artifacthub.io/screenshots: |
- title: Cluster Overview
url: https://raw.githubusercontent.com/kubernetes-sigs/headlamp/screenshots/screenshots/cluster_overview.png
Expand Down
14 changes: 12 additions & 2 deletions charts/headlamp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ $ helm install my-headlamp headlamp/headlamp \
--set ingress.hosts[0].paths[0].path=/
```

## Upgrading

### Security Improvements

Starting from version 0.39.0, the chart implements enhanced security by default:

- **Removed Permissions**: The default ClusterRoleBinding creation has been disabled (`create: false`), removing the previous `cluster-admin` binding.

**Automatic Migration**: A pre-upgrade hook automatically removes the old `headlamp-admin` ClusterRoleBinding during upgrades to help migrate to the new security configuration. The hook only removes ClusterRoleBindings that were created by Helm, preserving any user-created resources with the same name.

## Configuration

### Core Parameters
Expand Down Expand Up @@ -148,8 +158,8 @@ config:
| serviceAccount.create | bool | `true` | Create service account |
| serviceAccount.name | string | `""` | Service account name |
| serviceAccount.annotations | object | `{}` | Service account annotations |
| clusterRoleBinding.create | bool | `true` | Create cluster role binding |
| clusterRoleBinding.clusterRoleName | string | `"cluster-admin"` | Kubernetes ClusterRole name |
| clusterRoleBinding.create | bool | `false` | Create cluster role binding |
| clusterRoleBinding.clusterRoleName | string | `""` | Kubernetes ClusterRole name |
| clusterRoleBinding.annotations | object | `{}` | Cluster role binding annotations |
| hostUsers | bool | `true` | Run in host uid namespace |
| podSecurityContext | object | `{}` | Pod security context (e.g., fsGroup: 2000) |
Expand Down
12 changes: 7 additions & 5 deletions charts/headlamp/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ include "headlamp.namespace" . }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
{{- if .Values.clusterRoleBinding.create }}
{{- if and ( ge .Capabilities.KubeVersion.Major "1" ) ( ge .Capabilities.KubeVersion.Minor "24" ) }}
2. Get the token using
kubectl create token {{ include "headlamp.serviceAccountName" . }} --namespace {{ include "headlamp.namespace" . }}
2. Create a service account using
kubectl create serviceaccount {{ include "headlamp.serviceAccountName" . }}-admin --namespace {{ include "headlamp.namespace" . }}
3. Create a clusterrolebinding using
kubectl create clusterrolebinding {{ include "headlamp.serviceAccountName" . }}-admin --clusterrole=cluster-admin --serviceaccount={{ include "headlamp.namespace" . }}:{{ include "headlamp.serviceAccountName" . }}-admin
4. Get the token using
kubectl create token {{ include "headlamp.serviceAccountName" . }}-admin --namespace {{ include "headlamp.namespace" . }}
{{- else }}
2. Get the clusterrolebinding token using
5. Get the clusterrolebinding token using
export SECRET=$(kubectl get secrets --namespace {{ include "headlamp.namespace" . }} -o custom-columns=":metadata.name" | grep "{{ include "headlamp.fullname" . }}-token")
kubectl get secret $SECRET --namespace {{ include "headlamp.namespace" . }} --template=\{\{.data.token\}\} | base64 --decode
{{- end }}
{{- end }}
4 changes: 2 additions & 2 deletions charts/headlamp/templates/clusterrolebinding.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "headlamp.fullname" . }}-admin
name: {{ include "headlamp.fullname" . }}
labels:
{{- include "headlamp.labels" . | nindent 4 }}
{{- with .Values.clusterRoleBinding.annotations }}
Expand All @@ -12,7 +12,7 @@ metadata:
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ .Values.clusterRoleBinding.clusterRoleName }}
name: {{ required "clusterRoleBinding.clusterRoleName is required when clusterRoleBinding.create is true" .Values.clusterRoleBinding.clusterRoleName }}
subjects:
- kind: ServiceAccount
name: {{ include "headlamp.serviceAccountName" . }}
Expand Down
127 changes: 127 additions & 0 deletions charts/headlamp/templates/pre-upgrade-cleanup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "headlamp.fullname" . }}-pre-upgrade
namespace: {{ include "headlamp.namespace" . }}
labels:
{{- include "headlamp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: {{ include "headlamp.fullname" . }}-pre-upgrade
labels:
{{- include "headlamp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterrolebindings"]
verbs: ["get", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: {{ include "headlamp.fullname" . }}-pre-upgrade
labels:
{{- include "headlamp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-4"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: {{ include "headlamp.fullname" . }}-pre-upgrade
subjects:
- kind: ServiceAccount
name: {{ include "headlamp.fullname" . }}-pre-upgrade
namespace: {{ include "headlamp.namespace" . }}
---
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "headlamp.fullname" . }}-pre-upgrade
namespace: {{ include "headlamp.namespace" . }}
labels:
{{- include "headlamp.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": pre-upgrade
"helm.sh/hook-weight": "-3"
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
spec:
ttlSecondsAfterFinished: 300
template:
metadata:
name: {{ include "headlamp.fullname" . }}-pre-upgrade
labels:
{{- include "headlamp.labels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "headlamp.fullname" . }}-pre-upgrade
restartPolicy: Never
containers:
- name: pre-upgrade-cleanup
image: alpine/kubectl:1.35.0@sha256:e7e078c7bb25012141e5957d500834b2a5b266d6de20ecfa862b30d8a892fc7e
command:
- /bin/sh
- -c
- |
set -e
CRB_NAME="{{ include "headlamp.fullname" . }}-admin"
RELEASE_NAME="{{ .Release.Name }}"

echo "Checking for old ClusterRoleBinding ${CRB_NAME}..."

if ! kubectl get clusterrolebinding "${CRB_NAME}" 2>/dev/null; then
echo "ClusterRoleBinding ${CRB_NAME} not found, nothing to clean up"
exit 0
fi

echo "Found ClusterRoleBinding ${CRB_NAME}, verifying it was created by Helm..."

# Check if the ClusterRoleBinding has Helm labels indicating it was created by this chart
MANAGED_BY=$(kubectl get clusterrolebinding "${CRB_NAME}" -o jsonpath='{.metadata.labels.app\.kubernetes\.io/managed-by}' 2>/dev/null || echo "")
INSTANCE=$(kubectl get clusterrolebinding "${CRB_NAME}" -o jsonpath='{.metadata.labels.app\.kubernetes\.io/instance}' 2>/dev/null || echo "")
APP_NAME=$(kubectl get clusterrolebinding "${CRB_NAME}" -o jsonpath='{.metadata.labels.app\.kubernetes\.io/name}' 2>/dev/null || echo "")

if [ "${MANAGED_BY}" = "Helm" ] && [ "${INSTANCE}" = "${RELEASE_NAME}" ] && [ "${APP_NAME}" = "headlamp" ]; then
echo "Confirmed: ${CRB_NAME} was created by this Helm release (${RELEASE_NAME})"
echo "Deleting old ClusterRoleBinding..."
kubectl delete clusterrolebinding "${CRB_NAME}"
echo "Successfully deleted old ClusterRoleBinding"
else
echo "WARNING: ${CRB_NAME} exists but was NOT created by this Helm release"
echo " managed-by: ${MANAGED_BY} (expected: Helm)"
echo " instance: ${INSTANCE} (expected: ${RELEASE_NAME})"
echo " app: ${APP_NAME} (expected: headlamp)"
echo "Skipping deletion to preserve user-created resource"
fi
resources:
requests:
cpu: 10m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi

securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
volumeMounts:
- name: tmp
mountPath: /tmp
volumes:
- name: tmp
emptyDir: {}
Loading
Loading