diff --git a/packages/runtime-security/tasks.yaml b/packages/runtime-security/tasks.yaml index 9cf0cec0a..81cdf7fa8 100644 --- a/packages/runtime-security/tasks.yaml +++ b/packages/runtime-security/tasks.yaml @@ -8,3 +8,4 @@ tasks: - name: validate actions: - task: neuvector:validate + - task: neuvector:e2e-test diff --git a/src/neuvector/chart/templates/uds-package.yaml b/src/neuvector/chart/templates/uds-package.yaml index 484b6866e..ff645dd34 100644 --- a/src/neuvector/chart/templates/uds-package.yaml +++ b/src/neuvector/chart/templates/uds-package.yaml @@ -81,6 +81,16 @@ spec: selector: app: neuvector-updater-pod + - direction: Egress + remoteGenerated: KubeAPI + selector: + app: neuvector-cert-upgrader-pod + + - direction: Egress + remoteGenerated: KubeAPI + selector: + app: neuvector-scanner-pod + - direction: Egress remoteGenerated: KubeAPI selector: diff --git a/src/neuvector/common/zarf.yaml b/src/neuvector/common/zarf.yaml index 730e0310f..fa46c34d0 100644 --- a/src/neuvector/common/zarf.yaml +++ b/src/neuvector/common/zarf.yaml @@ -14,7 +14,7 @@ components: charts: - name: crd url: https://neuvector.github.io/neuvector-helm/ - version: 2.7.9 + version: 2.8.2 namespace: neuvector gitPath: charts/crd - name: uds-neuvector-config @@ -23,14 +23,14 @@ components: localPath: ../chart - name: core url: https://neuvector.github.io/neuvector-helm/ - version: 2.7.9 + version: 2.8.2 namespace: neuvector gitPath: charts/core valuesFiles: - ../values/values.yaml # - name: monitor # url: https://neuvector.github.io/neuvector-helm/ - # version: 2.7.9 + # version: 2.8.2 # namespace: neuvector # gitPath: charts/monitor # valuesFiles: diff --git a/src/neuvector/tasks.yaml b/src/neuvector/tasks.yaml index 1609e8b94..2f66394ec 100644 --- a/src/neuvector/tasks.yaml +++ b/src/neuvector/tasks.yaml @@ -1,6 +1,9 @@ # Copyright 2024 Defense Unicorns # SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial +includes: + - utils: ../../tasks/utils.yaml + tasks: - name: validate actions: @@ -41,4 +44,17 @@ tasks: - name: e2e-test actions: - - description: "Run Neuvector E2E tests" + - description: "Setup the Keycloak admin user if needed" + task: utils:keycloak-admin-user + - description: "Setup the Doug User for testing" + # Self-reference this task file to avoid https://github.com/defenseunicorns/maru-runner/issues/144 + cmd: uds run -f tasks/test.yaml common-setup:create-doug-user --set KEYCLOAK_GROUP="/UDS Core/Admin" --no-progress # Adds the test doug user + - description: E2E Test for NeuVector + cmd: | + # renovate: datasource=docker depName=mcr.microsoft.com/playwright versioning=docker + docker run --rm --ipc=host -e FULL_CORE="${FULL_CORE}" --net=host --mount type=bind,source="$(pwd)",target=/app mcr.microsoft.com/playwright:v1.49.0-noble sh -c " \ + cd app && \ + npm ci && \ + npx playwright test neuvector.test.ts \ + " + dir: test/playwright diff --git a/src/neuvector/values/registry1-values.yaml b/src/neuvector/values/registry1-values.yaml index 436d488d7..c3f34fc57 100644 --- a/src/neuvector/values/registry1-values.yaml +++ b/src/neuvector/values/registry1-values.yaml @@ -3,7 +3,7 @@ registry: registry1.dso.mil # renovate: datasource=docker depName=registry1.dso.mil/ironbank/neuvector/neuvector/controller versioning=docker -tag: "5.3.4" +tag: "5.4.0" manager: image: repository: ironbank/neuvector/neuvector/manager @@ -47,7 +47,7 @@ cve: image: repository: ironbank/redhat/ubi/ubi9-minimal # renovate: datasource=docker depName=registry1.dso.mil/ironbank/redhat/ubi/ubi9-minimal versioning=docker - tag: "9.4" + tag: "9.5" containerSecurityContext: capabilities: drop: diff --git a/src/neuvector/values/unicorn-values.yaml b/src/neuvector/values/unicorn-values.yaml index f34c5c87a..bd22a04a6 100644 --- a/src/neuvector/values/unicorn-values.yaml +++ b/src/neuvector/values/unicorn-values.yaml @@ -6,7 +6,7 @@ autoGenerateCert: true registry: cgr.dev # renovate: datasource=docker depName=cgr.dev/du-uds-defenseunicorns/neuvector-controller-fips versioning=docker -tag: "5.3.4" +tag: "5.4.0" manager: image: repository: du-uds-defenseunicorns/neuvector-manager @@ -41,4 +41,4 @@ cve: image: repository: du-uds-defenseunicorns/neuvector-updater-fips # renovate: datasource=docker depName=cgr.dev/du-uds-defenseunicorns/neuvector-updater-fips versioning=docker - tag: 8.10.1-dev + tag: 8.11.0-dev diff --git a/src/neuvector/values/upstream-values.yaml b/src/neuvector/values/upstream-values.yaml index 0391ab7a2..fe0b33014 100644 --- a/src/neuvector/values/upstream-values.yaml +++ b/src/neuvector/values/upstream-values.yaml @@ -3,7 +3,7 @@ registry: docker.io # renovate: datasource=docker depName=docker.io/neuvector/controller versioning=docker -tag: "5.3.4" +tag: "5.4.0" manager: image: repository: neuvector/manager diff --git a/src/neuvector/values/values.yaml b/src/neuvector/values/values.yaml index a11c8d823..473e78a24 100644 --- a/src/neuvector/values/values.yaml +++ b/src/neuvector/values/values.yaml @@ -10,6 +10,9 @@ manager: svc: type: ClusterIP +internal: + autoRotateCert: true + controller: apisvc: type: ClusterIP diff --git a/src/neuvector/zarf.yaml b/src/neuvector/zarf.yaml index 2bb8ccc13..9e7198f67 100644 --- a/src/neuvector/zarf.yaml +++ b/src/neuvector/zarf.yaml @@ -25,11 +25,11 @@ components: valuesFiles: - values/upstream-values.yaml images: - - docker.io/neuvector/controller:5.3.4 - - docker.io/neuvector/manager:5.3.4 + - docker.io/neuvector/controller:5.4.0 + - docker.io/neuvector/manager:5.4.0 - docker.io/neuvector/updater:latest - docker.io/neuvector/scanner:latest - - docker.io/neuvector/enforcer:5.3.4 + - docker.io/neuvector/enforcer:5.4.0 - name: neuvector description: "Deploy Neuvector" @@ -43,11 +43,11 @@ components: valuesFiles: - values/registry1-values.yaml images: - - registry1.dso.mil/ironbank/neuvector/neuvector/controller:5.3.4 - - registry1.dso.mil/ironbank/neuvector/neuvector/manager:5.3.4 - - registry1.dso.mil/ironbank/redhat/ubi/ubi9-minimal:9.4 + - registry1.dso.mil/ironbank/neuvector/neuvector/controller:5.4.0 + - registry1.dso.mil/ironbank/neuvector/neuvector/manager:5.4.0 + - registry1.dso.mil/ironbank/redhat/ubi/ubi9-minimal:9.5 - registry1.dso.mil/ironbank/neuvector/neuvector/scanner:5 - - registry1.dso.mil/ironbank/neuvector/neuvector/enforcer:5.3.4 + - registry1.dso.mil/ironbank/neuvector/neuvector/enforcer:5.4.0 - name: neuvector description: "Deploy Neuvector" @@ -64,8 +64,8 @@ components: valuesFiles: - values/unicorn-values.yaml images: - - cgr.dev/du-uds-defenseunicorns/neuvector-manager:5.3.4 - - cgr.dev/du-uds-defenseunicorns/neuvector-enforcer-fips:5.3.4 - - cgr.dev/du-uds-defenseunicorns/neuvector-controller-fips:5.3.4 + - cgr.dev/du-uds-defenseunicorns/neuvector-manager:5.4.0 + - cgr.dev/du-uds-defenseunicorns/neuvector-enforcer-fips:5.4.0 + - cgr.dev/du-uds-defenseunicorns/neuvector-controller-fips:5.4.0 - docker.io/neuvector/scanner:latest - - cgr.dev/du-uds-defenseunicorns/neuvector-updater-fips:8.10.1-dev + - cgr.dev/du-uds-defenseunicorns/neuvector-updater-fips:8.11.0-dev diff --git a/src/pepr/patches/index.ts b/src/pepr/patches/index.ts index 78a99c277..a27dd0a5a 100644 --- a/src/pepr/patches/index.ts +++ b/src/pepr/patches/index.ts @@ -40,3 +40,28 @@ When(a.Service) grpcPort.appProtocol = "tcp"; } }); + +/** + * Mutate the Neuvector Enforcer DaemonSet to add a livenessProbe + * Temporary until fixed upstream + */ + +When(a.DaemonSet) + .IsCreatedOrUpdated() + .InNamespace("neuvector") + .WithName("neuvector-enforcer-pod") + .Mutate(async ds => { + const enforcerContainer = ds.Raw.spec?.template.spec?.containers.find( + container => container.name === "neuvector-enforcer-pod", + ); + + if (enforcerContainer && enforcerContainer.livenessProbe === undefined) { + log.debug("Patching NeuVector Enforcer Daemonset to add livenessProbe"); + const livenessProbe = { + exec: { command: ["curl", "--no-progress-meter", "127.0.0.1:8500"] }, + periodSeconds: 10, + failureThreshold: 2, + }; + enforcerContainer.livenessProbe = livenessProbe; + } + }); diff --git a/test/playwright/neuvector.test.ts b/test/playwright/neuvector.test.ts new file mode 100644 index 000000000..2d800d306 --- /dev/null +++ b/test/playwright/neuvector.test.ts @@ -0,0 +1,89 @@ +/** + * Copyright 2024 Defense Unicorns + * SPDX-License-Identifier: AGPL-3.0-or-later OR LicenseRef-Defense-Unicorns-Commercial + */ + +import { expect, test } from "@playwright/test"; +import { domain } from "./uds.config"; + +const url = `https://neuvector.admin.${domain}` +test.use({ baseURL: url }); + +test("validate system health", async ({ page }) => { + await test.step("check sso", async () => { + await page.goto('/#/login'); + await page.waitForLoadState("domcontentloaded"); + + await expect(page.getByRole('button', { name: 'Login with OpenID' })).toBeVisible(); + const termsCheckbox = await page.locator('.mat-checkbox-inner-container'); + if (await termsCheckbox.isVisible()) { + await termsCheckbox.click(); + } + await page.getByRole('button', { name: 'Login with OpenID' }).click(); + await expect(page).toHaveURL('/#/dashboard'); + await expect(page.locator('.navbar-header')).toBeVisible(); + }); + + // Expect counts for scanner, controller, enforcer are based on chart defaults + await test.step("check system components", async () => { + await page.goto('/#/controllers'); + await page.waitForLoadState("domcontentloaded"); + + // Ensure at least three scanners are connected and at least one scan complete + await page.getByRole('tab', { name: 'Scanners' }).click(); + await page.waitForLoadState("domcontentloaded"); + const scannerPromise = page.waitForResponse(`${url}/scanner`); + await page.getByLabel('Scanners').getByRole('button', { name: 'refresh Refresh' }).click(); + const scannerResponse = await scannerPromise; + const scannerData = await scannerResponse.json(); + + expect(scannerData).toHaveProperty('scanners'); + expect(Array.isArray(scannerData.scanners)).toBe(true); + expect(scannerData.scanners.length).toBeGreaterThanOrEqual(3); + const hasScannedContainers = scannerData.scanners.some( + (scanner: { scanned_containers: number }) => scanner.scanned_containers > 0 + ); + expect(hasScannedContainers).toBe(true); + + // Ensure at least three controller exists and all are connected + await page.getByRole('tab', { name: 'Controllers' }).click(); + await page.waitForLoadState("domcontentloaded"); + const controllerPromise = page.waitForResponse(`${url}/controller`); + await page.getByLabel('Controllers').getByRole('button', { name: 'refresh Refresh' }).click(); + const controllerResponse = await controllerPromise; + const controllerData = await controllerResponse.json(); + + expect(controllerData).toHaveProperty('controllers'); + expect(Array.isArray(controllerData.controllers)).toBe(true); + expect(controllerData.controllers.length).toBeGreaterThanOrEqual(3); + controllerData.controllers.forEach((controller: { connection_state: string }) => { + expect(controller.connection_state).toBe('connected'); + }); + + // Ensure at least one enforcer exists and all are connected + await page.getByRole('tab', { name: 'Enforcers' }).click(); + await page.waitForLoadState("domcontentloaded"); + const enforcerPromise = page.waitForResponse(`${url}/enforcer`); + await page.getByLabel('Enforcers').getByRole('button', { name: 'refresh Refresh' }).click(); + const enforcerResponse = await enforcerPromise; + const enforcerData = await enforcerResponse.json(); + + expect(enforcerData).toHaveProperty('enforcers'); + expect(Array.isArray(enforcerData.enforcers)).toBe(true); + expect(enforcerData.enforcers.length).toBeGreaterThanOrEqual(1); + enforcerData.enforcers.forEach((enforcer: { connection_state: string }) => { + expect(enforcer.connection_state).toBe('connected'); + }); + }); +}); + +test("validate local login is blocked", async ({ page }) => { + await test.step("check local login", async () => { + await page.goto('/#/login'); + await page.locator('.mat-checkbox-inner-container').click(); + await page.locator('#Email1').fill('admin'); + await page.locator('#password1').fill('admin'); + await page.getByRole('button', { name: 'Login', exact: true }).click(); + await expect(page.getByText('RBAC: access denied')).toBeVisible(); + }); +});