Skip to content

Feature/lab7#3

Open
raaller wants to merge 5 commits into
mainfrom
feature/lab7
Open

Feature/lab7#3
raaller wants to merge 5 commits into
mainfrom
feature/lab7

Conversation

@raaller

@raaller raaller commented Jul 2, 2026

Copy link
Copy Markdown
Owner

Goal

Scan the OWASP Juice Shop v20.0.0 container image with Trivy, harden its Kubernetes deployment according to the Pod Security Standards restricted profile, restrict network access with a NetworkPolicy, and implement a Conftest policy that rejects insecure workloads before deployment.

Changes

  • submissions/lab7.md

    • Trivy image vulnerability severity breakdown
    • Top ten vulnerabilities with available fixes
    • Trivy and Grype comparison
    • Dockerfile misconfiguration scan analysis
    • Kubernetes hardening evidence
    • Trivy Kubernetes scan results
    • readOnlyRootFilesystem failure analysis and remediation
    • Conftest pass/fail evidence
    • CI-time policy enforcement discussion
  • labs/lab7/k8s/namespace.yaml

    • Creates the juice-shop namespace
    • Enables Pod Security Admission with:
      • enforce: restricted
      • warn: restricted
      • audit: restricted
  • labs/lab7/k8s/serviceaccount.yaml

    • Adds a dedicated juice-shop-sa ServiceAccount
    • Disables automatic ServiceAccount token mounting
  • labs/lab7/k8s/deployment.yaml

    • Pins Juice Shop v20.0.0 by image digest
    • Uses a non-default ServiceAccount
    • Disables ServiceAccount token mounting
    • Enforces non-root execution
    • Uses the RuntimeDefault seccomp profile
    • Drops all Linux capabilities
    • Disables privilege escalation
    • Enables a read-only root filesystem
    • Defines CPU and memory requests and limits
    • Uses writable emptyDir volumes for runtime data
    • Uses an init container to populate writable directories before application startup
  • labs/lab7/k8s/networkpolicy.yaml

    • Applies ingress and egress restrictions to app=juice-shop
    • Allows application traffic on TCP port 3000
    • Allows DNS access to kube-system on UDP port 53
    • Allows outbound HTTPS on TCP port 443
    • Denies other traffic implicitly
  • labs/lab7/policies/pod-hardening.rego

    • Rejects Deployments missing runAsNonRoot: true
    • Rejects containers missing readOnlyRootFilesystem: true
    • Rejects containers missing allowPrivilegeEscalation: false
    • Rejects containers that do not drop the ALL capability set

Testing

Trivy image scan

trivy image bkimminich/juice-shop:v20.0.0 \
  --severity HIGH,CRITICAL \
  --format json \
  --output labs/lab7/results/trivy-image.json

Results:

CRITICAL: 5
HIGH:     43
Total:    48

With an available fix:    46
Without an available fix: 2

Dockerfile misconfiguration scan

trivy config labs/lab7/results \
  --misconfig-scanners dockerfile \
  --file-patterns 'dockerfile:.*Dockerfile-bad$' \
  --severity HIGH,CRITICAL \
  --format table

Results:

Tests:     20
Passed:    19
Failed:    1

HIGH:      1
CRITICAL:  0

Detected rule:

DS-0002: Last USER command in Dockerfile should not be 'root'

Conftest on the hardened Deployment

conftest test \
  labs/lab7/k8s/deployment.yaml \
  --policy labs/lab7/policies

Results:

4 tests, 4 passed, 0 warnings, 0 failures, 0 exceptions

Conftest on an intentionally insecure Deployment

conftest test \
  labs/lab7/results/bad-deployment.yaml \
  --policy labs/lab7/policies

Results:

4 tests, 0 passed, 0 warnings, 4 failures, 0 exceptions

The policy correctly rejected the insecure Deployment for:

Missing runAsNonRoot: true
Missing readOnlyRootFilesystem: true
Missing allowPrivilegeEscalation: false
Missing capabilities.drop: ["ALL"]

Kubernetes deployment

kubectl apply -f labs/lab7/k8s/

kubectl -n juice-shop rollout status \
  deployment/juice-shop \
  --timeout=300s

kubectl -n juice-shop get pods \
  -l app=juice-shop

Results:

deployment "juice-shop" successfully rolled out

READY:     1/1
STATUS:    Running
RESTARTS:  0

Pod Security Admission profile:

restricted

Trivy Kubernetes scan

trivy k8s \
  --include-namespaces juice-shop \
  --severity HIGH,CRITICAL \
  --format json \
  --output labs/lab7/results/trivy-k8s-final.json

Aggregated workload results:

CRITICAL vulnerabilities: 10
HIGH vulnerabilities:     86

HIGH/CRITICAL misconfigurations: 0

The Kubernetes vulnerability totals are doubled because the same pinned Juice Shop image is used by both the init container and the main application container:

Per image:
CRITICAL: 5
HIGH:     43

Two containers:
CRITICAL: 10
HIGH:     86

Runtime issue and remediation

Enabling readOnlyRootFilesystem: true initially caused Juice Shop to enter CrashLoopBackOff.

Mounting empty emptyDir volumes directly over application directories hid the original files shipped inside the image, including files required during startup.

The final Deployment uses an init container to:

  1. read the original files from the pinned Juice Shop image;
  2. copy them into writable emptyDir volumes;
  3. mount the populated volumes into the main application container.

This keeps the main container root filesystem read-only while allowing Juice Shop to start successfully.

Artifacts

  • Submission report: submissions/lab7.md
  • Kubernetes manifests:
    • labs/lab7/k8s/namespace.yaml
    • labs/lab7/k8s/serviceaccount.yaml
    • labs/lab7/k8s/deployment.yaml
    • labs/lab7/k8s/networkpolicy.yaml
  • Conftest policy:
    • labs/lab7/policies/pod-hardening.rego

Scanner-generated output under labs/lab7/results/ is intentionally not committed.

Checklist

  • Title follows the feat(labN): <topic> convention
  • No generated scanner output committed
  • Submission report exists at submissions/lab7.md
  • Task 1 — Trivy image + config scans + Grype comparison
  • Task 2 — Hardened K8s deployment with PSS restricted + NetworkPolicy
  • Bonus — Conftest policy passing on hardened + failing on bad manifest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant