Files
privilegedescalation-ceo[bot] e2ae92648c docs: replace hardcoded namespace with <your-namespace> placeholder
* docs: update Headlamp install namespace references from kube-system to headlamp

Updates all documentation references to the Headlamp install namespace
from kube-system to headlamp as part of PRI-433.

In-scope files updated:
- README.md, SECURITY.md
- docs/getting-started/installation.md, quick-start.md, prerequisites.md
- docs/deployment/helm.md, kubernetes.md, production.md
- docs/troubleshooting/README.md, common-issues.md, rbac-issues.md
- docs/user-guide/configuration.md, rbac-permissions.md
- docs/TESTING.md, TROUBLESHOOTING.md, DEPLOYMENT.md

Out-of-scope (unchanged):
- Source files referencing upstream workload namespace
- RBAC manifests describing Polaris namespace (polaris ns is unchanged)
- NetworkPolicy namespaceSelector (API server runs in kube-system)
- design-decisions.md and ARCHITECTURE.md (URL hashes refer to cluster namespaces, not Headlamp install ns)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix: correct RBAC manifest per QA review (PRI-555)

- Remove rbac.authorization.k8s.io privilege escalation block
- Fix orphaned comment from round 1
- Add EOF newline
- Keep serviceaccounts/token for E2E auth (confirmed needed)
- Namespace already correct (privilegedescalation-dev)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* docs: replace hardcoded namespace with <your-namespace> placeholder

Users choose their own namespace for Headlamp. Replace all hardcoded
namespace references (headlamp, kube-system) in user-facing docs with
<your-namespace> so users substitute their own value.

Conventions:
- Helm install: --namespace <your-namespace> --create-namespace
- kubectl commands: -n <your-namespace>
- YAML metadata: namespace: <your-namespace>
- Prose: "the namespace where Headlamp is installed"

Out-of-scope references left untouched:
- kube-system in NetworkPolicy selectors (API server namespace)
- polaris namespace references (upstream workload namespace)
- Source code and test files

Refs: PRI-433

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* docs: fix remaining hardcoded headlamp namespace to <your-namespace> placeholder

Prior commit was inconsistent — some files used <your-namespace> while
DEPLOYMENT.md, TROUBLESHOOTING.md and several troubleshooting/user-guide
docs still hardcoded headlamp as the namespace.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Chris Farhood <chris@farhood.org>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-05-10 21:34:49 +00:00

11 KiB

Kubernetes Deployment

Direct Kubernetes manifest deployment for the Headlamp Polaris Plugin.

Overview

This guide covers deploying the plugin using raw Kubernetes manifests without Helm. This approach is useful for:

  • Environments where Helm is not available
  • Highly customized deployments
  • GitOps workflows with Kustomize
  • Learning the underlying Kubernetes resources

RBAC Manifests

The plugin requires read-only access to the Polaris dashboard service proxy.

Complete RBAC Configuration

---
# Role: Read-only access to Polaris service proxy
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: polaris-proxy-reader
  namespace: polaris
  labels:
    app.kubernetes.io/name: headlamp-polaris-plugin
    app.kubernetes.io/component: rbac
rules:
  - apiGroups: ['']
    resources: ['services/proxy']
    resourceNames: ['polaris-dashboard']
    verbs: ['get']

---
# RoleBinding: Grant Headlamp service account access
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: headlamp-polaris-proxy
  namespace: polaris
  labels:
    app.kubernetes.io/name: headlamp-polaris-plugin
    app.kubernetes.io/component: rbac
subjects:
  - kind: ServiceAccount
    name: headlamp
    namespace: <your-namespace>
roleRef:
  kind: Role
  name: polaris-proxy-reader
  apiGroup: rbac.authorization.k8s.io

Apply the RBAC manifests:

kubectl apply -f polaris-plugin-rbac.yaml

RBAC Verification

# Verify Role exists
kubectl -n polaris get role polaris-proxy-reader

# Verify RoleBinding exists
kubectl -n polaris get rolebinding headlamp-polaris-proxy

# Test permission
kubectl auth can-i get services/proxy \
  --as=system:serviceaccount:<your-namespace>:headlamp \
  -n polaris \
  --resource-name=polaris-dashboard

# Expected output: yes

Plugin Installation via Init Container

Use an init container to download and install the plugin.

ConfigMap for Plugin Configuration

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: headlamp-plugin-config
  namespace: <your-namespace>
  labels:
    app.kubernetes.io/name: headlamp
    app.kubernetes.io/component: plugin-config
data:
  plugin.yml: |
    - name: headlamp-polaris-plugin
      version: 0.3.5
      url: https://github.com/privilegedescalation/headlamp-polaris-plugin/releases/download/v0.3.10/polaris-0.3.10.tar.gz

Headlamp Deployment with Plugin Init Container

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: headlamp
  namespace: <your-namespace>
  labels:
    app.kubernetes.io/name: headlamp
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: headlamp
  template:
    metadata:
      labels:
        app.kubernetes.io/name: headlamp
    spec:
      serviceAccountName: headlamp

      # Init container to install plugins
      initContainers:
        - name: install-plugins
          image: node:lts-alpine
          command:
            - sh
            - -c
            - |
              npm install -g @kinvolk/headlamp-plugin
              headlamp-plugin install --config /config/plugin.yml --plugins-dir /plugins
              echo "Plugin installation complete"
          volumeMounts:
            - name: plugins
              mountPath: /plugins
            - name: plugin-config
              mountPath: /config

      containers:
        - name: headlamp
          image: ghcr.io/headlamp-k8s/headlamp:v0.39.0
          args:
            - '-in-cluster'
            - '-plugins-dir=/headlamp/plugins'
          env:
            - name: HEADLAMP_CONFIG_WATCH_PLUGINS
              value: 'false' # CRITICAL: Must be false for plugin manager
          ports:
            - name: http
              containerPort: 4466
              protocol: TCP
          volumeMounts:
            - name: plugins
              mountPath: /headlamp/plugins
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 5
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
            requests:
              cpu: 100m
              memory: 128Mi

      volumes:
        - name: plugins
          emptyDir: {}
        - name: plugin-config
          configMap:
            name: headlamp-plugin-config

Supporting Resources

---
# ServiceAccount for Headlamp
apiVersion: v1
kind: ServiceAccount
metadata:
  name: headlamp
  namespace: <your-namespace>
  labels:
    app.kubernetes.io/name: headlamp

---
# Service for Headlamp
apiVersion: v1
kind: Service
metadata:
  name: headlamp
  namespace: <your-namespace>
  labels:
    app.kubernetes.io/name: headlamp
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
  selector:
    app.kubernetes.io/name: headlamp

Complete Deployment Workflow

1. Apply All Manifests

# Create RBAC for Polaris plugin
kubectl apply -f polaris-plugin-rbac.yaml

# Create plugin configuration
kubectl apply -f headlamp-plugin-config.yaml

# Deploy Headlamp with plugin init container
kubectl apply -f headlamp-deployment.yaml
kubectl apply -f headlamp-service.yaml
kubectl apply -f headlamp-serviceaccount.yaml

# Wait for deployment to be ready
kubectl -n <your-namespace> wait --for=condition=available deployment/headlamp --timeout=300s

2. Verify Deployment

# Check pods are running
kubectl -n <your-namespace> get pods -l app.kubernetes.io/name=headlamp

# Expected output:
# NAME                        READY   STATUS    RESTARTS   AGE
# headlamp-xxxxxxxxxx-xxxxx   1/1     Running   0          2m

# Check init container logs
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins

# Expected output:
# Plugin installation complete

# Verify plugin files exist
kubectl -n <your-namespace> exec deployment/headlamp -c headlamp -- \
  ls -la /headlamp/plugins/headlamp-polaris-plugin/

# Expected output:
# drwxr-xr-x  dist/
# -rw-r--r--  package.json

# Test Polaris API access
kubectl get --raw /api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/results.json \
  | jq .PolarisOutputVersion

# Expected output: "1.0" or similar

3. Access Headlamp

# Port-forward to access locally
kubectl -n <your-namespace> port-forward service/headlamp 8080:80

# Open browser to http://localhost:8080

Kustomize Integration

For GitOps workflows with Kustomize:

Directory Structure

k8s/
├── base/
│   ├── kustomization.yaml
│   ├── rbac.yaml
│   ├── configmap.yaml
│   ├── deployment.yaml
│   ├── service.yaml
│   └── serviceaccount.yaml
└── overlays/
    ├── production/
    │   ├── kustomization.yaml
    │   └── patches.yaml
    └── staging/
        ├── kustomization.yaml
        └── patches.yaml

Base Kustomization

# k8s/base/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: <your-namespace>

commonLabels:
  app.kubernetes.io/name: headlamp
  app.kubernetes.io/managed-by: kustomize

resources:
  - serviceaccount.yaml
  - service.yaml
  - deployment.yaml
  - configmap.yaml
  - rbac.yaml

configMapGenerator:
  - name: headlamp-plugin-config
    files:
      - plugin.yml

Production Overlay

# k8s/overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

bases:
  - ../../base

nameSuffix: -prod

replicas:
  - name: headlamp
    count: 2

patches:
  - path: patches.yaml
# k8s/overlays/production/patches.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: headlamp
spec:
  template:
    spec:
      containers:
        - name: headlamp
          resources:
            limits:
              cpu: 1000m
              memory: 1Gi
            requests:
              cpu: 200m
              memory: 256Mi

Deploy with Kustomize

# Build and preview
kubectl kustomize k8s/overlays/production

# Apply
kubectl apply -k k8s/overlays/production

FluxCD Integration

For GitOps with FluxCD:

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: headlamp-polaris-plugin
  namespace: flux-system
spec:
  interval: 10m
  path: ./k8s/overlays/production
  prune: true
  sourceRef:
    kind: GitRepository
    name: infrastructure
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: headlamp
      namespace: <your-namespace>

Upgrading the Plugin

Update ConfigMap

# Edit ConfigMap with new version
kubectl -n <your-namespace> edit configmap headlamp-plugin-config

# Update version and URL:
# version: 0.3.6
# url: https://github.com/.../v0.3.6/polaris-0.3.10.tar.gz

# Restart deployment to trigger init container
kubectl -n <your-namespace> rollout restart deployment/headlamp

# Wait for rollout to complete
kubectl -n <your-namespace> rollout status deployment/headlamp

Verify Upgrade

# Check init container logs
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins

# Verify new version in UI
# Navigate to Settings → Plugins in Headlamp

Troubleshooting

Init Container Fails

# Check init container logs
kubectl -n <your-namespace> logs deployment/headlamp -c install-plugins

# Common issues:
# 1. Network connectivity to GitHub
# 2. Invalid URL in ConfigMap
# 3. Tarball checksum mismatch

Plugin Not Loading

# Verify HEADLAMP_CONFIG_WATCH_PLUGINS is false
kubectl -n <your-namespace> get deployment headlamp -o yaml | grep WATCH_PLUGINS

# Expected output:
# - name: HEADLAMP_CONFIG_WATCH_PLUGINS
#   value: "false"

# If not set or "true", update deployment
kubectl -n <your-namespace> edit deployment headlamp

RBAC Permissions Denied

# Test RBAC
kubectl auth can-i get services/proxy \
  --as=system:serviceaccount:<your-namespace>:headlamp \
  -n polaris \
  --resource-name=polaris-dashboard

# If "no", verify RBAC manifests applied:
kubectl -n polaris get role polaris-proxy-reader
kubectl -n polaris get rolebinding headlamp-polaris-proxy

Next Steps

References