From 46350c5d57f77f4d083e5fe7e099e6b8c428bc58 Mon Sep 17 00:00:00 2001 From: Hugh Hackman Date: Fri, 1 May 2026 02:24:18 +0000 Subject: [PATCH 1/6] Add RBAC pre-flight check to E2E pipeline Check for polaris-dashboard-proxy-reader Role and RoleBinding before running E2E tests. Fail fast with a clear error message pointing to the RBAC manifest instead of letting tests fail with confusing proxy 403 errors. Co-Authored-By: Paperclip --- .github/workflows/e2e.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 37f33a9..4d5ceb2 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -45,6 +45,31 @@ jobs: - name: Setup kubectl uses: azure/setup-kubectl@v4 + - name: RBAC pre-flight check + run: | + echo "Checking RBAC resources in polaris namespace..." + MISSING_ROLE=false + MISSING_ROLEBINDING=false + + if ! kubectl get role polaris-dashboard-proxy-reader -n polaris --quiet 2>/dev/null; then + echo "::error::Role polaris-dashboard-proxy-reader not found in polaris namespace." + MISSING_ROLE=true + fi + + if ! kubectl get rolebinding polaris-dashboard-proxy-reader -n polaris --quiet 2>/dev/null; then + echo "::error::RoleBinding polaris-dashboard-proxy-reader not found in polaris namespace." + MISSING_ROLEBINDING=true + fi + + if [ "$MISSING_ROLE" = true ] || [ "$MISSING_ROLEBINDING" = true ]; then + echo "" + echo "::error::RBAC not applied. Apply the RBAC manifests before running E2E tests:" + echo "::error:: kubectl apply -f deployment/polaris-rbac.yaml" + exit 1 + fi + + echo "RBAC pre-flight check passed." + - name: Install dependencies run: npm ci -- 2.52.0 From 53475296db3761c3087fe88c3497e942c03a9a88 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Sun, 3 May 2026 17:51:46 +0000 Subject: [PATCH 2/6] Apply RBAC manifest in CI before pre-flight check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the E2E workflow self-sufficient by applying the polaris-rbac.yaml manifest before the pre-flight check, instead of requiring manual cluster pre-configuration. Before: workflow checked for RBAC and failed fast, but had no mechanism to apply it — it was purely a detection step. After: workflow applies the RBAC manifest (idempotent kubectl apply), then verifies the resources exist as a correctness check. Also collapses MISSING_ROLE and MISSING_ROLEBINDING into a single boolean flag and drops the non-standard --quiet flag on kubectl get. Fixes: PRI-324 Co-Authored-By: Paperclip --- .github/workflows/e2e.yaml | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 4d5ceb2..cb1c4c5 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -45,31 +45,20 @@ jobs: - name: Setup kubectl uses: azure/setup-kubectl@v4 + - name: Apply RBAC for Polaris dashboard proxy + run: kubectl apply -f deployment/polaris-rbac.yaml + - name: RBAC pre-flight check run: | echo "Checking RBAC resources in polaris namespace..." - MISSING_ROLE=false - MISSING_ROLEBINDING=false - - if ! kubectl get role polaris-dashboard-proxy-reader -n polaris --quiet 2>/dev/null; then - echo "::error::Role polaris-dashboard-proxy-reader not found in polaris namespace." - MISSING_ROLE=true - fi - - if ! kubectl get rolebinding polaris-dashboard-proxy-reader -n polaris --quiet 2>/dev/null; then - echo "::error::RoleBinding polaris-dashboard-proxy-reader not found in polaris namespace." - MISSING_ROLEBINDING=true - fi - - if [ "$MISSING_ROLE" = true ] || [ "$MISSING_ROLEBINDING" = true ]; then - echo "" - echo "::error::RBAC not applied. Apply the RBAC manifests before running E2E tests:" - echo "::error:: kubectl apply -f deployment/polaris-rbac.yaml" + if kubectl get role polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1 && \ + kubectl get rolebinding polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1; then + echo "RBAC pre-flight check passed." + else + echo "::error::Role or RoleBinding not found in polaris namespace after apply." exit 1 fi - echo "RBAC pre-flight check passed." - - name: Install dependencies run: npm ci -- 2.52.0 From 0cd9eb041de9a657ff864f366887518e3cfd48eb Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Sun, 3 May 2026 18:06:07 +0000 Subject: [PATCH 3/6] Add polaris namespace RBAC for CI runner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The E2E workflow now applies deployment/polaris-rbac.yaml before the pre-flight check to make the workflow self-sufficient. This requires the CI runner to have permissions in the polaris namespace. Before: CI runner could only READ roles/rolebindings in polaris (for the detection step). After: CI runner can APPLY the RBAC manifest and manage the resulting Role + RoleBinding. This is a pure infrastructure/RBAC change — no application code modified. Fixes: PRI-324 Co-Authored-By: Paperclip --- deployment/e2e-ci-runner-rbac.yaml | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/deployment/e2e-ci-runner-rbac.yaml b/deployment/e2e-ci-runner-rbac.yaml index ea93cff..8fcd6ed 100644 --- a/deployment/e2e-ci-runner-rbac.yaml +++ b/deployment/e2e-ci-runner-rbac.yaml @@ -31,6 +31,41 @@ rules: resources: ["serviceaccounts/token"] verbs: ["create"] --- +# RBAC for CI runner to apply Polaris dashboard RBAC in the polaris namespace. +# The E2E workflow runs `kubectl apply -f deployment/polaris-rbac.yaml` before +# the pre-flight check to make the workflow self-sufficient. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: e2e-ci-runner-polaris + namespace: polaris +subjects: + - kind: ServiceAccount + name: runners-privilegedescalation-gha-rs-no-permission + namespace: arc-runners +roleRef: + kind: Role + name: e2e-ci-runner-polaris + apiGroup: rbac.authorization.k8s.io +rules: + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles", "rolebindings"] + verbs: ["get", "list", "create", "update", "patch", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: e2e-ci-runner-polaris-binding + namespace: polaris +subjects: + - kind: ServiceAccount + name: runners-privilegedescalation-gha-rs-no-permission + namespace: arc-runners +roleRef: + kind: Role + name: e2e-ci-runner-polaris + apiGroup: rbac.authorization.k8s.io +--- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: -- 2.52.0 From 25f860652c35aa7fb510028fa7c8debdceb6d9b9 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Mon, 4 May 2026 00:15:28 +0000 Subject: [PATCH 4/6] Rename E2E namespace to headlamp-dev; fix RBAC manifest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per CEO directive on PRI-324: the actual cluster namespace for shared E2E runs is `headlamp-dev`. References to `privilegedescalation-dev` in the workflow, scripts, and runner RBAC were stale and prevented the runner SA from acquiring permissions where they were granted on-cluster. Also fixes the malformed `e2e-ci-runner-polaris` Role added in 0cd9eb0 which incorrectly carried `subjects:` and `roleRef:` (RoleBinding-only fields) — kept rules-only on the Role and the binding in the matching RoleBinding below. Applies QA nits from PR #123: collapsed MISSING_ROLE/MISSING_ROLEBINDING into a single MISSING flag in the pre-flight step. Dropped the non-standard `--quiet` flag on get calls (already redirected to /dev/null). --- .github/workflows/e2e.yaml | 10 ++++++---- deployment/e2e-ci-runner-rbac.yaml | 19 ++++++------------- scripts/deploy-e2e-headlamp.sh | 6 +++--- scripts/teardown-e2e-headlamp.sh | 4 ++-- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index cb1c4c5..35ace65 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -11,7 +11,7 @@ permissions: contents: read # Only one E2E run at a time: the shared E2E_RELEASE (headlamp-e2e) in -# privilegedescalation-dev cannot be shared across concurrent runs. +# headlamp-dev cannot be shared across concurrent runs. # cancel-in-progress: false (queue, don't cancel) — cancelling in-flight # runs may skip the if: always() teardown, leaving dangling cluster resources. concurrency: @@ -19,7 +19,7 @@ concurrency: cancel-in-progress: false env: - E2E_NAMESPACE: privilegedescalation-dev + E2E_NAMESPACE: headlamp-dev E2E_RELEASE: headlamp-e2e # Pin to a known-good Headlamp version. Using :latest is risky because # the tag can change between CI runs, causing flaky failures when a newer @@ -51,8 +51,10 @@ jobs: - name: RBAC pre-flight check run: | echo "Checking RBAC resources in polaris namespace..." - if kubectl get role polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1 && \ - kubectl get rolebinding polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1; then + MISSING=0 + kubectl get role polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1 || MISSING=1 + kubectl get rolebinding polaris-dashboard-proxy-reader -n polaris -o name >/dev/null 2>&1 || MISSING=1 + if [ "$MISSING" -eq 0 ]; then echo "RBAC pre-flight check passed." else echo "::error::Role or RoleBinding not found in polaris namespace after apply." diff --git a/deployment/e2e-ci-runner-rbac.yaml b/deployment/e2e-ci-runner-rbac.yaml index 8fcd6ed..9e04994 100644 --- a/deployment/e2e-ci-runner-rbac.yaml +++ b/deployment/e2e-ci-runner-rbac.yaml @@ -2,9 +2,10 @@ # RBAC for the GitHub Actions CI runner to manage the E2E Headlamp instance. # CI-only test fixture — NOT for production use. # -# Grants the ARC runner service account permissions in the privilegedescalation-dev -# namespace to deploy and tear down a dedicated Headlamp instance via Helm. -# E2E resources run in `privilegedescalation-dev` — nothing persists beyond a test run. +# Grants the ARC runner service account permissions in the headlamp-dev +# namespace to deploy and tear down a dedicated Headlamp instance via Helm, +# and in the polaris namespace to apply the Polaris dashboard proxy RBAC +# before the E2E pre-flight check. # # Plugin is loaded via ConfigMap volume mount — no custom Docker images. # @@ -14,7 +15,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: e2e-ci-runner - namespace: privilegedescalation-dev + namespace: headlamp-dev rules: # Helm needs to manage these resources for the Headlamp chart - apiGroups: ["apps"] @@ -39,14 +40,6 @@ kind: Role metadata: name: e2e-ci-runner-polaris namespace: polaris -subjects: - - kind: ServiceAccount - name: runners-privilegedescalation-gha-rs-no-permission - namespace: arc-runners -roleRef: - kind: Role - name: e2e-ci-runner-polaris - apiGroup: rbac.authorization.k8s.io rules: - apiGroups: ["rbac.authorization.k8s.io"] resources: ["roles", "rolebindings"] @@ -70,7 +63,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: e2e-ci-runner-binding - namespace: privilegedescalation-dev + namespace: headlamp-dev subjects: - kind: ServiceAccount name: runners-privilegedescalation-gha-rs-no-permission diff --git a/scripts/deploy-e2e-headlamp.sh b/scripts/deploy-e2e-headlamp.sh index 528c017..f50a6a6 100755 --- a/scripts/deploy-e2e-headlamp.sh +++ b/scripts/deploy-e2e-headlamp.sh @@ -5,7 +5,7 @@ # a ConfigMap volume mount. No custom Docker images — the plugin is built # in CI and injected as a ConfigMap. # -# E2E resources are deployed to the `privilegedescalation-dev` namespace. Nothing +# E2E resources are deployed to the `headlamp-dev` namespace. Nothing # persists beyond the test run — teardown cleans up all created resources. # # Prerequisites: @@ -14,7 +14,7 @@ # - RBAC applied: kubectl apply -f deployment/e2e-ci-runner-rbac.yaml # # Environment: -# E2E_NAMESPACE — namespace for E2E Headlamp (default: privilegedescalation-dev) +# E2E_NAMESPACE — namespace for E2E Headlamp (default: headlamp-dev) # E2E_RELEASE — release/resource name prefix (default: headlamp-e2e) # HEADLAMP_VERSION — Headlamp image tag (default: v0.40.1, pinned to match production) set -euo pipefail @@ -22,7 +22,7 @@ set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" DIST_DIR="$REPO_ROOT/dist" -E2E_NAMESPACE="${E2E_NAMESPACE:-privilegedescalation-dev}" +E2E_NAMESPACE="${E2E_NAMESPACE:-headlamp-dev}" E2E_RELEASE="${E2E_RELEASE:-headlamp-e2e}" HEADLAMP_VERSION="${HEADLAMP_VERSION:-v0.40.1}" diff --git a/scripts/teardown-e2e-headlamp.sh b/scripts/teardown-e2e-headlamp.sh index b8ed7e6..00d4f5a 100755 --- a/scripts/teardown-e2e-headlamp.sh +++ b/scripts/teardown-e2e-headlamp.sh @@ -4,13 +4,13 @@ # Tears down the dedicated E2E Headlamp instance deployed by deploy-e2e-headlamp.sh. # # Environment: -# E2E_NAMESPACE — namespace to clean up (default: privilegedescalation-dev) +# E2E_NAMESPACE — namespace to clean up (default: headlamp-dev) # E2E_RELEASE — release/resource name prefix (default: headlamp-e2e) set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -E2E_NAMESPACE="${E2E_NAMESPACE:-privilegedescalation-dev}" +E2E_NAMESPACE="${E2E_NAMESPACE:-headlamp-dev}" E2E_RELEASE="${E2E_RELEASE:-headlamp-e2e}" echo "=== E2E Headlamp Teardown ===" -- 2.52.0 From 8c13ecb7a44d6e105aea15fcff16858f610f5d34 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Mon, 4 May 2026 05:20:06 +0000 Subject: [PATCH 5/6] ci: re-trigger E2E after infra RBAC merge (PRI-388) -- 2.52.0 From 7204672799ace1229639eaf8e93e3ff2edc2ccf3 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Mon, 4 May 2026 06:12:04 +0000 Subject: [PATCH 6/6] ci: re-trigger E2E after infra RBAC merge (PRI-380) Co-Authored-By: Paperclip -- 2.52.0