docs: remove incorrect watchPlugins: false references

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>
This commit is contained in:
2026-02-13 09:54:15 -05:00
parent 0faa50cd9d
commit 24033ca977
25 changed files with 775 additions and 468 deletions
+46 -14
View File
@@ -28,12 +28,14 @@ Access plugin settings via **Settings → Plugins → Polaris** in the Headlamp
### Impact
**Affects:**
- Dashboard overview page
- Namespace list and detail views
- Inline audit sections on resource pages
- App bar score badge
**API Load:**
- Each refresh triggers one HTTP GET to Polaris dashboard
- Each request is logged in Kubernetes audit logs
- Longer intervals reduce API server and audit log pressure
@@ -41,16 +43,19 @@ Access plugin settings via **Settings → Plugins → Polaris** in the Headlamp
### Performance Considerations
**For small clusters (<100 pods):**
- Recommended: 5 minutes (default)
- Acceptable: 1 minute (if real-time data is critical)
**For large clusters (>1000 pods):**
- Recommended: 10-30 minutes
- Reason: Reduces audit log volume and API server load
- Example: 10 users × 1-minute refresh = ~14,400 audit logs/day
- Example: 10 users × 30-minute refresh = ~480 audit logs/day
**For production environments:**
- Start with 5 minutes
- Monitor API server metrics and audit log volume
- Increase interval if needed
@@ -62,6 +67,7 @@ Access plugin settings via **Settings → Plugins → Polaris** in the Headlamp
### Default Configuration
**Service proxy path (default):**
```
/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/
```
@@ -69,6 +75,7 @@ Access plugin settings via **Settings → Plugins → Polaris** in the Headlamp
This uses the Kubernetes API server to proxy requests to the Polaris dashboard service in the `polaris` namespace.
**Advantages:**
- Uses existing Headlamp authentication (service account or user token)
- Works with Headlamp's OIDC and token-auth modes
- No additional RBAC or network configuration needed
@@ -85,6 +92,7 @@ https://polaris.example.com/
```
**Requirements:**
- Polaris dashboard must be accessible from browser
- CORS must be configured on Polaris to allow Headlamp origin
- HTTPS recommended for production
@@ -98,6 +106,7 @@ If Polaris is deployed in a different namespace:
```
**Requirements:**
- Update RBAC Role namespace to match
- Service name must still be `polaris-dashboard` (or adjust in URL)
@@ -131,39 +140,43 @@ http://localhost:8080/
**What it does:** Verifies the plugin can reach the Polaris dashboard and fetch audit data.
**To test:**
1. Enter Dashboard URL in settings
2. Click **Test Connection**
3. Wait for response (2-5 seconds)
**Success Response:**
```
✓ Connected to Polaris v4.2.0
```
**Error Responses:**
| Error | Meaning | Solution |
|-------|---------|----------|
| **403 Forbidden** | RBAC permission denied | Check RBAC bindings (see [RBAC Guide](rbac-permissions.md)) |
| **404 Not Found** | Polaris service not found | Verify Polaris is running: `kubectl get svc -n polaris` |
| **503 Service Unavailable** | Polaris pod not ready | Check pod status: `kubectl get pods -n polaris` |
| **Network Error** | Cannot reach URL | Check URL format, CORS (for external), NetworkPolicies |
| **CORS Error** | Cross-origin blocked | Configure Polaris dashboard CORS headers |
| Error | Meaning | Solution |
| --------------------------- | ------------------------- | ----------------------------------------------------------- |
| **403 Forbidden** | RBAC permission denied | Check RBAC bindings (see [RBAC Guide](rbac-permissions.md)) |
| **404 Not Found** | Polaris service not found | Verify Polaris is running: `kubectl get svc -n polaris` |
| **503 Service Unavailable** | Polaris pod not ready | Check pod status: `kubectl get pods -n polaris` |
| **Network Error** | Cannot reach URL | Check URL format, CORS (for external), NetworkPolicies |
| **CORS Error** | Cross-origin blocked | Configure Polaris dashboard CORS headers |
### CORS Configuration (External Polaris)
If using an external Polaris URL, configure CORS to allow Headlamp origin.
**Polaris Helm values:**
```yaml
dashboard:
enabled: true
env:
- name: CORS_ALLOWED_ORIGINS
value: "https://headlamp.example.com"
value: 'https://headlamp.example.com'
```
**Test CORS:**
```bash
curl -v -H "Origin: https://headlamp.example.com" \
https://polaris.example.com/results.json \
@@ -180,25 +193,29 @@ curl -v -H "Origin: https://headlamp.example.com" \
Plugin settings are stored in browser **localStorage**:
**Keys:**
- `polaris-plugin-refresh-interval` - Refresh interval in seconds (number)
- `polaris-plugin-dashboard-url` - Dashboard URL (string)
**View settings:**
```javascript
// Open browser DevTools Console (F12)
console.log('Refresh Interval:', localStorage.getItem('polaris-plugin-refresh-interval'))
console.log('Dashboard URL:', localStorage.getItem('polaris-plugin-dashboard-url'))
console.log('Refresh Interval:', localStorage.getItem('polaris-plugin-refresh-interval'));
console.log('Dashboard URL:', localStorage.getItem('polaris-plugin-dashboard-url'));
```
**Reset to defaults:**
```javascript
// Open browser DevTools Console (F12)
localStorage.removeItem('polaris-plugin-refresh-interval')
localStorage.removeItem('polaris-plugin-dashboard-url')
localStorage.removeItem('polaris-plugin-refresh-interval');
localStorage.removeItem('polaris-plugin-dashboard-url');
// Hard refresh browser: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows/Linux)
```
**Notes:**
- Settings are per-browser, per-user
- Private/incognito mode may clear settings on browser close
- Settings are NOT synced across devices
@@ -208,6 +225,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
### For Development Clusters
**Recommended Settings:**
- **Refresh Interval:** 1-5 minutes (faster feedback loop)
- **Dashboard URL:** Service proxy (default)
@@ -216,6 +234,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
### For Staging Clusters
**Recommended Settings:**
- **Refresh Interval:** 5-10 minutes (balanced)
- **Dashboard URL:** Service proxy (default)
@@ -224,6 +243,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
### For Production Clusters
**Recommended Settings:**
- **Refresh Interval:** 10-30 minutes (reduce load)
- **Dashboard URL:** Service proxy (default)
@@ -232,6 +252,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
### For Multi-Tenant Environments
**Recommended Settings:**
- **Refresh Interval:** 10-30 minutes (minimize per-user load)
- **Dashboard URL:** Service proxy with per-namespace RBAC
@@ -240,6 +261,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
### For External Polaris
**Recommended Settings:**
- **Refresh Interval:** 5-10 minutes (depends on network latency)
- **Dashboard URL:** `https://polaris.example.com/`
- **CORS:** Must be configured on Polaris side
@@ -253,17 +275,19 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
**Symptom:** Changes to settings revert after clicking Save
**Possible Causes:**
1. Browser blocks localStorage (privacy mode)
2. Browser extension interfering
3. JavaScript error in console
**Solution:**
1. Open browser DevTools Console (F12)
2. Check for JavaScript errors
3. Disable privacy mode or try different browser
4. Check if localStorage is enabled:
```javascript
console.log('localStorage available:', typeof localStorage !== 'undefined')
console.log('localStorage available:', typeof localStorage !== 'undefined');
```
### Settings Lost After Browser Restart
@@ -273,6 +297,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
**Cause:** Browser privacy settings clear localStorage on exit
**Solution:**
- Use normal browsing mode (not private/incognito)
- Check browser settings for "Clear data on exit"
- Consider requesting ConfigMap-based settings (future feature)
@@ -284,6 +309,7 @@ localStorage.removeItem('polaris-plugin-dashboard-url')
**Solutions by error type:**
**403 Forbidden:**
```bash
# Verify RBAC exists
kubectl -n polaris get role polaris-proxy-reader
@@ -297,6 +323,7 @@ kubectl auth can-i get services/proxy \
```
**404 Not Found:**
```bash
# Verify Polaris is running
kubectl -n polaris get pods
@@ -310,6 +337,7 @@ helm install polaris fairwinds-stable/polaris \
```
**503 Service Unavailable:**
```bash
# Check pod status
kubectl -n polaris get pods
@@ -319,6 +347,7 @@ kubectl -n polaris logs deployment/polaris-dashboard
```
**Network Error / CORS:**
```bash
# For external Polaris, test CORS
curl -v -H "Origin: https://headlamp.example.com" \
@@ -332,15 +361,17 @@ curl -v -H "Origin: https://headlamp.example.com" \
**Symptom:** Data doesn't refresh automatically
**Check:**
1. Verify setting is saved (localStorage key exists)
2. Check browser console for errors
3. Verify Polaris is returning data (manual refresh works)
4. Ensure you're on a Polaris plugin page (not other Headlamp pages)
**Debug:**
```javascript
// Check refresh interval
console.log(localStorage.getItem('polaris-plugin-refresh-interval'))
console.log(localStorage.getItem('polaris-plugin-refresh-interval'));
// Should return: "300" (5 minutes), "600" (10 minutes), etc.
```
@@ -361,6 +392,7 @@ Before going to production, verify:
## Future Configuration Options
**Planned features:**
- ConfigMap-based settings (server-side, not localStorage)
- Per-cluster settings (multi-cluster Headlamp support)
- Webhook notifications for score changes
+19
View File
@@ -58,6 +58,7 @@ Click the refresh button to fetch the latest audit data immediately (bypasses au
Navigate to **Polaris → Namespaces** to see all namespaces with audit results.
**Table Columns:**
- **Namespace** - Clickable namespace name (opens detail panel)
- **Score** - Per-namespace score with color coding
- **Pass** - Passing checks count
@@ -72,6 +73,7 @@ Navigate to **Polaris → Namespaces** to see all namespaces with audit results.
Click any namespace to open a 1000px-wide side panel with detailed information.
**Features:**
- **Namespace Score** - Color-coded score gauge
- **Check Counts** - Pass/Warning/Danger/Skipped breakdown
- **Resource Table** - Per-resource audit results:
@@ -92,6 +94,7 @@ Polaris audit results automatically appear on resource detail pages.
### Supported Resources
Inline audit sections appear on:
- Deployments
- StatefulSets
- DaemonSets
@@ -101,6 +104,7 @@ Inline audit sections appear on:
### What's Shown
**Compact Audit Section:**
- **Score Badge** - Color-coded score
- **Check Counts** - Pass/Warning/Danger summary
- **Failing Checks Table** - Only failed checks listed:
@@ -116,6 +120,7 @@ Inline audit sections appear on:
Top-right corner of Headlamp shows a persistent cluster score badge.
**Features:**
- **Color-Coded Chip** - Green/Yellow/Red based on score
- **Shield Icon** - Visual indicator
- **Score Percentage** - e.g., "85%"
@@ -134,12 +139,14 @@ Access plugin settings via **Settings → Plugins → Polaris**.
Controls how often the plugin fetches new audit data.
**Options:**
- 1 minute - Most frequent (highest API load)
- 5 minutes - **Default** (recommended)
- 10 minutes - Moderate refresh rate
- 30 minutes - Light load (large clusters)
**Impact:**
- Affects all views (dashboard, namespaces, inline audits, app bar badge)
- Longer intervals reduce Kubernetes API audit logging
- Changes take effect immediately (no restart required)
@@ -151,11 +158,13 @@ See [Configuration Guide](configuration.md) for details.
Specifies which Polaris instance to connect to.
**Default:** Kubernetes service proxy path
```
/api/v1/namespaces/polaris/services/polaris-dashboard:80/proxy/
```
**Custom Options:**
- External Polaris: `https://polaris.example.com/`
- Different namespace: `/api/v1/namespaces/custom-ns/services/polaris-dashboard:80/proxy/`
@@ -168,6 +177,7 @@ See [Configuration Guide](configuration.md) for advanced setup.
Full theme adaptation for Headlamp's light and dark modes.
**Features:**
- **Auto Dark Mode** - Respects system preference when Headlamp uses it
- **Theme Toggle** - Adapts when you change Headlamp theme
- **All UI Elements** - Drawer backgrounds, tables, buttons, badges, score gauge
@@ -180,6 +190,7 @@ Full theme adaptation for Headlamp's light and dark modes.
**Status:** Planned feature (UI components exist but not fully integrated)
**Future Capability:**
- View current exemptions on resources
- Add exemptions for specific failing checks
- Remove exemptions
@@ -190,21 +201,25 @@ This feature requires additional RBAC permissions (PATCH on workload resources)
## Data Refresh Behavior
**Initial Load:**
- Data fetched when you first navigate to any Polaris view
- Shared across all views via React Context (no duplicate fetches)
- Loading spinner displayed during initial fetch
**Auto-Refresh:**
- Configured via Settings → Plugins → Polaris
- Default: 5 minutes
- Triggers background fetch without disrupting UI
**Manual Refresh:**
- Click refresh button on overview dashboard
- Forces immediate data fetch
- Updates all views simultaneously
**Error Handling:**
- 403 errors show RBAC permission guidance
- 404/503 errors indicate Polaris not installed
- Network errors show generic failure with retry suggestion
@@ -212,12 +227,14 @@ This feature requires additional RBAC permissions (PATCH on workload resources)
## Browser Requirements
**Supported Browsers:**
- Chrome/Chromium 80+
- Firefox 75+
- Safari 13.1+
- Edge 80+
**Required:**
- JavaScript enabled
- localStorage enabled (for settings persistence)
- Cookies enabled (for Headlamp session)
@@ -227,11 +244,13 @@ This feature requires additional RBAC permissions (PATCH on workload resources)
**Bundle Size:** ~27 KB minified (gzip: ~7.6 KB)
**Data Volume:** Depends on cluster size. Example:
- Small cluster (50 resources): ~100 KB JSON
- Medium cluster (500 resources): ~1 MB JSON
- Large cluster (5000 resources): ~10 MB JSON
**Rendering Performance:**
- Handles up to 100 namespaces without virtual scrolling
- Namespace detail drawer renders instantly for up to 500 resources
- React Context prevents unnecessary re-fetches
+65 -34
View File
@@ -13,6 +13,7 @@ The plugin requires **one permission** to function:
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 `get` verb on `services/proxy` resource
- Scoped to specific service (`polaris-dashboard`) for security
@@ -37,13 +38,14 @@ metadata:
app.kubernetes.io/name: headlamp-polaris-plugin
app.kubernetes.io/component: rbac
rules:
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["polaris-dashboard"]
verbs: ["get"]
- apiGroups: ['']
resources: ['services/proxy']
resourceNames: ['polaris-dashboard']
verbs: ['get']
```
**Key points:**
- **Role** (not ClusterRole) - Scoped to `polaris` namespace only
- **resourceNames** - Restricts access to `polaris-dashboard` service only
- **verbs: ["get"]** - Read-only permission
@@ -62,8 +64,8 @@ metadata:
app.kubernetes.io/component: rbac
subjects:
- kind: ServiceAccount
name: headlamp # Adjust to your Headlamp SA name
namespace: kube-system # Adjust to Headlamp's namespace
name: headlamp # Adjust to your Headlamp SA name
namespace: kube-system # Adjust to Headlamp's namespace
roleRef:
kind: Role
name: polaris-proxy-reader
@@ -71,6 +73,7 @@ roleRef:
```
**Adjust for your environment:**
- `subjects[0].name` - Your Headlamp service account name (often `headlamp`)
- `subjects[0].namespace` - Namespace where Headlamp runs (often `kube-system`)
@@ -104,10 +107,12 @@ In token-auth mode, **each user's own identity** is used for Kubernetes API requ
### 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.com` or `system:serviceaccount:team-ns:user-sa`
- **Each user needs individual RBAC permissions**
@@ -125,7 +130,7 @@ metadata:
namespace: polaris
subjects:
- kind: Group
name: system:authenticated # All authenticated users
name: system:authenticated # All authenticated users
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
@@ -174,7 +179,7 @@ metadata:
namespace: polaris
subjects:
- kind: Group
name: sre-team # OIDC group claim
name: sre-team # OIDC group claim
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: devops-team
@@ -186,11 +191,13 @@ roleRef:
```
**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:**
```json
{
"sub": "user@example.com",
@@ -229,23 +236,23 @@ apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: polaris-proxy-reader
namespace: team-a-polaris # First Polaris instance
namespace: team-a-polaris # First Polaris instance
rules:
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["polaris-dashboard"]
verbs: ["get"]
- 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
namespace: team-b-polaris # Second Polaris instance
rules:
- apiGroups: [""]
resources: ["services/proxy"]
resourceNames: ["polaris-dashboard"]
verbs: ["get"]
- apiGroups: ['']
resources: ['services/proxy']
resourceNames: ['polaris-dashboard']
verbs: ['get']
```
### Create RoleBindings per Namespace
@@ -343,22 +350,24 @@ When using OAuth2/OIDC authentication with Headlamp:
### Required Configuration
**Headlamp Helm values:**
```yaml
env:
- name: HEADLAMP_CONFIG_OIDC_CLIENT_ID
value: "headlamp"
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"
value: 'https://auth.example.com/realms/kubernetes'
- name: HEADLAMP_CONFIG_OIDC_SCOPES
value: "openid,profile,email,groups"
value: 'openid,profile,email,groups'
```
**RBAC for OIDC users:**
```yaml
---
apiVersion: rbac.authorization.k8s.io/v1
@@ -368,7 +377,7 @@ metadata:
namespace: polaris
subjects:
- kind: Group
name: kubernetes-admins # OIDC group claim
name: kubernetes-admins # OIDC group claim
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
@@ -421,32 +430,36 @@ Every plugin data fetch creates a Kubernetes API audit log entry.
### 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**
```yaml
# kube-apiserver audit policy
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata # Log metadata only, not full request/response
verbs: ["get"]
- level: Metadata # Log metadata only, not full request/response
verbs: ['get']
resources:
- group: ""
resources: ["services/proxy"]
namespaces: ["polaris"]
- group: ''
resources: ['services/proxy']
namespaces: ['polaris']
```
**Option 3: Filter audit logs**
@@ -461,18 +474,23 @@ If using a log aggregator (e.g., Elasticsearch), create filters to exclude or do
**Diagnosis:**
1. **Check Role exists:**
```bash
kubectl -n polaris get role polaris-proxy-reader
```
If missing: Apply Role manifest
2. **Check RoleBinding exists:**
```bash
kubectl -n polaris get rolebinding headlamp-polaris-proxy
```
If missing: Apply RoleBinding manifest
3. **Test permission:**
```bash
# Service account mode
kubectl auth can-i get services/proxy \
@@ -486,6 +504,7 @@ If using a log aggregator (e.g., Elasticsearch), create filters to exclude or do
-n polaris \
--resource-name=polaris-dashboard
```
Expected: `yes`
4. **Verify RoleBinding subjects match:**
@@ -499,6 +518,7 @@ If using a log aggregator (e.g., Elasticsearch), create filters to exclude or do
**This is NOT an RBAC issue.** 404 means Polaris service doesn't exist.
**Check:**
```bash
kubectl -n polaris get svc polaris-dashboard
```
@@ -510,14 +530,17 @@ If missing, install Polaris with dashboard enabled.
**Possible causes:**
1. **Wrong namespace in RoleBinding:**
- RoleBinding must be in `polaris` namespace (where the service is)
- Common mistake: Creating RoleBinding in `kube-system`
2. **Wrong resourceName:**
- Must match service name exactly: `polaris-dashboard`
- Check: `kubectl -n polaris get svc`
3. **Browser caching old 403:**
- Hard refresh browser: Cmd+Shift+R (Mac) or Ctrl+Shift+R (Windows/Linux)
4. **Token expired (OIDC mode):**
@@ -529,6 +552,7 @@ If missing, install Polaris with dashboard enabled.
### 1. Use Namespaced Roles (Not ClusterRoles)
✅ **Good:**
```yaml
kind: Role
metadata:
@@ -536,6 +560,7 @@ metadata:
```
❌ **Bad:**
```yaml
kind: ClusterRole
# Grants access to all namespaces
@@ -546,13 +571,15 @@ kind: ClusterRole
### 2. Always Specify resourceNames
✅ **Good:**
```yaml
resourceNames: ["polaris-dashboard"]
resourceNames: ['polaris-dashboard']
```
❌ **Bad:**
```yaml
resourceNames: [] # Allows access to ALL services
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.
@@ -560,13 +587,15 @@ resourceNames: [] # Allows access to ALL services
### 3. Use Read-Only Verb
✅ **Good:**
```yaml
verbs: ["get"]
verbs: ['get']
```
❌ **Bad:**
```yaml
verbs: ["get", "create", "update", "delete"]
verbs: ['get', 'create', 'update', 'delete']
```
**Why:** Plugin only needs `get` to fetch audit results. Additional verbs violate principle of least privilege.
@@ -580,6 +609,7 @@ verbs: ["get", "create", "update", "delete"]
### 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)
@@ -587,11 +617,12 @@ Set alerts for:
### 6. Avoid Wildcard Permissions
❌ **Never do this:**
```yaml
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
- apiGroups: ['*']
resources: ['*']
verbs: ['*']
```
This grants cluster-admin equivalent permissions. Always use specific resources and verbs.