Remove all references to the incorrect `config.watchPlugins: false` requirement that was believed necessary for Headlamp v0.39.0+. Investigation revealed that plugins work correctly with the default `watchPlugins: true` setting. The earlier documentation was based on a misunderstanding of the plugin loading mechanism. Changes: - Remove watchPlugins: false from all YAML configuration examples - Remove warning sections about watchPlugins requirement - Update troubleshooting guides to focus on actual issues - Simplify installation instructions by removing unnecessary config Files updated: - README.md (main installation docs and troubleshooting table) - docs/DEPLOYMENT.md - docs/TROUBLESHOOTING.md - docs/getting-started/* (quick-start, installation, prerequisites) - docs/deployment/* (helm, production) - docs/troubleshooting/* (common-issues, README) - Multiple other doc files formatted by prettier This cleanup ensures ArtifactHub and GitHub documentation show correct, simplified installation instructions. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
15 KiB
RBAC Permissions Guide
Understanding and configuring RBAC for the Headlamp Polaris Plugin.
Quick Reference
The plugin requires one permission to function:
| Verb | API Group | Resource | Resource Name | Namespace |
|---|---|---|---|---|
get |
"" (core) |
services/proxy |
polaris-dashboard |
polaris |
This allows the plugin to fetch audit results via the Kubernetes service proxy.
Why this permission?
- Plugin accesses Polaris through Kubernetes API server's service proxy
- Service proxy requires
getverb onservices/proxyresource - Scoped to specific service (
polaris-dashboard) for security - Read-only (no write operations)
Standard Setup (Service Account Mode)
Best for: Headlamp running with a fixed service account in the cluster (in-cluster mode)
This is the most common deployment pattern for production Headlamp instances.
Step 1: Create Role
---
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']
Key points:
- Role (not ClusterRole) - Scoped to
polarisnamespace only - resourceNames - Restricts access to
polaris-dashboardservice only - verbs: ["get"] - Read-only permission
Step 2: Create RoleBinding
---
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 # Adjust to your Headlamp SA name
namespace: kube-system # Adjust to Headlamp's namespace
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Adjust for your environment:
subjects[0].name- Your Headlamp service account name (oftenheadlamp)subjects[0].namespace- Namespace where Headlamp runs (oftenkube-system)
Step 3: Apply and Verify
# Apply RBAC manifests
kubectl apply -f polaris-rbac.yaml
# 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:kube-system:headlamp \
-n polaris \
--resource-name=polaris-dashboard
# Expected output: yes
Token-Auth Mode (Per-User Permissions)
Best for: Headlamp configured for user-supplied tokens, OIDC, or external authentication
In token-auth mode, each user's own identity is used for Kubernetes API requests (not a shared service account).
Why Per-User RBAC?
With service account mode:
- Single RoleBinding grants access to all Headlamp users
- Kubernetes sees all requests as
system:serviceaccount:kube-system:headlamp
With token-auth mode:
- Each user's own token (OIDC, kubeconfig) is used
- Kubernetes sees requests as
user@example.comorsystem:serviceaccount:team-ns:user-sa - Each user needs individual RBAC permissions
Option 1: Grant to All Authenticated Users
Use case: Everyone with cluster access should see Polaris data
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: authenticated-users-polaris-proxy
namespace: polaris
subjects:
- kind: Group
name: system:authenticated # All authenticated users
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Security consideration: This grants Polaris access to everyone with cluster access. Ensure Polaris data is not sensitive in your environment.
Option 2: Grant to Specific Users
Use case: Fine-grained control, only SRE/DevOps teams
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: sre-team-polaris-proxy
namespace: polaris
subjects:
- kind: User
name: alice@example.com
apiGroup: rbac.authorization.k8s.io
- kind: User
name: bob@example.com
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Maintenance: Add/remove users as team membership changes.
Option 3: Grant to OIDC Groups
Use case: OIDC provider with group claims (most flexible)
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-group-polaris-proxy
namespace: polaris
subjects:
- kind: Group
name: sre-team # OIDC group claim
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: devops-team
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Requirements:
- OIDC provider must include group claims in token
- Headlamp must be configured to extract groups from OIDC token
- Group names must match exactly (case-sensitive)
Example OIDC group claim:
{
"sub": "user@example.com",
"groups": ["sre-team", "developers"]
}
Verify User Permission
# Test specific user
kubectl auth can-i get services/proxy \
--as=user@example.com \
-n polaris \
--resource-name=polaris-dashboard
# Test OIDC group
kubectl auth can-i get services/proxy \
--as=user@example.com \
--as-group=sre-team \
-n polaris \
--resource-name=polaris-dashboard
# Expected output: yes (if bound correctly)
Multi-Namespace Polaris Deployments
Scenario: Polaris deployed in multiple namespaces (e.g., per-team Polaris instances)
Create Role per Namespace
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: polaris-proxy-reader
namespace: team-a-polaris # First Polaris instance
rules:
- apiGroups: ['']
resources: ['services/proxy']
resourceNames: ['polaris-dashboard']
verbs: ['get']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: polaris-proxy-reader
namespace: team-b-polaris # Second Polaris instance
rules:
- apiGroups: ['']
resources: ['services/proxy']
resourceNames: ['polaris-dashboard']
verbs: ['get']
Create RoleBindings per Namespace
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: headlamp-polaris-proxy
namespace: team-a-polaris
subjects:
- kind: ServiceAccount
name: headlamp
namespace: kube-system
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: headlamp-polaris-proxy
namespace: team-b-polaris
subjects:
- kind: ServiceAccount
name: headlamp
namespace: kube-system
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Plugin configuration: Users can switch between instances via Settings → Plugins → Polaris → Dashboard URL.
Network Security
NetworkPolicy Requirements
If the polaris namespace enforces NetworkPolicies, ensure the Kubernetes API server can reach Polaris dashboard.
Why? The Kubernetes API server proxies plugin requests, so it needs network access to Polaris.
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-apiserver-to-polaris
namespace: polaris
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: polaris
app.kubernetes.io/component: dashboard
policyTypes:
- Ingress
ingress:
# Allow from Kubernetes API server
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
component: kube-apiserver
ports:
- protocol: TCP
port: 80
Note: Headlamp pod itself does NOT need direct network access to Polaris (API server does the proxying).
Service Mesh Considerations
If using Istio, Linkerd, or other service meshes:
No special configuration needed - Service proxy requests bypass the mesh (go through API server).
OAuth2 / OIDC Integration
When using OAuth2/OIDC authentication with Headlamp:
How It Works
- User authenticates with OIDC provider (e.g., Google, Okta, Keycloak)
- OIDC provider issues token with user identity and group claims
- Headlamp receives token and passes it to Kubernetes API
- Plugin makes request using user's token (not service account)
- Kubernetes RBAC evaluates user's permissions against RoleBinding
Required Configuration
Headlamp Helm values:
env:
- name: HEADLAMP_CONFIG_OIDC_CLIENT_ID
value: 'headlamp'
- name: HEADLAMP_CONFIG_OIDC_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: headlamp-oidc
key: client-secret
- name: HEADLAMP_CONFIG_OIDC_ISSUER_URL
value: 'https://auth.example.com/realms/kubernetes'
- name: HEADLAMP_CONFIG_OIDC_SCOPES
value: 'openid,profile,email,groups'
RBAC for OIDC users:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-polaris-proxy
namespace: polaris
subjects:
- kind: Group
name: kubernetes-admins # OIDC group claim
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: polaris-proxy-reader
apiGroup: rbac.authorization.k8s.io
Testing OIDC Permissions
# Simulate OIDC user with group
kubectl auth can-i get services/proxy \
--as=user@example.com \
--as-group=kubernetes-admins \
-n polaris \
--resource-name=polaris-dashboard
# Expected: yes
Audit Logging Considerations
Every plugin data fetch creates a Kubernetes API audit log entry.
Example Audit Log
{
"kind": "Event",
"apiVersion": "audit.k8s.io/v1",
"level": "Metadata",
"verb": "get",
"user": {
"username": "system:serviceaccount:kube-system:headlamp"
},
"sourceIPs": ["10.96.0.1"],
"objectRef": {
"resource": "services",
"subresource": "proxy",
"namespace": "polaris",
"name": "polaris-dashboard",
"apiVersion": "v1"
},
"responseStatus": {
"code": 200
}
}
Volume Estimates
Per user:
- 1 refresh per 5 minutes = 288 requests/day
- 1 refresh per 30 minutes = 48 requests/day
Cluster-wide:
- 10 concurrent users × 5-minute refresh = 2,880 audit logs/day
- 100 concurrent users × 30-minute refresh = 4,800 audit logs/day
Reducing Audit Volume
Option 1: Increase refresh interval
Settings → Plugins → Polaris → Refresh Interval → 30 minutes
Option 2: Adjust audit policy level
# kube-apiserver audit policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata # Log metadata only, not full request/response
verbs: ['get']
resources:
- group: ''
resources: ['services/proxy']
namespaces: ['polaris']
Option 3: Filter audit logs If using a log aggregator (e.g., Elasticsearch), create filters to exclude or downsample Polaris proxy requests.
Troubleshooting RBAC
"403 Forbidden" Error in Plugin
Symptom: Plugin shows "Access denied (403)" error when loading data
Diagnosis:
-
Check Role exists:
kubectl -n polaris get role polaris-proxy-readerIf missing: Apply Role manifest
-
Check RoleBinding exists:
kubectl -n polaris get rolebinding headlamp-polaris-proxyIf missing: Apply RoleBinding manifest
-
Test permission:
# Service account mode kubectl auth can-i get services/proxy \ --as=system:serviceaccount:kube-system:headlamp \ -n polaris \ --resource-name=polaris-dashboard # Token-auth mode (replace with your username) kubectl auth can-i get services/proxy \ --as=user@example.com \ -n polaris \ --resource-name=polaris-dashboardExpected:
yes -
Verify RoleBinding subjects match:
kubectl -n polaris get rolebinding headlamp-polaris-proxy -o yamlCheck
subjects[].nameandsubjects[].namespacematch your Headlamp SA or user
"404 Not Found" Error
This is NOT an RBAC issue. 404 means Polaris service doesn't exist.
Check:
kubectl -n polaris get svc polaris-dashboard
If missing, install Polaris with dashboard enabled.
Permission Test Passes but Plugin Still Shows 403
Possible causes:
-
Wrong namespace in RoleBinding:
- RoleBinding must be in
polarisnamespace (where the service is) - Common mistake: Creating RoleBinding in
kube-system
- RoleBinding must be in
-
Wrong resourceName:
- Must match service name exactly:
polaris-dashboard - Check:
kubectl -n polaris get svc
- Must match service name exactly:
-
Browser caching old 403:
- Hard refresh browser: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows/Linux)
-
Token expired (OIDC mode):
- Re-authenticate with OIDC provider
- Check token expiration in browser DevTools (Application → Session Storage)
Security Best Practices
1. Use Namespaced Roles (Not ClusterRoles)
✅ Good:
kind: Role
metadata:
namespace: polaris
❌ Bad:
kind: ClusterRole
# Grants access to all namespaces
Why: Namespaced Roles limit scope to polaris namespace only. ClusterRoles would allow access to service proxies in all namespaces.
2. Always Specify resourceNames
✅ Good:
resourceNames: ['polaris-dashboard']
❌ Bad:
resourceNames: [] # Allows access to ALL services
Why: resourceNames restricts permission to a specific service. Without it, the binding grants access to proxy all services in the namespace.
3. Use Read-Only Verb
✅ Good:
verbs: ['get']
❌ Bad:
verbs: ['get', 'create', 'update', 'delete']
Why: Plugin only needs get to fetch audit results. Additional verbs violate principle of least privilege.
4. Review Bindings Quarterly
- Remove users who no longer need access
- Update OIDC group bindings when org structure changes
- Audit who has access:
kubectl -n polaris get rolebindings -o yaml
5. Monitor Audit Logs
Set alerts for:
- Unusual access patterns (e.g., 403 spikes = permission issues)
- High request volume (e.g., misconfigured refresh interval)
- Access from unexpected users (security monitoring)
6. Avoid Wildcard Permissions
❌ Never do this:
rules:
- apiGroups: ['*']
resources: ['*']
verbs: ['*']
This grants cluster-admin equivalent permissions. Always use specific resources and verbs.
Next Steps
- Features Guide - Learn about plugin capabilities
- Configuration Guide - Configure refresh intervals and dashboard URL
- Troubleshooting RBAC - Detailed RBAC debugging