Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cbe1c9d0db | |||
| 091a8dad0e | |||
| 1ba1a67f01 | |||
| 22b4bdeba0 | |||
| a10d274e71 | |||
| 8c256f9dc5 | |||
| 98e0cf7ea1 | |||
| cbf5ba4a2a | |||
| 1c5e50ce8c | |||
| b4e6cb9367 | |||
| 6459913304 | |||
| d9fec8b93c | |||
| dd2d942d39 | |||
| 8e9b2c2645 | |||
| ac3d9e87ca |
@@ -2,9 +2,9 @@ name: CI
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main, dev, uat]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main, dev, uat]
|
||||||
workflow_call:
|
workflow_call:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
name: Dual Approval (CTO + QA)
|
name: Promotion Gate
|
||||||
|
|
||||||
# Calls the shared dual-approval-check workflow.
|
# Calls the shared promotion gate workflow.
|
||||||
# Passes when both privilegedescalation-cto and privilegedescalation-qa
|
# dev PRs: no gate (engineer self-merges).
|
||||||
# have approved the PR. Add "Dual Approval (CTO + QA)" to required_status_checks
|
# uat PRs: QA approval required.
|
||||||
# in branch protection to enforce this gate.
|
# main PRs: UAT approval required (uat→main promotions).
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request_review:
|
pull_request_review:
|
||||||
types: [submitted, dismissed]
|
types: [submitted, dismissed]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [uat, main]
|
||||||
types: [opened, reopened, synchronize]
|
types: [opened, reopened, synchronize]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
dual-approval:
|
promotion-gate:
|
||||||
uses: privilegedescalation/.github/.github/workflows/dual-approval-check.yaml@main
|
uses: privilegedescalation/.github/.github/workflows/dual-approval-check.yaml@main
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
pr_number: ${{ github.event.pull_request.number }}
|
pr_number: ${{ github.event.pull_request.number }}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
name: Mend Renovate GitHub App Token
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
outputs:
|
||||||
|
token:
|
||||||
|
description: "Short-lived GitHub App installation token"
|
||||||
|
value: ${{ jobs.app-token.outputs.token }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
app-token:
|
||||||
|
runs-on: runners-privilegedescalation
|
||||||
|
outputs:
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
steps:
|
||||||
|
- name: Generate GitHub App token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v3
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: Renovate
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 3 * * 0'
|
||||||
|
workflow_dispatch:
|
||||||
|
jobs:
|
||||||
|
renovate:
|
||||||
|
runs-on: runners-privilegedescalation
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Generate GitHub App token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v3
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.RELEASE_APP_ID }}
|
||||||
|
private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
|
||||||
|
- uses: renovatebot/github-action@v40.3.0
|
||||||
|
with:
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
configurationFile: renovate.json
|
||||||
@@ -5,3 +5,9 @@ dist/
|
|||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
|
# E2E
|
||||||
|
e2e/.auth/
|
||||||
|
.env.e2e
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"config": {
|
||||||
|
// Line length — not enforced for docs with code examples
|
||||||
|
"MD013": false,
|
||||||
|
// First line heading — files use YAML frontmatter, not headings
|
||||||
|
"MD041": false,
|
||||||
|
// Emphasis as heading — common pattern for Option 1/2/3 sections
|
||||||
|
"MD036": false,
|
||||||
|
// No duplicate heading — changelog files repeat section names intentionally
|
||||||
|
"MD024": false,
|
||||||
|
// Fenced code language — not always applicable for diagram blocks
|
||||||
|
"MD040": false,
|
||||||
|
// Table column style — table alignment is visual, not semantic
|
||||||
|
"MD060": false,
|
||||||
|
// Ordered list item prefix — number resets are intentional in documents
|
||||||
|
"MD029": false,
|
||||||
|
// No inline HTML — each elements are valid in valid Markdown
|
||||||
|
"MD033": false,
|
||||||
|
// List marker space — spacing after list markers varies by editor
|
||||||
|
"MD030": false,
|
||||||
|
// Blanks around headings — not always needed in compact docs
|
||||||
|
"MD022": false,
|
||||||
|
// Blanks around lists — not always needed in compact docs
|
||||||
|
"MD032": false,
|
||||||
|
// Blanks around fences — not always needed between adjacent blocks
|
||||||
|
"MD031": false,
|
||||||
|
// Multiple blanks — editor artifacts, not semantic
|
||||||
|
"MD012": false,
|
||||||
|
// Single title — files may have multiple H1 sections
|
||||||
|
"MD025": false,
|
||||||
|
// Trailing spaces — editor artifacts
|
||||||
|
"MD009": false,
|
||||||
|
// Bare URLs — URL shortening not always needed
|
||||||
|
"MD034": false,
|
||||||
|
// Single trailing newline — editor artifacts
|
||||||
|
"MD047": false,
|
||||||
|
// Trailing punctuation — heading punctuation is intentional
|
||||||
|
"MD026": false,
|
||||||
|
// Space in emphasis — double-asterisk bold spacing varies by renderer
|
||||||
|
"MD037": false,
|
||||||
|
// No hard tabs — some generated docs use tabs for indentation
|
||||||
|
"MD010": false,
|
||||||
|
// Code block style — generated docs may use inconsistent styles
|
||||||
|
"MD046": false,
|
||||||
|
// Comment style — generated docs have no comments
|
||||||
|
"MD048": false,
|
||||||
|
// Commands show output — shell examples intentionally show only commands
|
||||||
|
"MD014": false
|
||||||
|
},
|
||||||
|
"ignores": [
|
||||||
|
"docs/api-reference/generated/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
docs/api-reference/generated/**
|
||||||
@@ -8,7 +8,7 @@ Headlamp plugin for kube-vip virtual IP and load balancer visibility. Read-only
|
|||||||
|
|
||||||
- **Plugin name**: `kube-vip`
|
- **Plugin name**: `kube-vip`
|
||||||
- **Target**: Headlamp >= v0.26
|
- **Target**: Headlamp >= v0.26
|
||||||
- **Data sources**: kube-vip DaemonSet/pods in `kube-system`, Services (type:LoadBalancer), Nodes, Leases, `kubevip` ConfigMap
|
- **Data sources**: kube-vip DaemonSet/pods in `headlamp`, Services (type:LoadBalancer), Nodes, Leases, `kubevip` ConfigMap
|
||||||
- **Reference plugin**: `../headlamp-polaris-plugin`
|
- **Reference plugin**: `../headlamp-polaris-plugin`
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
@@ -58,7 +58,7 @@ kube-vip uses **no CRDs**. All state comes from standard Kubernetes resources an
|
|||||||
|
|
||||||
## Key constants (src/api/k8s.ts)
|
## Key constants (src/api/k8s.ts)
|
||||||
|
|
||||||
- Namespace: `kube-system`
|
- Namespace: `headlamp`
|
||||||
- DaemonSet name: `kube-vip-ds`
|
- DaemonSet name: `kube-vip-ds`
|
||||||
- Cloud provider name: `kube-vip-cloud-provider`
|
- Cloud provider name: `kube-vip-cloud-provider`
|
||||||
- ConfigMap name: `kubevip`
|
- ConfigMap name: `kubevip`
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Search for `kube-vip` in the Headlamp Plugin Manager (Settings → Plugins → C
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Headlamp >= v0.26
|
- Headlamp >= v0.26
|
||||||
- kube-vip deployed in `kube-system` (DaemonSet or static pod)
|
- kube-vip deployed in `headlamp` (DaemonSet or static pod)
|
||||||
- Optional: kube-vip-cloud-provider for IP pool management
|
- Optional: kube-vip-cloud-provider for IP pool management
|
||||||
|
|
||||||
## RBAC
|
## RBAC
|
||||||
@@ -66,7 +66,7 @@ npm run lint # ESLint
|
|||||||
|
|
||||||
| Symptom | Cause | Fix |
|
| Symptom | Cause | Fix |
|
||||||
|---------|-------|-----|
|
|---------|-------|-----|
|
||||||
| "kube-vip Not Detected" | No kube-vip pods in kube-system | Install kube-vip per https://kube-vip.io/docs/installation/ |
|
| "kube-vip Not Detected" | No kube-vip pods in headlamp namespace | Install kube-vip per https://kube-vip.io/docs/installation/ |
|
||||||
| No IP pools shown | kubevip ConfigMap not found | Install kube-vip-cloud-provider |
|
| No IP pools shown | kubevip ConfigMap not found | Install kube-vip-cloud-provider |
|
||||||
| Services show "Pending" VIP | No IP pool configured or pool exhausted | Add IP ranges to kubevip ConfigMap |
|
| Services show "Pending" VIP | No IP pool configured or pool exhausted | Add IP ranges to kubevip ConfigMap |
|
||||||
| Leader shows "—" | No kube-vip leases found | Verify leader election is enabled (`vip_leaderelection=true`) |
|
| Leader shows "—" | No kube-vip leases found | Verify leader election is enabled (`vip_leaderelection=true`) |
|
||||||
|
|||||||
+29
-4
@@ -12,13 +12,38 @@ This plugin is **read-only**. It does not perform any write operations against t
|
|||||||
|
|
||||||
- Services (type: LoadBalancer)
|
- Services (type: LoadBalancer)
|
||||||
- Nodes
|
- Nodes
|
||||||
- Pods in `kube-system`
|
- Pods in `headlamp`
|
||||||
- DaemonSets in `kube-system`
|
- DaemonSets in `headlamp`
|
||||||
- Leases in `kube-system`
|
- Leases in `headlamp`
|
||||||
- ConfigMaps in `kube-system`
|
- ConfigMaps in `headlamp`
|
||||||
|
|
||||||
All data is fetched through Headlamp's built-in API proxy, which respects the user's existing RBAC permissions.
|
All data is fetched through Headlamp's built-in API proxy, which respects the user's existing RBAC permissions.
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
Please report security vulnerabilities by opening a private issue or emailing the maintainers directly.
|
Please report security vulnerabilities by opening a private issue or emailing the maintainers directly.
|
||||||
|
|
||||||
|
## Known Low-Severity Vulnerabilities
|
||||||
|
|
||||||
|
### GHSA-848j-6mx2-7j84 (elliptic)
|
||||||
|
|
||||||
|
**Severity:** High (but not exploitable in this plugin's context)
|
||||||
|
|
||||||
|
**Affected component:** `elliptic` (transitive, via `vite-plugin-node-polyfills` → `node-stdlib-browser` → `crypto-browserify` → `browserify-sign`)
|
||||||
|
|
||||||
|
**Description:** The elliptic library used in this plugin's development dependencies contains a prototype pollution vulnerability. This plugin is a **read-only** Headlamp plugin that never executes any cryptographic operations at runtime. The vulnerable code path requires:
|
||||||
|
- Use of `elliptic` curve operations on untrusted input, AND
|
||||||
|
- Ability for an attacker to influence the `elliptic` curve key generation input
|
||||||
|
|
||||||
|
Neither condition is met in this plugin's runtime context.
|
||||||
|
|
||||||
|
**Remediation:** No patched version of `elliptic` exists on npm. The current override in `package.json` (`"elliptic": ">=6.6.1"`) is a placeholder — no resolvable version satisfies this constraint.
|
||||||
|
|
||||||
|
**Risk acceptance rationale:**
|
||||||
|
1. Plugin has no write operations against the cluster
|
||||||
|
2. All data flows through Headlamp's API proxy with standard RBAC enforcement
|
||||||
|
3. The vulnerable dependency is only in the development/build toolchain, not runtime
|
||||||
|
4. No untrusted input can reach `elliptic` curve operations through this plugin
|
||||||
|
|
||||||
|
**Review date:** 2026-05-05
|
||||||
|
**Reviewed by:** Hugh Hackman (VP Engineering Operations)
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
version: "1.0.1"
|
version: "1.0.2"
|
||||||
name: headlamp-kube-vip
|
name: headlamp-kube-vip
|
||||||
displayName: kube-vip
|
displayName: kube-vip
|
||||||
createdAt: "2026-03-04T00:00:00Z"
|
createdAt: "2026-03-04T00:00:00Z"
|
||||||
@@ -25,8 +25,8 @@ maintainers:
|
|||||||
provider:
|
provider:
|
||||||
name: privilegedescalation
|
name: privilegedescalation
|
||||||
annotations:
|
annotations:
|
||||||
headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-kube-vip-plugin/releases/download/v1.0.1/kube-vip-1.0.1.tar.gz"
|
headlamp/plugin/archive-url: "https://github.com/privilegedescalation/headlamp-kube-vip-plugin/releases/download/v1.0.2/kube-vip-1.0.2.tar.gz"
|
||||||
headlamp/plugin/archive-checksum: sha256:b5568aa3706a75edfa80ba60335587107df0eea2efedc2123ebbffd2934d25d9
|
headlamp/plugin/archive-checksum: sha256:cb6b8b6d93a41c129304c57ed705cdafbcb4d6e7511ce5bad0aa05d5762c3fbf
|
||||||
headlamp/plugin/version-compat: ">=0.26"
|
headlamp/plugin/version-compat: ">=0.26"
|
||||||
headlamp/plugin/distro-compat: "in-cluster"
|
headlamp/plugin/distro-compat: "in-cluster"
|
||||||
changes:
|
changes:
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// Allowlist for inherited dev-dependency CVEs from @kinvolk/headlamp-plugin
|
||||||
|
// CTO decision (PRI-854): these high-severity vulns are dev/build-time only,
|
||||||
|
// trace to @kinvolk/headlamp-plugin transitive deps (Picomatch, Vite, lodash),
|
||||||
|
// and do NOT ship in production plugin artifacts.
|
||||||
|
"allowlist": [
|
||||||
|
{
|
||||||
|
"id": "GHSA-hhpm-516h-p3p6",
|
||||||
|
"reason": "Picomatch ReDoS: devDependency only, does not ship in production plugin bundle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "GHSA-36xf-7xpp-53w5",
|
||||||
|
"reason": "Vite arbitrary file read: devDependency only, does not ship in production plugin bundle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "GHSA-jf8v-p3pp-93qh",
|
||||||
|
"reason": "lodash code injection via _.template: devDependency only, does not ship in production plugin bundle"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+5
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kube-vip",
|
"name": "kube-vip",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"description": "Headlamp plugin for kube-vip virtual IP and load balancer visibility",
|
"description": "Headlamp plugin for kube-vip virtual IP and load balancer visibility",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -31,7 +31,10 @@
|
|||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"tar": "^7.5.11",
|
"tar": "^7.5.11",
|
||||||
"undici": "^7.24.3"
|
"undici": "^7.24.3",
|
||||||
|
"lodash": ">=4.18.0",
|
||||||
|
"vite": ">=6.4.2",
|
||||||
|
"elliptic": ">=6.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@headlamp-k8s/eslint-config": "^0.6.0",
|
"@headlamp-k8s/eslint-config": "^0.6.0",
|
||||||
|
|||||||
Generated
+934
-851
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ import {
|
|||||||
isEgressEnabled,
|
isEgressEnabled,
|
||||||
isKubeVipService,
|
isKubeVipService,
|
||||||
isPodReady,
|
isPodReady,
|
||||||
|
KUBE_VIP_NAMESPACE,
|
||||||
phaseToStatus,
|
phaseToStatus,
|
||||||
} from '../api/k8s';
|
} from '../api/k8s';
|
||||||
import { useKubeVipContext } from '../api/KubeVipDataContext';
|
import { useKubeVipContext } from '../api/KubeVipDataContext';
|
||||||
@@ -105,7 +106,9 @@ export default function OverviewPage() {
|
|||||||
{
|
{
|
||||||
name: 'Status',
|
name: 'Status',
|
||||||
value: (
|
value: (
|
||||||
<StatusLabel status="error">No kube-vip pods found in kube-system</StatusLabel>
|
<StatusLabel status="error">
|
||||||
|
No kube-vip pods found in {KUBE_VIP_NAMESPACE}
|
||||||
|
</StatusLabel>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user