From de3c754d0101310cee570849c4767e5c397b28ef Mon Sep 17 00:00:00 2001 From: pepega Date: Sun, 8 Feb 2026 14:57:54 +0300 Subject: [PATCH 1/4] docs: add PR template --- .github/pull_request_template.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..096aca87 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Goal + + +## Changes + +- +- +- + +## Testing + + +## Artifacts & Screenshots + + +## Checklist +- [ ] Clear, descriptive PR title +- [ ] Documentation updated if needed +- [ ] No secrets, API keys, or large temporary files committed From 4eb4112137b5a6e3824bc1fbaecd4a13cf2de9ba Mon Sep 17 00:00:00 2001 From: pepegx Date: Mon, 23 Mar 2026 19:27:32 +0300 Subject: [PATCH 2/4] docs: add lab7 submission - container security analysis --- labs/lab7/analysis/deployment-comparison.txt | 51 + .../docker-bench-results-adapted.txt | 1 + .../docker-bench-results-rehosted.txt | 323 +++++ .../docker-bench-results-stock-failure.txt | 4 + .../docker-bench-results-workaround.txt | 1 + labs/lab7/hardening/docker-bench-results.txt | 323 +++++ .../docker-bench-src/docker-bench-security.sh | 152 ++ .../docker-bench-security.sh.log | 304 ++++ .../docker-bench-security.sh.log.json | 125 ++ .../docker-bench-src/functions_lib.sh | 170 +++ .../hardening/docker-bench-src/helper_lib.sh | 122 ++ .../hardening/docker-bench-src/output_lib.sh | 81 ++ .../tests/1_host_configuration.sh | 418 ++++++ .../tests/2_docker_daemon_configuration.sh | 465 ++++++ .../3_docker_daemon_configuration_files.sh | 612 ++++++++ .../tests/4_container_images.sh | 278 ++++ .../tests/5_container_runtime.sh | 1276 +++++++++++++++++ .../tests/6_docker_security_operations.sh | 64 + .../tests/7_docker_swarm_configuration.sh | 250 ++++ .../tests/99_community_checks.sh | 26 + labs/lab7/scanning/dockle-results.txt | 9 + labs/lab7/scanning/scout-cves.txt | 1179 +++++++++++++++ labs/lab7/scanning/snyk-results-amd64.txt | 80 ++ labs/lab7/scanning/snyk-results.txt | 80 ++ labs/submission7.md | 550 +++++++ 25 files changed, 6944 insertions(+) create mode 100644 labs/lab7/analysis/deployment-comparison.txt create mode 100644 labs/lab7/hardening/docker-bench-results-adapted.txt create mode 100644 labs/lab7/hardening/docker-bench-results-rehosted.txt create mode 100644 labs/lab7/hardening/docker-bench-results-stock-failure.txt create mode 100644 labs/lab7/hardening/docker-bench-results-workaround.txt create mode 100644 labs/lab7/hardening/docker-bench-results.txt create mode 100755 labs/lab7/hardening/docker-bench-src/docker-bench-security.sh create mode 100644 labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log create mode 100644 labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json create mode 100644 labs/lab7/hardening/docker-bench-src/functions_lib.sh create mode 100644 labs/lab7/hardening/docker-bench-src/helper_lib.sh create mode 100644 labs/lab7/hardening/docker-bench-src/output_lib.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/1_host_configuration.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/2_docker_daemon_configuration.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/3_docker_daemon_configuration_files.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/4_container_images.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/5_container_runtime.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/6_docker_security_operations.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/7_docker_swarm_configuration.sh create mode 100644 labs/lab7/hardening/docker-bench-src/tests/99_community_checks.sh create mode 100644 labs/lab7/scanning/dockle-results.txt create mode 100644 labs/lab7/scanning/scout-cves.txt create mode 100644 labs/lab7/scanning/snyk-results-amd64.txt create mode 100644 labs/lab7/scanning/snyk-results.txt create mode 100644 labs/submission7.md diff --git a/labs/lab7/analysis/deployment-comparison.txt b/labs/lab7/analysis/deployment-comparison.txt new file mode 100644 index 00000000..d413f52f --- /dev/null +++ b/labs/lab7/analysis/deployment-comparison.txt @@ -0,0 +1,51 @@ +=== Functionality Test === +Default: HTTP 200 +Hardened: HTTP 200 +Production: HTTP 200 + +=== Resource Usage === +NAME CPU % MEM USAGE / LIMIT MEM % +juice-default 0.64% 156.6MiB / 5.786GiB 2.64% +juice-hardened 0.63% 105.8MiB / 512MiB 20.66% +juice-production 0.59% 92.55MiB / 512MiB 18.08% + +=== Security Configurations === + +Container: juice-default +User: "65532" +CapDrop: null +CapAdd: null +SecurityOpt: null +Memory: 0 +MemorySwap: 0 +NanoCpus: 0 +CpuQuota: 0 +PIDs: +Restart: no:0 +ReadonlyRootfs: false + +Container: juice-hardened +User: "65532" +CapDrop: ["ALL"] +CapAdd: null +SecurityOpt: ["no-new-privileges"] +Memory: 536870912 +MemorySwap: 1073741824 +NanoCpus: 1000000000 +CpuQuota: 0 +PIDs: +Restart: no:0 +ReadonlyRootfs: false + +Container: juice-production +User: "65532" +CapDrop: ["ALL"] +CapAdd: ["CAP_NET_BIND_SERVICE"] +SecurityOpt: ["no-new-privileges","seccomp=builtin"] +Memory: 536870912 +MemorySwap: 536870912 +NanoCpus: 1000000000 +CpuQuota: 0 +PIDs: 100 +Restart: on-failure:3 +ReadonlyRootfs: false diff --git a/labs/lab7/hardening/docker-bench-results-adapted.txt b/labs/lab7/hardening/docker-bench-results-adapted.txt new file mode 100644 index 00000000..15b48611 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-results-adapted.txt @@ -0,0 +1 @@ +Error connecting to docker daemon (does docker ps work?) diff --git a/labs/lab7/hardening/docker-bench-results-rehosted.txt b/labs/lab7/hardening/docker-bench-results-rehosted.txt new file mode 100644 index 00000000..bb3ccb5f --- /dev/null +++ b/labs/lab7/hardening/docker-bench-results-rehosted.txt @@ -0,0 +1,323 @@ +# ------------------------------------------------------------------------------ +# Docker Bench for Security v1.3.4 +# +# Docker, Inc. (c) 2015- +# +# Checks for dozens of common best-practices around deploying Docker containers in production. +# Inspired by the CIS Docker Community Edition Benchmark v1.1.0. +# ------------------------------------------------------------------------------ + +Initializing Mon Mar 23 16:12:56 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: elegant_sammet +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: elegant_sammet +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to elegant_sammet +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[WARN] 5.9 - Ensure the host's network namespace is not shared +[WARN] * Container running with networking mode 'host': elegant_sammet +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: elegant_sammet +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: elegant_sammet +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: elegant_sammet +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: elegant_sammet +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[WARN] 5.15 - Ensure the host's process namespace is not shared +[WARN] * Host PID namespace being shared with: elegant_sammet +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: elegant_sammet +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: elegant_sammet +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: elegant_sammet +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[WARN] 5.30 - Ensure the host's user namespaces is not shared +[WARN] * Namespace shared: elegant_sammet +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: elegant_sammet +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 28 images +[INFO] * Only 12 out of 28 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) + +[INFO] Checks: 105 +[INFO] Score: -3 diff --git a/labs/lab7/hardening/docker-bench-results-stock-failure.txt b/labs/lab7/hardening/docker-bench-results-stock-failure.txt new file mode 100644 index 00000000..1ce58a0a --- /dev/null +++ b/labs/lab7/hardening/docker-bench-results-stock-failure.txt @@ -0,0 +1,4 @@ +WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested +docker: Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/var/lib/docker/containers/5b59aaecdec190bd0c6112b69f971a288ef909b089147332a6cb6c0d9f5f80b3/hostname" to rootfs at "/etc/hostname": create mountpoint for /etc/hostname mount: make mountpoint "/etc/hostname": read-only file system + +Run 'docker run --help' for more information diff --git a/labs/lab7/hardening/docker-bench-results-workaround.txt b/labs/lab7/hardening/docker-bench-results-workaround.txt new file mode 100644 index 00000000..15b48611 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-results-workaround.txt @@ -0,0 +1 @@ +Error connecting to docker daemon (does docker ps work?) diff --git a/labs/lab7/hardening/docker-bench-results.txt b/labs/lab7/hardening/docker-bench-results.txt new file mode 100644 index 00000000..bb3ccb5f --- /dev/null +++ b/labs/lab7/hardening/docker-bench-results.txt @@ -0,0 +1,323 @@ +# ------------------------------------------------------------------------------ +# Docker Bench for Security v1.3.4 +# +# Docker, Inc. (c) 2015- +# +# Checks for dozens of common best-practices around deploying Docker containers in production. +# Inspired by the CIS Docker Community Edition Benchmark v1.1.0. +# ------------------------------------------------------------------------------ + +Initializing Mon Mar 23 16:12:56 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: elegant_sammet +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: elegant_sammet +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to elegant_sammet +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[WARN] 5.9 - Ensure the host's network namespace is not shared +[WARN] * Container running with networking mode 'host': elegant_sammet +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: elegant_sammet +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: elegant_sammet +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: elegant_sammet +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: elegant_sammet +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[WARN] 5.15 - Ensure the host's process namespace is not shared +[WARN] * Host PID namespace being shared with: elegant_sammet +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: elegant_sammet +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: elegant_sammet +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: elegant_sammet +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +sh: : out of range +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[WARN] 5.30 - Ensure the host's user namespaces is not shared +[WARN] * Namespace shared: elegant_sammet +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: elegant_sammet +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 28 images +[INFO] * Only 12 out of 28 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) + +[INFO] Checks: 105 +[INFO] Score: -3 diff --git a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh new file mode 100755 index 00000000..2e7d0e69 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh @@ -0,0 +1,152 @@ +#!/bin/sh +# ------------------------------------------------------------------------------ +# Docker Bench for Security +# +# Docker, Inc. (c) 2015- +# +# Checks for dozens of common best-practices around deploying Docker containers in production. +# ------------------------------------------------------------------------------ + +version='1.3.4' + +# Load dependencies +. ./functions_lib.sh +. ./helper_lib.sh + +# Setup the paths +this_path=$(abspath "$0") ## Path of this file including filename +myname=$(basename "${this_path}") ## file name of this script. + +readonly version +readonly this_path +readonly myname + +export PATH=$PATH:/bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin/ + +# Check for required program(s) +req_progs='awk docker grep ss stat' +for p in $req_progs; do + command -v "$p" >/dev/null 2>&1 || { printf "%s command not found.\n" "$p"; exit 1; } +done + +# Ensure we can connect to docker daemon +if ! docker ps -q >/dev/null 2>&1; then + printf "Error connecting to docker daemon (does docker ps work?)\n" + exit 1 +fi + +usage () { + cat </dev/null 2>&1; then + benchcont="$c" + fi + done + + if [ -n "$include" ]; then + pattern=$(echo "$include" | sed 's/,/|/g') + containers=$(docker ps | sed '1d' | awk '{print $NF}' | grep -v "$benchcont" | grep -E "$pattern") + elif [ -n "$exclude" ]; then + pattern=$(echo "$exclude" | sed 's/,/|/g') + containers=$(docker ps | sed '1d' | awk '{print $NF}' | grep -v "$benchcont" | grep -Ev "$pattern") + else + containers=$(docker ps | sed '1d' | awk '{print $NF}' | grep -v "$benchcont") + fi + + if [ -z "$containers" ]; then + running_containers=0 + else + running_containers=1 + fi + + for test in tests/*.sh; do + . ./"$test" + done + + if [ -z "$check" ] && [ ! "$checkexclude" ]; then + cis + elif [ -z "$check" ] && [ "$checkexclude" ]; then + checkexcluded="$(echo ",$checkexclude" | sed -e 's/^/\^/g' -e 's/,/\$|/g' -e 's/$/\$/g')" + for c in $(grep 'check_[0-9]' functions_lib.sh | grep -vE "$checkexcluded"); do + "$c" + done + else + for i in $(echo "$check" | sed "s/,/ /g"); do + if command -v "$i" 2>/dev/null 1>&2; then + "$i" + else + echo "Check \"$i\" doesn't seem to exist." + continue + fi + done + fi + + printf "\n" + info "Checks: $totalChecks" + info "Score: $currentScore" + + endjson "$totalChecks" "$currentScore" "$(date +%s)" +} + +main "$@" diff --git a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log new file mode 100644 index 00000000..b9c8be08 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log @@ -0,0 +1,304 @@ +Initializing Mon Mar 23 16:12:56 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: elegant_sammet +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: elegant_sammet +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to elegant_sammet +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[WARN] 5.9 - Ensure the host's network namespace is not shared +[WARN] * Container running with networking mode 'host': elegant_sammet +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: elegant_sammet +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: elegant_sammet +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: elegant_sammet +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: elegant_sammet +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[WARN] 5.15 - Ensure the host's process namespace is not shared +[WARN] * Host PID namespace being shared with: elegant_sammet +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: elegant_sammet +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: elegant_sammet +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: elegant_sammet +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[WARN] 5.30 - Ensure the host's user namespaces is not shared +[WARN] * Namespace shared: elegant_sammet +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: elegant_sammet +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 28 images +[INFO] * Only 12 out of 28 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) +[INFO] Checks: 105 +[INFO] Score: -3 diff --git a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json new file mode 100644 index 00000000..18730fea --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json @@ -0,0 +1,125 @@ +{ + "dockerbenchsecurity": "1.3.4", + "start": 1774282376, + "tests": [ + {"id": "1", "desc": "Host Configuration", "results": [ + {"id": "1.1", "desc": "Ensure a separate partition for containers has been created", "result": "WARN"}, + {"id": "1.2", "desc": "Ensure the container host has been Hardened", "result": "INFO"}, + {"id": "1.3", "desc": "Ensure Docker is up to date", "result": "PASS", "details": "Using 29.2.1"}, + {"id": "1.4", "desc": "Ensure only trusted users are allowed to control Docker daemon", "result": "INFO", "details": "users: ", "items": []}, + {"id": "1.5", "desc": "Ensure auditing is configured for the Docker daemon", "result": "WARN"}, + {"id": "1.6", "desc": "Ensure auditing is configured for Docker files and directories - /var/lib/docker", "result": "INFO", "details": "Directory not found"}, + {"id": "1.7", "desc": "Ensure auditing is configured for Docker files and directories - /etc/docker", "result": "INFO", "details": "Directory not found"}, + {"id": "1.8", "desc": "Ensure auditing is configured for Docker files and directories - docker.service", "result": "INFO", "details": "File not found"}, + {"id": "1.9", "desc": "Ensure auditing is configured for Docker files and directories - docker.socket", "result": "INFO", "details": "File not found"}, + {"id": "1.10", "desc": "Ensure auditing is configured for Docker files and directories - /etc/default/docker", "result": "INFO", "details": "File not found"}, + {"id": "1.11", "desc": "Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json", "result": "INFO", "details": "File not found"}, + {"id": "1.12", "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd", "result": "INFO", "details": "File not found"}, + {"id": "1.13", "desc": "Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc", "result": "INFO", "details": "File not found"} + ]}, + {"id": "2", "desc": "Docker daemon configuration", "results": [ + {"id": "2.1", "desc": "Ensure network traffic is restricted between containers on the default bridge", "result": "WARN"}, + {"id": "2.2", "desc": "Ensure the logging level is set to 'info'", "result": "PASS"}, + {"id": "2.3", "desc": "Ensure Docker is allowed to make changes to iptables", "result": "PASS"}, + {"id": "2.4", "desc": "Ensure insecure registries are not used", "result": "PASS"}, + {"id": "2.5", "desc": "Ensure aufs storage driver is not used", "result": "PASS"}, + {"id": "2.6", "desc": "Ensure TLS authentication for Docker daemon is configured", "result": "INFO", "details": "Docker daemon not listening on TCP"}, + {"id": "2.7", "desc": "Ensure the default ulimit is configured appropriately", "result": "INFO", "details": "Default ulimit doesn't appear to be set"}, + {"id": "2.8", "desc": "Enable user namespace support", "result": "WARN"}, + {"id": "2.9", "desc": "Ensure the default cgroup usage has been confirmed", "result": "PASS"}, + {"id": "2.10", "desc": "Ensure base device size is not changed until needed", "result": "PASS"}, + {"id": "2.11", "desc": "Ensure that authorization for Docker client commands is enabled", "result": "WARN"}, + {"id": "2.12", "desc": "Ensure centralized and remote logging is configured", "result": "WARN"}, + {"id": "2.13", "desc": "Ensure operations on legacy registry (v1) are Disabled", "result": "INFO"}, + {"id": "2.14", "desc": "Ensure live restore is Enabled", "result": "WARN"}, + {"id": "2.15", "desc": "Ensure Userland Proxy is Disabled", "result": "WARN"}, + {"id": "2.16", "desc": "Ensure daemon-wide custom seccomp profile is applied, if needed", "result": "INFO"}, + {"id": "2.17", "desc": "Ensure experimental features are avoided in production", "result": "PASS"}, + {"id": "2.18", "desc": "Ensure containers are restricted from acquiring new privileges", "result": "WARN"} + ]}, + {"id": "3", "desc": "Docker daemon configuration files", "results": [ + {"id": "3.1", "desc": "Ensure that docker.service file ownership is set to root:root", "result": "INFO", "details": "File not found"}, + {"id": "3.2", "desc": "Ensure that docker.service file permissions are set to 644 or more restrictive", "result": "INFO", "details": "File not found"}, + {"id": "3.3", "desc": "Ensure that docker.socket file ownership is set to root:root", "result": "INFO", "details": "File not found"}, + {"id": "3.4", "desc": "Ensure that docker.socket file permissions are set to 644 or more restrictive", "result": "INFO", "details": "File not found"}, + {"id": "3.5", "desc": "Ensure that /etc/docker directory ownership is set to root:root", "result": "INFO", "details": "Directory not found"}, + {"id": "3.6", "desc": "Ensure that /etc/docker directory permissions are set to 755 or more restrictive", "result": "INFO", "details": "Directory not found"}, + {"id": "3.7", "desc": "Ensure that registry certificate file ownership is set to root:root", "result": "INFO", "details": "Directory not found"}, + {"id": "3.8", "desc": "Ensure that registry certificate file permissions are set to 444 or more restrictive", "result": "INFO", "details": "Directory not found"}, + {"id": "3.9", "desc": "Ensure that TLS CA certificate file ownership is set to root:root", "result": "INFO", "details": "No TLS CA certificate found"}, + {"id": "3.10", "desc": "Ensure that TLS CA certificate file permissions are set to 444 or more restrictive", "result": "INFO", "details": "No TLS CA certificate found"}, + {"id": "3.11", "desc": "Ensure that Docker server certificate file ownership is set to root:root", "result": "INFO", "details": "No TLS Server certificate found"}, + {"id": "3.12", "desc": "Ensure that Docker server certificate file permissions are set to 444 or more restrictive", "result": "INFO", "details": "No TLS Server certificate found"}, + {"id": "3.13", "desc": "Ensure that Docker server certificate key file ownership is set to root:root", "result": "INFO", "details": "No TLS Key found"}, + {"id": "3.14", "desc": "Ensure that Docker server certificate key file permissions are set to 400", "result": "INFO", "details": "No TLS Key found"}, + {"id": "3.15", "desc": "Ensure that Docker socket file ownership is set to root:docker", "result": "WARN", "details": "Wrong ownership for /var/run/docker.sock"}, + {"id": "3.16", "desc": "Ensure that Docker socket file permissions are set to 660 or more restrictive", "result": "PASS"}, + {"id": "3.17", "desc": "Ensure that daemon.json file ownership is set to root:root", "result": "INFO", "details": "File not found"}, + {"id": "3.18", "desc": "Ensure that daemon.json file permissions are set to 644 or more restrictive", "result": "INFO", "details": "File not found"}, + {"id": "3.19", "desc": "Ensure that /etc/default/docker file ownership is set to root:root", "result": "INFO", "details": "File not found"}, + {"id": "3.20", "desc": "Ensure that /etc/default/docker file permissions are set to 644 or more restrictive", "result": "INFO", "details": "File not found"} + ]}, + {"id": "4", "desc": "Container Images and Build File", "results": [ + {"id": "4.1", "desc": "Ensure a user for the container has been created", "result": "WARN", "details": "running as root: elegant_sammet devops-lab9-control-plane devops-lab9-worker promtail", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","promtail"]}, + {"id": "4.2", "desc": "Ensure that containers use trusted base images", "result": "NOTE"}, + {"id": "4.3", "desc": "Ensure unnecessary packages are not installed in the container", "result": "NOTE"}, + {"id": "4.4", "desc": "Ensure images are scanned and rebuilt to include security patches", "result": "NOTE"}, + {"id": "4.5", "desc": "Ensure Content trust for Docker is Enabled", "result": "WARN"}, + {"id": "4.6", "desc": "Ensure HEALTHCHECK instructions have been added to the container image", "result": "WARN", "details": "Images w/o HEALTHCHECK: [pepegx/devops-info-service:lab02] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-go:lab02] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-service:lab07] [devops-info-service-go:lab07] [host.docker.internal:5001/devops-info-service:latest] [host.docker.internal:5001/devops-info-service-go:latest] [bridgecrew/checkov:latest] [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [checkmarx/kics:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [prom/prometheus:v3.9.0] [grafana/grafana:12.3.1] [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] [bkimminich/juice-shop:v19.0.0] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [tenable/terrascan:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2]", "items": ["[pepegx/devops-info-service:lab02]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-go:lab02]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-service:lab07]","[devops-info-service-go:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[host.docker.internal:5001/devops-info-service-go:latest]","[bridgecrew/checkov:latest]","[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[checkmarx/kics:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[prom/prometheus:v3.9.0]","[grafana/grafana:12.3.1]","[kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48]","[bkimminich/juice-shop:v19.0.0]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[tenable/terrascan:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]"]}, + {"id": "4.7", "desc": "Ensure update instructions are not use alone in the Dockerfile", "result": "INFO", "details": "Update instructions found: [pepegx/devops-info-service:lab02] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab07] [host.docker.internal:5001/devops-info-service:latest] [bridgecrew/checkov:latest] [checkmarx/kics:latest] [ghcr.io/sullo/nikto:latest] [sh1co/wikifet_arm:latest] [grafana/promtail:3.0.0]", "items": ["[pepegx/devops-info-service:lab02]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[bridgecrew/checkov:latest]","[checkmarx/kics:latest]","[ghcr.io/sullo/nikto:latest]","[sh1co/wikifet_arm:latest]","[grafana/promtail:3.0.0]"]}, + {"id": "4.8", "desc": "Ensure setuid and setgid permissions are removed in the images", "result": "NOTE"}, + {"id": "4.9", "desc": "Ensure COPY is used instead of ADD in Dockerfile", "result": "INFO", "details": "Images using ADD: [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [grafana/grafana:12.3.1] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2] [docker/docker-bench-security:latest]", "items": ["[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[grafana/grafana:12.3.1]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]","[docker/docker-bench-security:latest]"]}, + {"id": "4.10", "desc": "Ensure secrets are not stored in Dockerfiles", "result": "NOTE"}, + {"id": "4.11", "desc": "Ensure verified packages are only Installed", "result": "NOTE"} + ]}, + {"id": "5", "desc": "Container Runtime", "results": [ + {"id": "5.1", "desc": "Ensure AppArmor Profile is Enabled", "result": "WARN", "details": "Containers with no AppArmorProfile: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.2", "desc": "Ensure SELinux security options are set, if applicable", "result": "WARN", "details": "Containers with no SecurityOptions: prometheus grafana promtail devops-go devops-python loki", "items": ["prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.3", "desc": "Ensure Linux Kernel Capabilities are restricted within containers", "result": "WARN", "details": "Capabilities added for containers: elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.4", "desc": "Ensure privileged containers are not used", "result": "WARN", "details": "Containers running in privileged mode: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.5", "desc": "Ensure sensitive host system directories are not mounted on containers", "result": "PASS"}, + {"id": "5.6", "desc": "Ensure ssh is not run within containers", "result": "PASS"}, + {"id": "5.7", "desc": "Ensure privileged ports are not mapped within containers", "result": "WARN", "details": "Containers using privileged ports: devops-lab9-control-plane:80 devops-lab9-control-plane:443", "items": ["devops-lab9-control-plane:80","devops-lab9-control-plane:443"]}, + {"id": "5.8", "desc": "Ensure only needed ports are open on the container", "result": "NOTE"}, + {"id": "5.9", "desc": "Ensure the host's network namespace is not shared", "result": "WARN", "details": "Containers running with networking mode 'host': elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.10", "desc": "Ensure memory usage for container is limited", "result": "WARN", "details": "Container running without memory restrictions: elegant_sammet devops-lab9-control-plane devops-lab9-worker", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.11", "desc": "Ensure CPU priority is set appropriately on the container", "result": "WARN", "details": "Containers running without CPU restrictions: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.12", "desc": "Ensure the container's root filesystem is mounted as read only", "result": "WARN", "details": "Containers running with root FS mounted R/W: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.13", "desc": "Ensure incoming container traffic is binded to a specific host interface", "result": "WARN", "details": "Containers with port bound to wildcard IP: devops-lab9-control-plane:0.0.0.0 devops-lab9-control-plane:0.0.0.0 prometheus:0.0.0.0 grafana:0.0.0.0 promtail:0.0.0.0 devops-go:0.0.0.0 devops-python:0.0.0.0 loki:0.0.0.0", "items": ["devops-lab9-control-plane:0.0.0.0","devops-lab9-control-plane:0.0.0.0","prometheus:0.0.0.0","grafana:0.0.0.0","promtail:0.0.0.0","devops-go:0.0.0.0","devops-python:0.0.0.0","loki:0.0.0.0"]}, + {"id": "5.14", "desc": "Ensure 'on-failure' container restart policy is set to '5'", "result": "WARN", "details": "Containers with MaximumRetryCount not set to 5: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.15", "desc": "Ensure the host's process namespace is not shared", "result": "WARN", "details": "Containers sharing host PID namespace: elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.16", "desc": "Ensure the host's IPC namespace is not shared", "result": "PASS"}, + {"id": "5.17", "desc": "Ensure host devices are not directly exposed to containers", "result": "PASS"}, + {"id": "5.18", "desc": "Ensure the default ulimit is overwritten at runtime, only if needed", "result": "INFO", "details": "Containers with no default ulimit override: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.19", "desc": "Ensure mount propagation mode is not set to shared", "result": "PASS"}, + {"id": "5.20", "desc": "Ensure the host's UTS namespace is not shared", "result": "PASS"}, + {"id": "5.21", "desc": "Ensure the default seccomp profile is not Disabled", "result": "WARN", "details": "Containers with default seccomp profile disabled: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.22", "desc": "Ensure docker exec commands are not used with privileged option", "result": "NOTE"}, + {"id": "5.23", "desc": "Ensure docker exec commands are not used with user option", "result": "NOTE"}, + {"id": "5.24", "desc": "Ensure cgroup usage is confirmed", "result": "PASS"}, + {"id": "5.25", "desc": "Ensure the container is restricted from acquiring additional privileges", "result": "WARN", "details": "Containers without restricted privileges: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.26", "desc": "Ensure container health is checked at runtime", "result": "WARN", "details": "Containers without health check: elegant_sammet devops-lab9-control-plane devops-lab9-worker", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.27", "desc": "Ensure docker commands always get the latest version of the image", "result": "INFO"}, + {"id": "5.28", "desc": "Ensure PIDs cgroup limit is used", "result": "PASS"}, + {"id": "5.29", "desc": "Ensure Docker's default bridge docker0 is not used", "result": "PASS"}, + {"id": "5.30", "desc": "Ensure the host's user namespaces is not shared", "result": "WARN", "details": "Containers sharing host user namespace: elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.31", "desc": "Ensure the Docker socket is not mounted inside any containers", "result": "WARN", "details": "Containers sharing docker socket: elegant_sammet promtail", "items": ["elegant_sammet","promtail"]} + ]}, + {"id": "6", "desc": "Docker Security Operations", "results": [ + {"id": "6.1", "desc": "Avoid image sprawl", "result": "INFO", "details": "12 active/28 in use"}, + {"id": "6.2", "desc": "Avoid container sprawl", "result": "INFO", "details": "12 total/9 running"} + ]}, + {"id": "7", "desc": "Docker Swarm Configuration", "results": [ + {"id": "7.1", "desc": "Ensure swarm mode is not Enabled, if not needed", "result": "PASS"}, + {"id": "7.2", "desc": "Ensure the minimum number of manager nodes have been created in a swarm", "result": "PASS"}, + {"id": "7.3", "desc": "Ensure swarm services are binded to a specific host interface", "result": "PASS"}, + {"id": "7.4", "desc": "Ensure data exchanged between containers are encrypted on different nodes on the overlay network", "result": "PASS"}, + {"id": "7.5", "desc": "Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster", "result": "PASS"}, + {"id": "7.6", "desc": "Ensure swarm manager is run in auto-lock mode", "result": "PASS"}, + {"id": "7.7", "desc": "Ensure swarm manager auto-lock key is rotated periodically", "result": "PASS"}, + {"id": "7.8", "desc": "Ensure node certificates are rotated as appropriate", "result": "PASS"}, + {"id": "7.9", "desc": "Ensure CA certificates are rotated as appropriate", "result": "PASS"}, + {"id": "7.10", "desc": "Ensure management plane traffic has been separated from data plane traffic", "result": "PASS"} + ]} + ], "checks": 105, "score": -3, "end": 1774282382 +} diff --git a/labs/lab7/hardening/docker-bench-src/functions_lib.sh b/labs/lab7/hardening/docker-bench-src/functions_lib.sh new file mode 100644 index 00000000..f7c32ba6 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/functions_lib.sh @@ -0,0 +1,170 @@ +#!/bin/sh + +host_configuration() { + check_1 + check_1_1 + check_1_2 + check_1_3 + check_1_4 + check_1_5 + check_1_6 + check_1_7 + check_1_8 + check_1_9 + check_1_10 + check_1_11 + check_1_12 + check_1_13 + check_1_end +} + +docker_daemon_configuration() { + check_2 + check_2_1 + check_2_2 + check_2_3 + check_2_4 + check_2_5 + check_2_6 + check_2_7 + check_2_8 + check_2_9 + check_2_10 + check_2_11 + check_2_12 + check_2_13 + check_2_14 + check_2_15 + check_2_16 + check_2_17 + check_2_18 + check_2_end +} + +docker_daemon_files() { + check_3 + check_3_1 + check_3_2 + check_3_3 + check_3_4 + check_3_5 + check_3_6 + check_3_7 + check_3_8 + check_3_9 + check_3_10 + check_3_11 + check_3_12 + check_3_13 + check_3_14 + check_3_15 + check_3_16 + check_3_17 + check_3_18 + check_3_19 + check_3_20 + check_3_end +} + +container_images() { + check_4 + check_4_1 + check_4_2 + check_4_3 + check_4_4 + check_4_5 + check_4_6 + check_4_7 + check_4_8 + check_4_9 + check_4_10 + check_4_11 + check_4_end +} + +container_runtime() { + check_5 + check_running_containers + check_5_1 + check_5_2 + check_5_3 + check_5_4 + check_5_5 + check_5_6 + check_5_7 + check_5_8 + check_5_9 + check_5_10 + check_5_11 + check_5_12 + check_5_13 + check_5_14 + check_5_15 + check_5_16 + check_5_17 + check_5_18 + check_5_19 + check_5_20 + check_5_21 + check_5_22 + check_5_23 + check_5_24 + check_5_25 + check_5_26 + check_5_27 + check_5_28 + check_5_29 + check_5_30 + check_5_31 + check_5_end +} + +docker_security_operations() { + check_6 + check_6_1 + check_6_2 + check_6_end +} + +docker_swarm_configuration() { + check_7 + check_7_1 + check_7_2 + check_7_3 + check_7_4 + check_7_5 + check_7_6 + check_7_7 + check_7_8 + check_7_9 + check_7_10 + check_7_end +} + +community_checks() { + check_c + check_c_1 + check_c_end +} + +# CIS +cis() { + host_configuration + docker_daemon_configuration + docker_daemon_files + container_images + container_runtime + docker_security_operations + docker_swarm_configuration +} + +# Community contributed +community() { + community_checks +} + +# All +all() { + cis + community +} diff --git a/labs/lab7/hardening/docker-bench-src/helper_lib.sh b/labs/lab7/hardening/docker-bench-src/helper_lib.sh new file mode 100644 index 00000000..37568610 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/helper_lib.sh @@ -0,0 +1,122 @@ +#!/bin/sh + +# Returns the absolute path of a given string +abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; } + +# Audit rules default path +auditrules="/etc/audit/audit.rules" + +# Compares versions of software of the format X.Y.Z +do_version_check() { + [ "$1" = "$2" ] && return 10 + + ver1front=$(printf "%s" "$1" | cut -d "." -f -1) + ver1back=$(printf "%s" "$1" | cut -d "." -f 2-) + ver2front=$(printf "%s" "$2" | cut -d "." -f -1) + ver2back=$(printf "%s" "$2" | cut -d "." -f 2-) + + if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then + [ "$ver1front" -gt "$ver2front" ] && return 11 + [ "$ver1front" -lt "$ver2front" ] && return 9 + + [ "$ver1front" = "$1" ] || [ -z "$ver1back" ] && ver1back=0 + [ "$ver2front" = "$2" ] || [ -z "$ver2back" ] && ver2back=0 + do_version_check "$ver1back" "$ver2back" + return $? + else + [ "$1" -gt "$2" ] && return 11 || return 9 + fi +} + +# Extracts commandline args from the newest running processes named like the first parameter +get_command_line_args() { + PROC="$1" + + for PID in $(pgrep -f -n "$PROC"); do + tr "\0" " " < /proc/"$PID"/cmdline + done +} + +# Extract the cumulative command line arguments for the docker daemon +# +# If specified multiple times, all matches are returned. +# Accounts for long and short variants, call with short option. +# Does not account for option defaults or implicit options. +get_docker_cumulative_command_line_args() { + OPTION="$1" + + if ! get_command_line_args "docker daemon" >/dev/null 2>&1 ; then + line_arg="docker daemon" + else + line_arg="dockerd" + fi + + get_command_line_args "$line_arg" | + # normalize known long options to their short versions + sed \ + -e 's/\-\-debug/-D/g' \ + -e 's/\-\-host/-H/g' \ + -e 's/\-\-log-level/-l/g' \ + -e 's/\-\-version/-v/g' \ + | + # normalize parameters separated by space(s) to -O=VALUE + sed \ + -e 's/\-\([DHlv]\)[= ]\([^- ][^ ]\)/-\1=\2/g' \ + | + # get the last interesting option + tr ' ' "\n" | + grep "^${OPTION}" | + # normalize quoting of values + sed \ + -e 's/"//g' \ + -e "s/'//g" +} + +# Extract the effective command line arguments for the docker daemon +# +# Accounts for multiple specifications, takes the last option. +# Accounts for long and short variants, call with short option +# Does not account for option default or implicit options. +get_docker_effective_command_line_args() { + OPTION="$1" + get_docker_cumulative_command_line_args "$OPTION" | tail -n1 +} + +get_docker_configuration_file_args() { + OPTION="$1" + FILE="$(get_docker_effective_command_line_args '--config-file' | \ + sed 's/.*=//g')" + + if [ -f "$FILE" ]; then + CONFIG_FILE="$FILE" + elif [ -f '/etc/docker/daemon.json' ]; then + CONFIG_FILE='/etc/docker/daemon.json' + else + CONFIG_FILE='/dev/null' + fi + + grep "$OPTION" "$CONFIG_FILE" | sed 's/.*: //g' | tr -d \", +} + +get_systemd_service_file() { + SERVICE="$1" + + if [ -f "/etc/systemd/system/$SERVICE" ]; then + echo "/etc/systemd/system/$SERVICE" + elif systemctl show -p FragmentPath "$SERVICE" 2> /dev/null 1>&2; then + systemctl show -p FragmentPath "$SERVICE" | sed 's/.*=//' + else + echo "/usr/lib/systemd/system/$SERVICE" + fi +} + +yell_info() { +yell "# ------------------------------------------------------------------------------ +# Docker Bench for Security v$version +# +# Docker, Inc. (c) 2015- +# +# Checks for dozens of common best-practices around deploying Docker containers in production. +# Inspired by the CIS Docker Community Edition Benchmark v1.1.0. +# ------------------------------------------------------------------------------" +} diff --git a/labs/lab7/hardening/docker-bench-src/output_lib.sh b/labs/lab7/hardening/docker-bench-src/output_lib.sh new file mode 100644 index 00000000..c0de4e42 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/output_lib.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +if ! [ -z "$nocolor" ] && [ "$nocolor" = "nocolor" ]; then + bldred='' + bldgrn='' + bldblu='' + bldylw='' + txtrst='' +else + bldred='\033[1;31m' + bldgrn='\033[1;32m' + bldblu='\033[1;34m' + bldylw='\033[1;33m' # Yellow + txtrst='\033[0m' +fi + +logit () { + printf "%b\n" "$1" | tee -a "$logger" +} + +info () { + printf "%b\n" "${bldblu}[INFO]${txtrst} $1" | tee -a "$logger" +} + +pass () { + printf "%b\n" "${bldgrn}[PASS]${txtrst} $1" | tee -a "$logger" +} + +warn () { + printf "%b\n" "${bldred}[WARN]${txtrst} $1" | tee -a "$logger" +} + +note () { + printf "%b\n" "${bldylw}[NOTE]${txtrst} $1" | tee -a "$logger" +} + +yell () { + printf "%b\n" "${bldylw}$1${txtrst}\n" +} + +beginjson () { + printf "{\n \"dockerbenchsecurity\": \"%s\",\n \"start\": %s,\n \"tests\": [" "$1" "$2" | tee "$logger.json" 2>/dev/null 1>&2 +} + +endjson (){ + printf "\n ], \"checks\": %s, \"score\": %s, \"end\": %s \n}\n" "$1" "$2" "$3" | tee -a "$logger.json" 2>/dev/null 1>&2 +} + +logjson (){ + printf "\n \"%s\": \"%s\"," "$1" "$2" | tee -a "$logger.json" 2>/dev/null 1>&2 +} + +SSEP= +SEP= +startsectionjson() { + printf "%s\n {\"id\": \"%s\", \"desc\": \"%s\", \"results\": [" "$SSEP" "$1" "$2" | tee -a "$logger.json" 2>/dev/null 1>&2 + SEP= + SSEP="," +} + +endsectionjson() { + printf "\n ]}" | tee -a "$logger.json" 2>/dev/null 1>&2 +} + +starttestjson() { + printf "%s\n {\"id\": \"%s\", \"desc\": \"%s\", " "$SEP" "$1" "$2" | tee -a "$logger.json" 2>/dev/null 1>&2 + SEP="," +} + +resulttestjson() { + if [ $# -eq 1 ]; then + printf "\"result\": \"%s\"}" "$1" | tee -a "$logger.json" 2>/dev/null 1>&2 + elif [ $# -eq 2 ]; then + # Result also contains details + printf "\"result\": \"%s\", \"details\": \"%s\"}" "$1" "$2" | tee -a "$logger.json" 2>/dev/null 1>&2 + else + # Result also includes details and a list of items. Add that directly to details and to an array property "items" + itemsJson=$(printf "["; ISEP=""; for item in $3; do printf "%s\"%s\"" "$ISEP" "$item"; ISEP=","; done; printf "]") + printf "\"result\": \"%s\", \"details\": \"%s: %s\", \"items\": %s}" "$1" "$2" "$3" "$itemsJson" | tee -a "$logger.json" 2>/dev/null 1>&2 + fi +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/1_host_configuration.sh b/labs/lab7/hardening/docker-bench-src/tests/1_host_configuration.sh new file mode 100644 index 00000000..3ecb8442 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/1_host_configuration.sh @@ -0,0 +1,418 @@ +#!/bin/sh + +check_1() { + logit "" + id_1="1" + desc_1="Host Configuration" + check_1="$id_1 - $desc_1" + info "$check_1" + startsectionjson "$id_1" "$desc_1" +} + +# 1.1 +check_1_1() { + id_1_1="1.1" + desc_1_1="Ensure a separate partition for containers has been created" + check_1_1="$id_1_1 - $desc_1_1" + starttestjson "$id_1_1" "$desc_1_1" + + totalChecks=$((totalChecks + 1)) + + if mountpoint -q -- "$(docker info -f '{{ .DockerRootDir }}')" >/dev/null 2>&1; then + pass "$check_1_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_1" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 1.2 +check_1_2() { + id_1_2="1.2" + desc_1_2="Ensure the container host has been Hardened" + check_1_2="$id_1_2 - $desc_1_2" + starttestjson "$id_1_2" "$desc_1_2" + + totalChecks=$((totalChecks + 1)) + note "$check_1_2" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) +} + +# 1.3 +check_1_3() { + id_1_3="1.3" + desc_1_3="Ensure Docker is up to date" + check_1_3="$id_1_3 - $desc_1_3" + starttestjson "$id_1_3" "$desc_1_3" + + totalChecks=$((totalChecks + 1)) + docker_version=$(docker version | grep -i -A2 '^server' | grep ' Version:' \ + | awk '{print $NF; exit}' | tr -d '[:alpha:]-,') + docker_current_version="$(date +%y.%m.0 -d @$(( $(date +%s) - 2592000)))" + do_version_check "$docker_current_version" "$docker_version" + if [ $? -eq 11 ]; then + info "$check_1_3" + info " * Using $docker_version, verify is it up to date as deemed necessary" + info " * Your operating system vendor may provide support and security maintenance for Docker" + resulttestjson "INFO" "Using $docker_version" + currentScore=$((currentScore + 0)) + else + pass "$check_1_3" + info " * Using $docker_version which is current" + info " * Check with your operating system vendor for support and security maintenance for Docker" + resulttestjson "PASS" "Using $docker_version" + currentScore=$((currentScore + 0)) + fi +} + +# 1.4 +check_1_4() { + id_1_4="1.4" + desc_1_4="Ensure only trusted users are allowed to control Docker daemon" + check_1_4="$id_1_4 - $desc_1_4" + starttestjson "$id_1_4" "$desc_1_4" + + totalChecks=$((totalChecks + 1)) + docker_users=$(getent group docker) + info "$check_1_4" + for u in $docker_users; do + info " * $u" + done + resulttestjson "INFO" "users" "$docker_users" + currentScore=$((currentScore + 0)) +} + +# 1.5 +check_1_5() { + id_1_5="1.5" + desc_1_5="Ensure auditing is configured for the Docker daemon" + check_1_5="$id_1_5 - $desc_1_5" + starttestjson "$id_1_5" "$desc_1_5" + + totalChecks=$((totalChecks + 1)) + file="/usr/bin/docker " + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep "$file" >/dev/null 2>&1; then + pass "$check_1_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_5" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_5" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 1.6 +check_1_6() { + id_1_6="1.6" + desc_1_6="Ensure auditing is configured for Docker files and directories - /var/lib/docker" + check_1_6="$id_1_6 - $desc_1_6" + starttestjson "$id_1_6" "$desc_1_6" + + totalChecks=$((totalChecks + 1)) + directory="/var/lib/docker" + if [ -d "$directory" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $directory >/dev/null 2>&1; then + pass "$check_1_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_6" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$directory" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_6" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_6" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.7 +check_1_7() { + id_1_7="1.7" + desc_1_7="Ensure auditing is configured for Docker files and directories - /etc/docker" + check_1_7="$id_1_7 - $desc_1_7" + starttestjson "$id_1_7" "$desc_1_7" + + totalChecks=$((totalChecks + 1)) + directory="/etc/docker" + if [ -d "$directory" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $directory >/dev/null 2>&1; then + pass "$check_1_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_7" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$directory" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_7" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_7" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) +fi +} + +# 1.8 +check_1_8() { + id_1_8="1.8" + desc_1_8="Ensure auditing is configured for Docker files and directories - docker.service" + check_1_8="$id_1_8 - $desc_1_8" + starttestjson "$id_1_8" "$desc_1_8" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.service)" + if [ -f "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep "$file" >/dev/null 2>&1; then + pass "$check_1_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_8" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_8" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_8" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.9 +check_1_9() { + id_1_9="1.9" + desc_1_9="Ensure auditing is configured for Docker files and directories - docker.socket" + check_1_9="$id_1_9 - $desc_1_9" + starttestjson "$id_1_9" "$desc_1_9" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.socket)" + if [ -e "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep "$file" >/dev/null 2>&1; then + pass "$check_1_9" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_9" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_9" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_9" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_9" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.10 +check_1_10() { + id_1_10="1.10" + desc_1_10="Ensure auditing is configured for Docker files and directories - /etc/default/docker" + check_1_10="$id_1_10 - $desc_1_10" + starttestjson "$id_1_10" "$desc_1_10" + + totalChecks=$((totalChecks + 1)) + file="/etc/default/docker" + if [ -f "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $file >/dev/null 2>&1; then + pass "$check_1_10" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_10" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_10" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_10" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_10" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.11 +check_1_11() { + id_1_11="1.11" + desc_1_11="Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json" + check_1_11="$id_1_11 - $desc_1_11" + starttestjson "$id_1_11" "$desc_1_11" + + totalChecks=$((totalChecks + 1)) + file="/etc/docker/daemon.json" + if [ -f "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $file >/dev/null 2>&1; then + pass "$check_1_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_11" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_11" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_11" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.12 +check_1_12() { + id_1_12="1.12" + desc_1_12="Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd" + check_1_12="$id_1_12 - $desc_1_12" + starttestjson "$id_1_12" "$desc_1_12" + + totalChecks=$((totalChecks + 1)) + file="/usr/bin/docker-containerd" + if [ -f "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $file >/dev/null 2>&1; then + pass "$check_1_12" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_12" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_12" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_12" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_12" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 1.13 +check_1_13() { + id_1_13="1.13" + desc_1_13="Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc" + check_1_13="$id_1_13 - $desc_1_13" + starttestjson "$id_1_13" "$desc_1_13" + + totalChecks=$((totalChecks + 1)) + file="/usr/bin/docker-runc" + if [ -f "$file" ]; then + if command -v auditctl >/dev/null 2>&1; then + if auditctl -l | grep $file >/dev/null 2>&1; then + pass "$check_1_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_13" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif grep -s "$file" "$auditrules" | grep "^[^#;]" 2>/dev/null 1>&2; then + pass "$check_1_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_1_13" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + info "$check_1_13" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +check_1_end() { + endsectionjson +} + diff --git a/labs/lab7/hardening/docker-bench-src/tests/2_docker_daemon_configuration.sh b/labs/lab7/hardening/docker-bench-src/tests/2_docker_daemon_configuration.sh new file mode 100644 index 00000000..f4cea69e --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/2_docker_daemon_configuration.sh @@ -0,0 +1,465 @@ +#!/bin/sh + +check_2() { + logit "\n" + id_2="2" + desc_2="Docker daemon configuration" + check_2="$id_2 - $desc_2" + info "$check_2" + startsectionjson "$id_2" "$desc_2" +} + +# 2.1 +check_2_1() { + id_2_1="2.1" + desc_2_1="Ensure network traffic is restricted between containers on the default bridge" + check_2_1="$id_2_1 - $desc_2_1" + starttestjson "$id_2_1" "$desc_2_1" + + totalChecks=$((totalChecks + 1)) + if get_docker_effective_command_line_args '--icc' | grep false >/dev/null 2>&1; then + pass "$check_2_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_configuration_file_args 'icc' | grep "false" >/dev/null 2>&1; then + pass "$check_2_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_1" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 2.2 +check_2_2() { + id_2_2="2.2" + desc_2_2="Ensure the logging level is set to 'info'" + check_2_2="$id_2_2 - $desc_2_2" + starttestjson "$id_2_2" "$desc_2_2" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'log-level' >/dev/null 2>&1; then + if get_docker_configuration_file_args 'log-level' | grep info >/dev/null 2>&1; then + pass "$check_2_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif [ -z "$(get_docker_configuration_file_args 'log-level')" ]; then + pass "$check_2_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_2" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + elif get_docker_effective_command_line_args '-l'; then + if get_docker_effective_command_line_args '-l' | grep "info" >/dev/null 2>&1; then + pass "$check_2_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_2" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + pass "$check_2_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.3 +check_2_3() { + id_2_3="2.3" + desc_2_3="Ensure Docker is allowed to make changes to iptables" + check_2_3="$id_2_3 - $desc_2_3" + starttestjson "$id_2_3" "$desc_2_3" + + totalChecks=$((totalChecks + 1)) + if get_docker_effective_command_line_args '--iptables' | grep "false" >/dev/null 2>&1; then + warn "$check_2_3" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + elif get_docker_configuration_file_args 'iptables' | grep "false" >/dev/null 2>&1; then + warn "$check_2_3" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + else + pass "$check_2_3" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.4 +check_2_4() { + id_2_4="2.4" + desc_2_4="Ensure insecure registries are not used" + check_2_4="$id_2_4 - $desc_2_4" + starttestjson "$id_2_4" "$desc_2_4" + + totalChecks=$((totalChecks + 1)) + if get_docker_effective_command_line_args '--insecure-registry' | grep "insecure-registry" >/dev/null 2>&1; then + warn "$check_2_4" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + elif ! [ -z "$(get_docker_configuration_file_args 'insecure-registries')" ]; then + if get_docker_configuration_file_args 'insecure-registries' | grep '\[]' >/dev/null 2>&1; then + pass "$check_2_4" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_4" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + pass "$check_2_4" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.5 +check_2_5() { + id_2_5="2.5" + desc_2_5="Ensure aufs storage driver is not used" + check_2_5="$id_2_5 - $desc_2_5" + starttestjson "$id_2_5" "$desc_2_5" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "^Storage Driver:\s*aufs\s*$" >/dev/null 2>&1; then + warn "$check_2_5" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + else + pass "$check_2_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.6 +check_2_6() { + id_2_6="2.6" + desc_2_6="Ensure TLS authentication for Docker daemon is configured" + check_2_6="$id_2_6 - $desc_2_6" + starttestjson "$id_2_6" "$desc_2_6" + + totalChecks=$((totalChecks + 1)) + if [ grep -i 'tcp://' "$CONFIG_FILE" 2>/dev/null 1>&2 ] || \ + [ $(get_docker_cumulative_command_line_args '-H' | grep -vE '(unix|fd)://') >/dev/null 2>&1 ]; then + if [ $(get_docker_configuration_file_args '"tlsverify":' | grep 'true') ] || \ + [ $(get_docker_cumulative_command_line_args '--tlsverify' | grep 'tlsverify') >/dev/null 2>&1 ]; then + pass "$check_2_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif [ $(get_docker_configuration_file_args '"tls":' | grep 'true') ] || \ + [ $(get_docker_cumulative_command_line_args '--tls' | grep 'tls$') >/dev/null 2>&1 ]; then + warn "$check_2_6" + warn " * Docker daemon currently listening on TCP with TLS, but no verification" + resulttestjson "WARN" "Docker daemon currently listening on TCP with TLS, but no verification" + currentScore=$((currentScore - 1)) + else + warn "$check_2_6" + warn " * Docker daemon currently listening on TCP without TLS" + resulttestjson "WARN" "Docker daemon currently listening on TCP without TLS" + currentScore=$((currentScore - 1)) + fi + else + info "$check_2_6" + info " * Docker daemon not listening on TCP" + resulttestjson "INFO" "Docker daemon not listening on TCP" + currentScore=$((currentScore + 0)) + fi +} + +# 2.7 +check_2_7() { + id_2_7="2.7" + desc_2_7="Ensure the default ulimit is configured appropriately" + check_2_7="$id_2_7 - $desc_2_7" + starttestjson "$id_2_7" "$desc_2_7" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'default-ulimit' | grep -v '{}' >/dev/null 2>&1; then + pass "$check_2_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--default-ulimit' | grep "default-ulimit" >/dev/null 2>&1; then + pass "$check_2_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + info "$check_2_7" + info " * Default ulimit doesn't appear to be set" + resulttestjson "INFO" "Default ulimit doesn't appear to be set" + currentScore=$((currentScore + 0)) + fi +} + +# 2.8 +check_2_8() { + id_2_8="2.8" + desc_2_8="Enable user namespace support" + check_2_8="$id_2_8 - $desc_2_8" + starttestjson "$id_2_8" "$desc_2_8" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'userns-remap' | grep -v '""'; then + pass "$check_2_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--userns-remap' | grep "userns-remap" >/dev/null 2>&1; then + pass "$check_2_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_8" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 2.9 +check_2_9() { + id_2_9="2.9" + desc_2_9="Ensure the default cgroup usage has been confirmed" + check_2_9="$id_2_9 - $desc_2_9" + starttestjson "$id_2_9" "$desc_2_9" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'cgroup-parent' | grep -v '""'; then + warn "$check_2_9" + info " * Confirm cgroup usage" + resulttestjson "WARN" "Confirm cgroup usage" + currentScore=$((currentScore + 0)) + elif get_docker_effective_command_line_args '--cgroup-parent' | grep "cgroup-parent" >/dev/null 2>&1; then + warn "$check_2_9" + info " * Confirm cgroup usage" + resulttestjson "WARN" "Confirm cgroup usage" + currentScore=$((currentScore + 0)) + else + pass "$check_2_9" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.10 +check_2_10() { + id_2_10="2.10" + desc_2_10="Ensure base device size is not changed until needed" + check_2_10="$id_2_10 - $desc_2_10" + starttestjson "$id_2_10" "$desc_2_10" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'storage-opts' | grep "dm.basesize" >/dev/null 2>&1; then + warn "$check_2_10" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + elif get_docker_effective_command_line_args '--storage-opt' | grep "dm.basesize" >/dev/null 2>&1; then + warn "$check_2_10" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + else + pass "$check_2_10" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.11 +check_2_11() { + id_2_11="2.11" + desc_2_11="Ensure that authorization for Docker client commands is enabled" + check_2_11="$id_2_11 - $desc_2_11" + starttestjson "$id_2_11" "$desc_2_11" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'authorization-plugins' | grep -v '\[]'; then + pass "$check_2_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--authorization-plugin' | grep "authorization-plugin" >/dev/null 2>&1; then + pass "$check_2_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_11" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 2.12 +check_2_12() { + id_2_12="2.12" + desc_2_12="Ensure centralized and remote logging is configured" + check_2_12="$id_2_12 - $desc_2_12" + starttestjson "$id_2_12" "$desc_2_12" + + totalChecks=$((totalChecks + 1)) + if docker info --format '{{ .LoggingDriver }}' | grep 'json-file' >/dev/null 2>&1; then + warn "$check_2_12" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + else + pass "$check_2_12" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 2.13 +check_2_13() { + docker_version=$(docker version | grep -i -A2 '^server' | grep ' Version:' \ + | awk '{print $NF; exit}' | tr -d '[:alpha:]-,.') + totalChecks=$((totalChecks + 1)) + + id_2_13="2.13" + desc_2_13="Ensure operations on legacy registry (v1) are Disabled" + check_2_13="$id_2_13 - $desc_2_13" + starttestjson "$id_2_13" "$desc_2_13" + + if [ "$docker_version" -lt 1712 ]; then + if get_docker_configuration_file_args 'disable-legacy-registry' | grep 'true' >/dev/null 2>&1; then + pass "$check_2_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--disable-legacy-registry' | grep "disable-legacy-registry" >/dev/null 2>&1; then + pass "$check_2_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_13" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + desc_2_13="$desc_2_13 (Deprecated)" + check_2_13="$id_2_13 - $desc_2_13" + info "$check_2_13" + resulttestjson "INFO" + fi +} + +# 2.14 +check_2_14() { + id_2_14="2.14" + desc_2_14="Ensure live restore is Enabled" + check_2_14="$id_2_14 - $desc_2_14" + starttestjson "$id_2_14" "$desc_2_14" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Live Restore Enabled:\s*true\s*" >/dev/null 2>&1; then + pass "$check_2_14" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + if docker info 2>/dev/null | grep -e "Swarm:*\sactive\s*" >/dev/null 2>&1; then + pass "$check_2_14 (Incompatible with swarm mode)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--live-restore' | grep "live-restore" >/dev/null 2>&1; then + pass "$check_2_14" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_14" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + fi +} + +# 2.15 +check_2_15() { + id_2_15="2.15" + desc_2_15="Ensure Userland Proxy is Disabled" + check_2_15="$id_2_15 - $desc_2_15" + starttestjson "$id_2_15" "$desc_2_15" + + totalChecks=$((totalChecks + 1)) + if get_docker_configuration_file_args 'userland-proxy' | grep false >/dev/null 2>&1; then + pass "$check_2_15" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_effective_command_line_args '--userland-proxy=false' 2>/dev/null | grep "userland-proxy=false" >/dev/null 2>&1; then + pass "$check_2_15" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_15" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 2.16 +check_2_16() { + id_2_16="2.16" + desc_2_16="Ensure daemon-wide custom seccomp profile is applied, if needed" + check_2_16="$id_2_16 - $desc_2_16" + starttestjson "$id_2_16" "$desc_2_16" + + totalChecks=$((totalChecks + 1)) + if docker info --format '{{ .SecurityOptions }}' | grep 'name=seccomp,profile=default' 2>/dev/null 1>&2; then + pass "$check_2_16" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + info "$check_2_16" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) + fi +} + +# 2.17 +check_2_17() { + id_2_17="2.17" + desc_2_17="Ensure experimental features are avoided in production" + check_2_17="$id_2_17 - $desc_2_17" + starttestjson "$id_2_17" "$desc_2_17" + + totalChecks=$((totalChecks + 1)) + if docker version -f '{{.Server.Experimental}}' | grep false 2>/dev/null 1>&2; then + pass "$check_2_17" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_17" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 2.18 +check_2_18() { + id_2_18="2.18" + desc_2_18="Ensure containers are restricted from acquiring new privileges" + check_2_18="$id_2_18 - $desc_2_18" + starttestjson "$id_2_18" "$desc_2_18" + + totalChecks=$((totalChecks + 1)) + if get_docker_effective_command_line_args '--no-new-privileges' | grep "no-new-privileges" >/dev/null 2>&1; then + pass "$check_2_18" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + elif get_docker_configuration_file_args 'no-new-privileges' | grep true >/dev/null 2>&1; then + pass "$check_2_18" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_2_18" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +check_2_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/3_docker_daemon_configuration_files.sh b/labs/lab7/hardening/docker-bench-src/tests/3_docker_daemon_configuration_files.sh new file mode 100644 index 00000000..74831a60 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/3_docker_daemon_configuration_files.sh @@ -0,0 +1,612 @@ +#!/bin/sh + +check_3() { + logit "\n" + id_3="3" + desc_3="Docker daemon configuration files" + check_3="$id_3 - $desc_3" + info "$check_3" + startsectionjson "$id_3" "$desc_3" +} + +# 3.1 +check_3_1() { + id_3_1="3.1" + desc_3_1="Ensure that docker.service file ownership is set to root:root" + check_3_1="$id_3_1 - $desc_3_1" + starttestjson "$id_3_1" "$desc_3_1" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.service)" + if [ -f "$file" ]; then + if [ "$(stat -c %u%g $file)" -eq 00 ]; then + pass "$check_3_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_1" + warn " * Wrong ownership for $file" + resulttestjson "WARN" "Wrong ownership for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_1" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.2 +check_3_2() { + id_3_2="3.2" + desc_3_2="Ensure that docker.service file permissions are set to 644 or more restrictive" + check_3_2="$id_3_2 - $desc_3_2" + starttestjson "$id_3_2" "$desc_3_2" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.service)" + if [ -f "$file" ]; then + if [ "$(stat -c %a $file)" -eq 644 ] || [ "$(stat -c %a $file)" -eq 600 ]; then + pass "$check_3_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_2" + warn " * Wrong permissions for $file" + resulttestjson "WARN" "Wrong permissions for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_2" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.3 +check_3_3() { + id_3_3="3.3" + desc_3_3="Ensure that docker.socket file ownership is set to root:root" + check_3_3="$id_3_3 - $desc_3_3" + starttestjson "$id_3_3" "$desc_3_3" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.socket)" + if [ -f "$file" ]; then + if [ "$(stat -c %u%g $file)" -eq 00 ]; then + pass "$check_3_3" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_3" + warn " * Wrong ownership for $file" + resulttestjson "WARN" "Wrong ownership for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_3" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.4 +check_3_4() { + id_3_4="3.4" + desc_3_4="Ensure that docker.socket file permissions are set to 644 or more restrictive" + check_3_4="$id_3_4 - $desc_3_4" + starttestjson "$id_3_4" "$desc_3_4" + + totalChecks=$((totalChecks + 1)) + file="$(get_systemd_service_file docker.socket)" + if [ -f "$file" ]; then + if [ "$(stat -c %a $file)" -eq 644 ] || [ "$(stat -c %a $file)" -eq 600 ]; then + pass "$check_3_4" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_4" + warn " * Wrong permissions for $file" + resulttestjson "WARN" "Wrong permissions for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_4" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.5 +check_3_5() { + id_3_5="3.5" + desc_3_5="Ensure that /etc/docker directory ownership is set to root:root" + check_3_5="$id_3_5 - $desc_3_5" + starttestjson "$id_3_5" "$desc_3_5" + + totalChecks=$((totalChecks + 1)) + directory="/etc/docker" + if [ -d "$directory" ]; then + if [ "$(stat -c %u%g $directory)" -eq 00 ]; then + pass "$check_3_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_5" + warn " * Wrong ownership for $directory" + resulttestjson "WARN" "Wrong ownership for $directory" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_5" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.6 +check_3_6() { + id_3_6="3.6" + desc_3_6="Ensure that /etc/docker directory permissions are set to 755 or more restrictive" + check_3_6="$id_3_6 - $desc_3_6" + starttestjson "$id_3_6" "$desc_3_6" + + totalChecks=$((totalChecks + 1)) + directory="/etc/docker" + if [ -d "$directory" ]; then + if [ "$(stat -c %a $directory)" -eq 755 ] || [ "$(stat -c %a $directory)" -eq 700 ]; then + pass "$check_3_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_6" + warn " * Wrong permissions for $directory" + resulttestjson "WARN" "Wrong permissions for $directory" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_6" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.7 +check_3_7() { + id_3_7="3.7" + desc_3_7="Ensure that registry certificate file ownership is set to root:root" + check_3_7="$id_3_7 - $desc_3_7" + starttestjson "$id_3_7" "$desc_3_7" + + totalChecks=$((totalChecks + 1)) + directory="/etc/docker/certs.d/" + if [ -d "$directory" ]; then + fail=0 + owners=$(find "$directory" -type f -name '*.crt') + for p in $owners; do + if [ "$(stat -c %u $p)" -ne 0 ]; then + fail=1 + fi + done + if [ $fail -eq 1 ]; then + warn "$check_3_7" + warn " * Wrong ownership for $directory" + resulttestjson "WARN" "Wrong ownership for $directory" + currentScore=$((currentScore - 1)) + else + pass "$check_3_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi + else + info "$check_3_7" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.8 +check_3_8() { + id_3_8="3.8" + desc_3_8="Ensure that registry certificate file permissions are set to 444 or more restrictive" + check_3_8="$id_3_8 - $desc_3_8" + starttestjson "$id_3_8" "$desc_3_8" + + totalChecks=$((totalChecks + 1)) + directory="/etc/docker/certs.d/" + if [ -d "$directory" ]; then + fail=0 + perms=$(find "$directory" -type f -name '*.crt') + for p in $perms; do + if [ "$(stat -c %a $p)" -ne 444 ] && [ "$(stat -c %a $p)" -ne 400 ]; then + fail=1 + fi + done + if [ $fail -eq 1 ]; then + warn "$check_3_8" + warn " * Wrong permissions for $directory" + resulttestjson "WARN" "Wrong permissions for $directory" + currentScore=$((currentScore - 1)) + else + pass "$check_3_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi + else + info "$check_3_8" + info " * Directory not found" + resulttestjson "INFO" "Directory not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.9 +check_3_9() { + id_3_9="3.9" + desc_3_9="Ensure that TLS CA certificate file ownership is set to root:root" + check_3_9="$id_3_9 - $desc_3_9" + starttestjson "$id_3_9" "$desc_3_9" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlscacert') ]; then + tlscacert=$(get_docker_configuration_file_args 'tlscacert') + else + tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlscacert" ]; then + if [ "$(stat -c %u%g "$tlscacert")" -eq 00 ]; then + pass "$check_3_9" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_9" + warn " * Wrong ownership for $tlscacert" + resulttestjson "WARN" "Wrong ownership for $tlscacert" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_9" + info " * No TLS CA certificate found" + resulttestjson "INFO" "No TLS CA certificate found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.10 +check_3_10() { + id_3_10="3.10" + desc_3_10="Ensure that TLS CA certificate file permissions are set to 444 or more restrictive" + check_3_10="$id_3_10 - $desc_3_10" + starttestjson "$id_3_10" "$desc_3_10" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlscacert') ]; then + tlscacert=$(get_docker_configuration_file_args 'tlscacert') + else + tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlscacert" ]; then + if [ "$(stat -c %a $tlscacert)" -eq 444 ] || [ "$(stat -c %a $tlscacert)" -eq 400 ]; then + pass "$check_3_10" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_10" + warn " * Wrong permissions for $tlscacert" + resulttestjson "WARN" "Wrong permissions for $tlscacert" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_10" + info " * No TLS CA certificate found" + resulttestjson "INFO" "No TLS CA certificate found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.11 +check_3_11() { + id_3_11="3.11" + desc_3_11="Ensure that Docker server certificate file ownership is set to root:root" + check_3_11="$id_3_11 - $desc_3_11" + starttestjson "$id_3_11" "$desc_3_11" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlscert') ]; then + tlscert=$(get_docker_configuration_file_args 'tlscert') + else + tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlscert" ]; then + if [ "$(stat -c %u%g "$tlscert")" -eq 00 ]; then + pass "$check_3_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_11" + warn " * Wrong ownership for $tlscert" + resulttestjson "WARN" "Wrong ownership for $tlscert" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_11" + info " * No TLS Server certificate found" + resulttestjson "INFO" "No TLS Server certificate found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.12 +check_3_12() { + id_3_12="3.12" + desc_3_12="Ensure that Docker server certificate file permissions are set to 444 or more restrictive" + check_3_12="$id_3_12 - $desc_3_12" + starttestjson "$id_3_12" "$desc_3_12" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlscert') ]; then + tlscert=$(get_docker_configuration_file_args 'tlscert') + else + tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlscert" ]; then + if [ "$(stat -c %a $tlscert)" -eq 444 ] || [ "$(stat -c %a $tlscert)" -eq 400 ]; then + pass "$check_3_12" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_12" + warn " * Wrong permissions for $tlscert" + resulttestjson "WARN" "Wrong permissions for $tlscert" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_12" + info " * No TLS Server certificate found" + resulttestjson "INFO" "No TLS Server certificate found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.13 +check_3_13() { + id_3_13="3.13" + desc_3_13="Ensure that Docker server certificate key file ownership is set to root:root" + check_3_13="$id_3_13 - $desc_3_13" + starttestjson "$id_3_13" "$desc_3_13" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlskey') ]; then + tlskey=$(get_docker_configuration_file_args 'tlskey') + else + tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlskey" ]; then + if [ "$(stat -c %u%g "$tlskey")" -eq 00 ]; then + pass "$check_3_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_13" + warn " * Wrong ownership for $tlskey" + resulttestjson "WARN" "Wrong ownership for $tlskey" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_13" + info " * No TLS Key found" + resulttestjson "INFO" "No TLS Key found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.14 +check_3_14() { + id_3_14="3.14" + desc_3_14="Ensure that Docker server certificate key file permissions are set to 400" + check_3_14="$id_3_14 - $desc_3_14" + starttestjson "$id_3_14" "$desc_3_14" + + totalChecks=$((totalChecks + 1)) + if ! [ -z $(get_docker_configuration_file_args 'tlskey') ]; then + tlskey=$(get_docker_configuration_file_args 'tlskey') + else + tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1) + fi + if [ -f "$tlskey" ]; then + if [ "$(stat -c %a $tlskey)" -eq 400 ]; then + pass "$check_3_14" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_14" + warn " * Wrong permissions for $tlskey" + resulttestjson "WARN" "Wrong permissions for $tlskey" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_14" + info " * No TLS Key found" + resulttestjson "INFO" "No TLS Key found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.15 +check_3_15() { + id_3_15="3.15" + desc_3_15="Ensure that Docker socket file ownership is set to root:docker" + check_3_15="$id_3_15 - $desc_3_15" + starttestjson "$id_3_15" "$desc_3_15" + + totalChecks=$((totalChecks + 1)) + file="/var/run/docker.sock" + if [ -S "$file" ]; then + if [ "$(stat -c %U:%G $file)" = 'root:docker' ]; then + pass "$check_3_15" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_15" + warn " * Wrong ownership for $file" + resulttestjson "WARN" "Wrong ownership for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_15" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.16 +check_3_16() { + id_3_16="3.16" + desc_3_16="Ensure that Docker socket file permissions are set to 660 or more restrictive" + check_3_16="$id_3_16 - $desc_3_16" + starttestjson "$id_3_16" "$desc_3_16" + + totalChecks=$((totalChecks + 1)) + file="/var/run/docker.sock" + if [ -S "$file" ]; then + if [ "$(stat -c %a $file)" -eq 660 ] || [ "$(stat -c %a $file)" -eq 600 ]; then + pass "$check_3_16" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_16" + warn " * Wrong permissions for $file" + resulttestjson "WARN" "Wrong permissions for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_16" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.17 +check_3_17() { + id_3_17="3.17" + desc_3_17="Ensure that daemon.json file ownership is set to root:root" + check_3_17="$id_3_17 - $desc_3_17" + starttestjson "$id_3_17" "$desc_3_17" + + totalChecks=$((totalChecks + 1)) + file="/etc/docker/daemon.json" + if [ -f "$file" ]; then + if [ "$(stat -c %U:%G $file)" = 'root:root' ]; then + pass "$check_3_17" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_17" + warn " * Wrong ownership for $file" + resulttestjson "WARN" "Wrong ownership for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_17" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.18 +check_3_18() { + id_3_18="3.18" + desc_3_18="Ensure that daemon.json file permissions are set to 644 or more restrictive" + check_3_18="$id_3_18 - $desc_3_18" + starttestjson "$id_3_18" "$desc_3_18" + + totalChecks=$((totalChecks + 1)) + file="/etc/docker/daemon.json" + if [ -f "$file" ]; then + if [ "$(stat -c %a $file)" -eq 644 ] || [ "$(stat -c %a $file)" -eq 640 ] || [ "$(stat -c %a $file)" -eq 600 ]; then + pass "$check_3_18" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_18" + warn " * Wrong permissions for $file" + resulttestjson "WARN" "Wrong permissions for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_18" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.19 +check_3_19() { + id_3_19="3.19" + desc_3_19="Ensure that /etc/default/docker file ownership is set to root:root" + check_3_19="$id_3_19 - $desc_3_19" + starttestjson "$id_3_19" "$desc_3_19" + + totalChecks=$((totalChecks + 1)) + file="/etc/default/docker" + if [ -f "$file" ]; then + if [ "$(stat -c %U:%G $file)" = 'root:root' ]; then + pass "$check_3_19" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_19" + warn " * Wrong ownership for $file" + resulttestjson "WARN" "Wrong ownership for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_19" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +# 3.20 +check_3_20() { + id_3_20="3.20" + desc_3_20="Ensure that /etc/default/docker file permissions are set to 644 or more restrictive" + check_3_20="$id_3_20 - $desc_3_20" + starttestjson "$id_3_20" "$desc_3_20" + + totalChecks=$((totalChecks + 1)) + file="/etc/default/docker" + if [ -f "$file" ]; then + if [ "$(stat -c %a $file)" -eq 644 ] || [ "$(stat -c %a $file)" -eq 600 ]; then + pass "$check_3_20" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_3_20" + warn " * Wrong permissions for $file" + resulttestjson "WARN" "Wrong permissions for $file" + currentScore=$((currentScore - 1)) + fi + else + info "$check_3_20" + info " * File not found" + resulttestjson "INFO" "File not found" + currentScore=$((currentScore + 0)) + fi +} + +check_3_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/4_container_images.sh b/labs/lab7/hardening/docker-bench-src/tests/4_container_images.sh new file mode 100644 index 00000000..165b5e2b --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/4_container_images.sh @@ -0,0 +1,278 @@ +#!/bin/sh + +if [ -n "$imgList" ]; then + pattern=$(echo "$imgList" | sed 's/,/ /g') + for img in $pattern; do + echo "Looking for image $img" + sha256=$(docker image ls "$img" -q) + if [ -z "$sha256" ]; then + echo "Image $img not found. Exiting." + exit 1 + fi + images="$images $sha256 " + done +else + images=$(docker images -q) +fi + +check_4() { + logit "\n" + id_4="4" + desc_4="Container Images and Build File" + check_4="$id_4 - $desc_4" + info "$check_4" + startsectionjson "$id_4" "$desc_4" +} + +# 4.1 +check_4_1() { + id_4_1="4.1" + desc_4_1="Ensure a user for the container has been created" + check_4_1="$id_4_1 - $desc_4_1" + starttestjson "$id_4_1" "$desc_4_1" + + totalChecks=$((totalChecks + 1)) + + # If container_users is empty, there are no running containers + if [ -z "$containers" ]; then + info "$check_4_1" + info " * No containers running" + resulttestjson "INFO" "No containers running" + currentScore=$((currentScore + 0)) + else + # We have some containers running, set failure flag to 0. Check for Users. + fail=0 + # Make the loop separator be a new-line in POSIX compliant fashion + set -f; IFS=$' + ' + root_containers="" + for c in $containers; do + user=$(docker inspect --format 'User={{.Config.User}}' "$c") + + if [ "$user" = "User=" ] || [ "$user" = "User=[]" ] || [ "$user" = "User=" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_4_1" + warn " * Running as root: $c" + root_containers="$root_containers $c" + fail=1 + else + warn " * Running as root: $c" + root_containers="$root_containers $c" + fi + fi + done + # We went through all the containers and found none running as root + if [ $fail -eq 0 ]; then + pass "$check_4_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "running as root" "$root_containers" + currentScore=$((currentScore - 1)) + fi + fi + # Make the loop separator go back to space + set +f; unset IFS +} + +# 4.2 +check_4_2() { + id_4_2="4.2" + desc_4_2="Ensure that containers use trusted base images" + check_4_2="$id_4_2 - $desc_4_2" + starttestjson "$id_4_2" "$desc_4_2" + + totalChecks=$((totalChecks + 1)) + note "$check_4_2" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 4.3 +check_4_3() { + id_4_3="4.3" + desc_4_3="Ensure unnecessary packages are not installed in the container" + check_4_3="$id_4_3 - $desc_4_3" + starttestjson "$id_4_3" "$desc_4_3" + + totalChecks=$((totalChecks + 1)) + note "$check_4_3" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 4.4 +check_4_4() { + id_4_4="4.4" + desc_4_4="Ensure images are scanned and rebuilt to include security patches" + check_4_4="$id_4_4 - $desc_4_4" + starttestjson "$id_4_4" "$desc_4_4" + + totalChecks=$((totalChecks + 1)) + note "$check_4_4" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 4.5 +check_4_5() { + id_4_5="4.5" + desc_4_5="Ensure Content trust for Docker is Enabled" + check_4_5="$id_4_5 - $desc_4_5" + starttestjson "$id_4_5" "$desc_4_5" + + totalChecks=$((totalChecks + 1)) + if [ "x$DOCKER_CONTENT_TRUST" = "x1" ]; then + pass "$check_4_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_4_5" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 4.6 +check_4_6() { + id_4_6="4.6" + desc_4_6="Ensure HEALTHCHECK instructions have been added to the container image" + check_4_6="$id_4_6 - $desc_4_6" + starttestjson "$id_4_6" "$desc_4_6" + + totalChecks=$((totalChecks + 1)) + fail=0 + no_health_images="" + for img in $images; do + if docker inspect --format='{{.Config.Healthcheck}}' "$img" 2>/dev/null | grep -e "" >/dev/null 2>&1; then + if [ $fail -eq 0 ]; then + fail=1 + warn "$check_4_6" + fi + imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null) + if ! [ "$imgName" = '[]' ]; then + warn " * No Healthcheck found: $imgName" + no_health_images="$no_health_images $imgName" + fi + fi + done + if [ $fail -eq 0 ]; then + pass "$check_4_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Images w/o HEALTHCHECK" "$no_health_images" + currentScore=$((currentScore - 1)) + fi +} + +# 4.7 +check_4_7() { + id_4_7="4.7" + desc_4_7="Ensure update instructions are not use alone in the Dockerfile" + check_4_7="$id_4_7 - $desc_4_7" + starttestjson "$id_4_7" "$desc_4_7" + + totalChecks=$((totalChecks + 1)) + fail=0 + update_images="" + for img in $images; do + if docker history "$img" 2>/dev/null | grep -e "update" >/dev/null 2>&1; then + if [ $fail -eq 0 ]; then + fail=1 + info "$check_4_7" + fi + imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null) + if ! [ "$imgName" = '[]' ]; then + info " * Update instruction found: $imgName" + update_images="$update_images $imgName" + fi + fi + done + if [ $fail -eq 0 ]; then + pass "$check_4_7" + resulttestjson "PASS" + currentScore=$((currentScore + 0)) + else + resulttestjson "INFO" "Update instructions found" "$update_images" + currentScore=$((currentScore + 0)) + fi +} + +# 4.8 +check_4_8() { + id_4_8="4.8" + desc_4_8="Ensure setuid and setgid permissions are removed in the images" + check_4_8="$id_4_8 - $desc_4_8" + starttestjson "$id_4_8" "$desc_4_8" + + totalChecks=$((totalChecks + 1)) + note "$check_4_8" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 4.9 +check_4_9() { + id_4_9="4.9" + desc_4_9="Ensure COPY is used instead of ADD in Dockerfile" + check_4_9="$id_4_9 - $desc_4_9" + starttestjson "$id_4_9" "$desc_4_9" + + totalChecks=$((totalChecks + 1)) + fail=0 + add_images="" + for img in $images; do + docker history "$img" 2> /dev/null | grep 'ADD' >/dev/null 2>&1 + if [ $? -eq 0 ]; then + if [ $fail -eq 0 ]; then + fail=1 + info "$check_4_9" + fi + imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null) + if ! [ "$imgName" = '[]' ]; then + info " * ADD in image history: $imgName" + add_images="$add_images $imgName" + fi + currentScore=$((currentScore + 0)) + fi + done + if [ $fail -eq 0 ]; then + pass "$check_4_9" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "INFO" "Images using ADD" "$add_images" + fi +} + +# 4.10 +check_4_10() { + id_4_10="4.10" + desc_4_10="Ensure secrets are not stored in Dockerfiles" + check_4_10="$id_4_10 - $desc_4_10" + starttestjson "$id_4_10" "$desc_4_10" + + totalChecks=$((totalChecks + 1)) + note "$check_4_10" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 4.11 +check_4_11() { + id_4_11="4.11" + desc_4_11="Ensure verified packages are only Installed" + check_4_11="$id_4_11 - $desc_4_11" + starttestjson "$id_4_11" "$desc_4_11" + + totalChecks=$((totalChecks + 1)) + note "$check_4_11" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +check_4_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/5_container_runtime.sh b/labs/lab7/hardening/docker-bench-src/tests/5_container_runtime.sh new file mode 100644 index 00000000..9db54d8b --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/5_container_runtime.sh @@ -0,0 +1,1276 @@ +#!/bin/sh + +check_5() { + logit "\n" + id_5="5" + desc_5="Container Runtime" + check_5="$id_5 - $desc_5" + info "$check_5" + startsectionjson "$id_5" "$desc_5" +} + +check_running_containers() { + # If containers is empty, there are no running containers + if [ -z "$containers" ]; then + info " * No containers running, skipping Section 5" + running_containers=0 + else + running_containers=1 + # Make the loop separator be a new-line in POSIX compliant fashion + set -f; IFS=$' + ' + fi +} + +# 5.1 +check_5_1() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_1="5.1" + desc_5_1="Ensure AppArmor Profile is Enabled" + check_5_1="$id_5_1 - $desc_5_1" + starttestjson "$id_5_1" "$desc_5_1" + + totalChecks=$((totalChecks + 1)) + + fail=0 + no_apparmor_containers="" + for c in $containers; do + policy=$(docker inspect --format 'AppArmorProfile={{ .AppArmorProfile }}' "$c") + + if [ "$policy" = "AppArmorProfile=" ] || [ "$policy" = "AppArmorProfile=[]" ] || [ "$policy" = "AppArmorProfile=" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_1" + warn " * No AppArmorProfile Found: $c" + no_apparmor_containers="$no_apparmor_containers $c" + fail=1 + else + warn " * No AppArmorProfile Found: $c" + no_apparmor_containers="$no_apparmor_containers $c" + fi + fi + done + # We went through all the containers and found none without AppArmor + if [ $fail -eq 0 ]; then + pass "$check_5_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with no AppArmorProfile" "$no_apparmor_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.2 +check_5_2() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_2="5.2" + desc_5_2="Ensure SELinux security options are set, if applicable" + check_5_2="$id_5_2 - $desc_5_2" + starttestjson "$id_5_2" "$desc_5_2" + + totalChecks=$((totalChecks + 1)) + + fail=0 + no_securityoptions_containers="" + for c in $containers; do + policy=$(docker inspect --format 'SecurityOpt={{ .HostConfig.SecurityOpt }}' "$c") + + if [ "$policy" = "SecurityOpt=" ] || [ "$policy" = "SecurityOpt=[]" ] || [ "$policy" = "SecurityOpt=" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_2" + warn " * No SecurityOptions Found: $c" + no_securityoptions_containers="$no_securityoptions_containers $c" + fail=1 + else + warn " * No SecurityOptions Found: $c" + no_securityoptions_containers="$no_securityoptions_containers $c" + fi + fi + done + # We went through all the containers and found none without SELinux + if [ $fail -eq 0 ]; then + pass "$check_5_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with no SecurityOptions" "$no_securityoptions_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.3 +check_5_3() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_3="5.3" + desc_5_3="Ensure Linux Kernel Capabilities are restricted within containers" + check_5_3="$id_5_3 - $desc_5_3" + starttestjson "$id_5_3" "$desc_5_3" + + totalChecks=$((totalChecks + 1)) + + fail=0 + caps_containers="" + for c in $containers; do + container_caps=$(docker inspect --format 'CapAdd={{ .HostConfig.CapAdd}}' "$c") + caps=$(echo "$container_caps" | tr "[:lower:]" "[:upper:]" | \ + sed 's/CAPADD/CapAdd/' | \ + sed -r "s/AUDIT_WRITE|CHOWN|DAC_OVERRIDE|FOWNER|FSETID|KILL|MKNOD|NET_BIND_SERVICE|NET_RAW|SETFCAP|SETGID|SETPCAP|SETUID|SYS_CHROOT|\s//g") + + if [ "$caps" != 'CapAdd=' ] && [ "$caps" != 'CapAdd=[]' ] && [ "$caps" != 'CapAdd=' ] && [ "$caps" != 'CapAdd=' ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_3" + warn " * Capabilities added: $caps to $c" + caps_containers="$caps_containers $c" + fail=1 + else + warn " * Capabilities added: $caps to $c" + caps_containers="$caps_containers $c" + fi + fi + done + # We went through all the containers and found none with extra capabilities + if [ $fail -eq 0 ]; then + pass "$check_5_3" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Capabilities added for containers" "$caps_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.4 +check_5_4() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_4="5.4" + desc_5_4="Ensure privileged containers are not used" + check_5_4="$id_5_4 - $desc_5_4" + starttestjson "$id_5_4" "$desc_5_4" + + totalChecks=$((totalChecks + 1)) + + fail=0 + privileged_containers="" + for c in $containers; do + privileged=$(docker inspect --format '{{ .HostConfig.Privileged }}' "$c") + + if [ "$privileged" = "true" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_4" + warn " * Container running in Privileged mode: $c" + privileged_containers="$privileged_containers $c" + fail=1 + else + warn " * Container running in Privileged mode: $c" + privileged_containers="$privileged_containers $c" + fi + fi + done + # We went through all the containers and found no privileged containers + if [ $fail -eq 0 ]; then + pass "$check_5_4" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers running in privileged mode" "$privileged_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.5 +check_5_5() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_5="5.5" + desc_5_5="Ensure sensitive host system directories are not mounted on containers" + check_5_5="$id_5_5 - $desc_5_5" + starttestjson "$id_5_5" "$desc_5_5" + + totalChecks=$((totalChecks + 1)) + + # List of sensitive directories to test for. Script uses new-lines as a separator. + # Note the lack of identation. It needs it for the substring comparison. + sensitive_dirs='/ +/boot +/dev +/etc +/lib +/proc +/sys +/usr' + fail=0 + sensitive_mount_containers="" + for c in $containers; do + if docker inspect --format '{{ .VolumesRW }}' "$c" 2>/dev/null 1>&2; then + volumes=$(docker inspect --format '{{ .VolumesRW }}' "$c") + else + volumes=$(docker inspect --format '{{ .Mounts }}' "$c") + fi + # Go over each directory in sensitive dir and see if they exist in the volumes + for v in $sensitive_dirs; do + sensitive=0 + if echo "$volumes" | grep -e "{.*\s$v\s.*true\s}" 2>/tmp/null 1>&2; then + sensitive=1 + fi + if [ $sensitive -eq 1 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_5" + warn " * Sensitive directory $v mounted in: $c" + sensitive_mount_containers="$sensitive_mount_containers $c:$v" + fail=1 + else + warn " * Sensitive directory $v mounted in: $c" + sensitive_mount_containers="$sensitive_mount_containers $c:$v" + fi + fi + done + done + # We went through all the containers and found none with sensitive mounts + if [ $fail -eq 0 ]; then + pass "$check_5_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with sensitive directories mounted" "$sensitive_mount_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.6 +check_5_6() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_6="5.6" + desc_5_6="Ensure ssh is not run within containers" + check_5_6="$id_5_6 - $desc_5_6" + starttestjson "$id_5_6" "$desc_5_6" + + totalChecks=$((totalChecks + 1)) + + fail=0 + ssh_exec_containers="" + printcheck=0 + for c in $containers; do + + processes=$(docker exec "$c" ps -el 2>/dev/null | grep -c sshd | awk '{print $1}') + if [ "$processes" -ge 1 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_6" + warn " * Container running sshd: $c" + ssh_exec_containers="$ssh_exec_containers $c" + fail=1 + printcheck=1 + else + warn " * Container running sshd: $c" + ssh_exec_containers="$ssh_exec_containers $c" + fi + fi + + exec_check=$(docker exec "$c" ps -el 2>/dev/null) + if [ $? -eq 255 ]; then + if [ $printcheck -eq 0 ]; then + warn "$check_5_6" + printcheck=1 + fi + warn " * Docker exec fails: $c" + ssh_exec_containers="$ssh_exec_containers $c" + fail=1 + fi + + done + # We went through all the containers and found none with sshd + if [ $fail -eq 0 ]; then + pass "$check_5_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with sshd/docker exec failures" "$ssh_exec_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.7 +check_5_7() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_7="5.7" + desc_5_7="Ensure privileged ports are not mapped within containers" + check_5_7="$id_5_7 - $desc_5_7" + starttestjson "$id_5_7" "$desc_5_7" + + totalChecks=$((totalChecks + 1)) + + fail=0 + privileged_port_containers="" + for c in $containers; do + # Port format is private port -> ip: public port + ports=$(docker port "$c" | awk '{print $0}' | cut -d ':' -f2) + + # iterate through port range (line delimited) + for port in $ports; do + if [ ! -z "$port" ] && [ "$port" -lt 1024 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_7" + warn " * Privileged Port in use: $port in $c" + privileged_port_containers="$privileged_port_containers $c:$port" + fail=1 + else + warn " * Privileged Port in use: $port in $c" + privileged_port_containers="$privileged_port_containers $c:$port" + fi + fi + done + done + # We went through all the containers and found no privileged ports + if [ $fail -eq 0 ]; then + pass "$check_5_7" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers using privileged ports" "$privileged_port_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.8 +check_5_8() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_8="5.8" + desc_5_8="Ensure only needed ports are open on the container" + check_5_8="$id_5_8 - $desc_5_8" + starttestjson "$id_5_8" "$desc_5_8" + + totalChecks=$((totalChecks + 1)) + note "$check_5_8" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 5.9 +check_5_9() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_9="5.9" + desc_5_9="Ensure the host's network namespace is not shared" + check_5_9="$id_5_9 - $desc_5_9" + starttestjson "$id_5_9" "$desc_5_9" + + totalChecks=$((totalChecks + 1)) + + fail=0 + net_host_containers="" + for c in $containers; do + mode=$(docker inspect --format 'NetworkMode={{ .HostConfig.NetworkMode }}' "$c") + + if [ "$mode" = "NetworkMode=host" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_9" + warn " * Container running with networking mode 'host': $c" + net_host_containers="$net_host_containers $c" + fail=1 + else + warn " * Container running with networking mode 'host': $c" + net_host_containers="$net_host_containers $c" + fi + fi + done + # We went through all the containers and found no Network Mode host + if [ $fail -eq 0 ]; then + pass "$check_5_9" + resulttestjson "PASS" + currentScore=$((currentScore + 0)) + else + resulttestjson "WARN" "Containers running with networking mode 'host'" "$net_host_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.10 +check_5_10() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_10="5.10" + desc_5_10="Ensure memory usage for container is limited" + check_5_10="$id_5_10 - $desc_5_10" + starttestjson "$id_5_10" "$desc_5_10" + + totalChecks=$((totalChecks + 1)) + + fail=0 + mem_unlimited_containers="" + for c in $containers; do + if docker inspect --format '{{ .Config.Memory }}' "$c" 2> /dev/null 1>&2; then + memory=$(docker inspect --format '{{ .Config.Memory }}' "$c") + else + memory=$(docker inspect --format '{{ .HostConfig.Memory }}' "$c") + fi + + if [ "$memory" = "0" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_10" + warn " * Container running without memory restrictions: $c" + mem_unlimited_containers="$mem_unlimited_containers $c" + fail=1 + else + warn " * Container running without memory restrictions: $c" + mem_unlimited_containers="$mem_unlimited_containers $c" + fi + fi + done + # We went through all the containers and found no lack of Memory restrictions + if [ $fail -eq 0 ]; then + pass "$check_5_10" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Container running without memory restrictions" "$mem_unlimited_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.11 +check_5_11() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_11="5.11" + desc_5_11="Ensure CPU priority is set appropriately on the container" + check_5_11="$id_5_11 - $desc_5_11" + starttestjson "$id_5_11" "$desc_5_11" + + totalChecks=$((totalChecks + 1)) + + fail=0 + cpu_unlimited_containers="" + for c in $containers; do + if docker inspect --format '{{ .Config.CpuShares }}' "$c" 2> /dev/null 1>&2; then + shares=$(docker inspect --format '{{ .Config.CpuShares }}' "$c") + else + shares=$(docker inspect --format '{{ .HostConfig.CpuShares }}' "$c") + fi + + if [ "$shares" = "0" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_11" + warn " * Container running without CPU restrictions: $c" + cpu_unlimited_containers="$cpu_unlimited_containers $c" + fail=1 + else + warn " * Container running without CPU restrictions: $c" + cpu_unlimited_containers="$cpu_unlimited_containers $c" + fi + fi + done + # We went through all the containers and found no lack of CPUShare restrictions + if [ $fail -eq 0 ]; then + pass "$check_5_11" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers running without CPU restrictions" "$cpu_unlimited_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.12 +check_5_12() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_12="5.12" + desc_5_12="Ensure the container's root filesystem is mounted as read only" + check_5_12="$id_5_12 - $desc_5_12" + starttestjson "$id_5_12" "$desc_5_12" + + totalChecks=$((totalChecks + 1)) + + fail=0 + fsroot_mount_containers="" + for c in $containers; do + read_status=$(docker inspect --format '{{ .HostConfig.ReadonlyRootfs }}' "$c") + + if [ "$read_status" = "false" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_12" + warn " * Container running with root FS mounted R/W: $c" + fsroot_mount_containers="$fsroot_mount_containers $c" + fail=1 + else + warn " * Container running with root FS mounted R/W: $c" + fsroot_mount_containers="$fsroot_mount_containers $c" + fi + fi + done + # We went through all the containers and found no R/W FS mounts + if [ $fail -eq 0 ]; then + pass "$check_5_12" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers running with root FS mounted R/W" "$fsroot_mount_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.13 +check_5_13() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_13="5.13" + desc_5_13="Ensure incoming container traffic is binded to a specific host interface" + check_5_13="$id_5_13 - $desc_5_13" + starttestjson "$id_5_13" "$desc_5_13" + + totalChecks=$((totalChecks + 1)) + + fail=0 + incoming_unbound_containers="" + for c in $containers; do + for ip in $(docker port "$c" | awk '{print $3}' | cut -d ':' -f1); do + if [ "$ip" = "0.0.0.0" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_13" + warn " * Port being bound to wildcard IP: $ip in $c" + incoming_unbound_containers="$incoming_unbound_containers $c:$ip" + fail=1 + else + warn " * Port being bound to wildcard IP: $ip in $c" + incoming_unbound_containers="$incoming_unbound_containers $c:$ip" + fi + fi + done + done + # We went through all the containers and found no ports bound to 0.0.0.0 + if [ $fail -eq 0 ]; then + pass "$check_5_13" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with port bound to wildcard IP" "$incoming_unbound_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.14 +check_5_14() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_14="5.14" + desc_5_14="Ensure 'on-failure' container restart policy is set to '5'" + check_5_14="$id_5_14 - $desc_5_14" + starttestjson "$id_5_14" "$desc_5_14" + + totalChecks=$((totalChecks + 1)) + + fail=0 + maxretry_unset_containers="" + for c in $containers; do + policy=$(docker inspect --format MaximumRetryCount='{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' "$c") + + if [ "$policy" != "MaximumRetryCount=5" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_14" + warn " * MaximumRetryCount is not set to 5: $c" + maxretry_unset_containers="$maxretry_unset_containers $c" + fail=1 + else + warn " * MaximumRetryCount is not set to 5: $c" + maxretry_unset_containers="$maxretry_unset_containers $c" + fi + fi + done + # We went through all the containers and they all had MaximumRetryCount=5 + if [ $fail -eq 0 ]; then + pass "$check_5_14" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with MaximumRetryCount not set to 5" "$maxretry_unset_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.15 +check_5_15() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_15="5.15" + desc_5_15="Ensure the host's process namespace is not shared" + check_5_15="$id_5_15 - $desc_5_15" + starttestjson "$id_5_15" "$desc_5_15" + + totalChecks=$((totalChecks + 1)) + + fail=0 + pidns_shared_containers="" + for c in $containers; do + mode=$(docker inspect --format 'PidMode={{.HostConfig.PidMode }}' "$c") + + if [ "$mode" = "PidMode=host" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_15" + warn " * Host PID namespace being shared with: $c" + pidns_shared_containers="$pidns_shared_containers $c" + fail=1 + else + warn " * Host PID namespace being shared with: $c" + pidns_shared_containers="$pidns_shared_containers $c" + fi + fi + done + # We went through all the containers and found none with PidMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_15" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers sharing host PID namespace" "$pidns_shared_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.16 +check_5_16() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_16="5.16" + desc_5_16="Ensure the host's IPC namespace is not shared" + check_5_16="$id_5_16 - $desc_5_16" + starttestjson "$id_5_16" "$desc_5_16" + + totalChecks=$((totalChecks + 1)) + + fail=0 + ipcns_shared_containers="" + for c in $containers; do + mode=$(docker inspect --format 'IpcMode={{.HostConfig.IpcMode }}' "$c") + + if [ "$mode" = "IpcMode=host" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_16" + warn " * Host IPC namespace being shared with: $c" + ipcns_shared_containers="$ipcns_shared_containers $c" + fail=1 + else + warn " * Host IPC namespace being shared with: $c" + ipcns_shared_containers="$ipcns_shared_containers $c" + fi + fi + done + # We went through all the containers and found none with IPCMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_16" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers sharing host IPC namespace" "$ipcns_shared_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.17 +check_5_17() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_17="5.17" + desc_5_17="Ensure host devices are not directly exposed to containers" + check_5_17="$id_5_17 - $desc_5_17" + starttestjson "$id_5_17" "$desc_5_17" + + totalChecks=$((totalChecks + 1)) + + fail=0 + hostdev_exposed_containers="" + for c in $containers; do + devices=$(docker inspect --format 'Devices={{ .HostConfig.Devices }}' "$c") + + if [ "$devices" != "Devices=" ] && [ "$devices" != "Devices=[]" ] && [ "$devices" != "Devices=" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + info "$check_5_17" + info " * Container has devices exposed directly: $c" + hostdev_exposed_containers="$hostdev_exposed_containers $c" + fail=1 + else + info " * Container has devices exposed directly: $c" + hostdev_exposed_containers="$hostdev_exposed_containers $c" + fi + fi + done + # We went through all the containers and found none with devices + if [ $fail -eq 0 ]; then + pass "$check_5_17" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "INFO" "Containers with host devices exposed directly" "$hostdev_exposed_containers" + currentScore=$((currentScore + 0)) + fi +} + +# 5.18 +check_5_18() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_18="5.18" + desc_5_18="Ensure the default ulimit is overwritten at runtime, only if needed" + check_5_18="$id_5_18 - $desc_5_18" + starttestjson "$id_5_18" "$desc_5_18" + + totalChecks=$((totalChecks + 1)) + + fail=0 + no_ulimit_containers="" + for c in $containers; do + ulimits=$(docker inspect --format 'Ulimits={{ .HostConfig.Ulimits }}' "$c") + + if [ "$ulimits" = "Ulimits=" ] || [ "$ulimits" = "Ulimits=[]" ] || [ "$ulimits" = "Ulimits=" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + info "$check_5_18" + info " * Container no default ulimit override: $c" + no_ulimit_containers="$no_ulimit_containers $c" + fail=1 + else + info " * Container no default ulimit override: $c" + no_ulimit_containers="$no_ulimit_containers $c" + fi + fi + done + # We went through all the containers and found none without Ulimits + if [ $fail -eq 0 ]; then + pass "$check_5_18" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "INFO" "Containers with no default ulimit override" "$no_ulimit_containers" + currentScore=$((currentScore + 0)) + fi +} + +# 5.19 +check_5_19() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_19="5.19" + desc_5_19="Ensure mount propagation mode is not set to shared" + check_5_19="$id_5_19 - $desc_5_19" + starttestjson "$id_5_19" "$desc_5_19" + + totalChecks=$((totalChecks + 1)) + + fail=0 + mountprop_shared_containers="" + for c in $containers; do + if docker inspect --format 'Propagation={{range $mnt := .Mounts}} {{json $mnt.Propagation}} {{end}}' "$c" | \ + grep shared 2>/dev/null 1>&2; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_19" + warn " * Mount propagation mode is shared: $c" + mountprop_shared_containers="$mountprop_shared_containers $c" + fail=1 + else + warn " * Mount propagation mode is shared: $c" + mountprop_shared_containers="$mountprop_shared_containers $c" + fi + fi + done + # We went through all the containers and found none with shared propagation mode + if [ $fail -eq 0 ]; then + pass "$check_5_19" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with shared mount propagation" "$mountprop_shared_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.20 +check_5_20() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_20="5.20" + desc_5_20="Ensure the host's UTS namespace is not shared" + check_5_20="$id_5_20 - $desc_5_20" + starttestjson "$id_5_20" "$desc_5_20" + + totalChecks=$((totalChecks + 1)) + + fail=0 + utcns_shared_containers="" + for c in $containers; do + mode=$(docker inspect --format 'UTSMode={{.HostConfig.UTSMode }}' "$c") + + if [ "$mode" = "UTSMode=host" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_20" + warn " * Host UTS namespace being shared with: $c" + utcns_shared_containers="$utcns_shared_containers $c" + fail=1 + else + warn " * Host UTS namespace being shared with: $c" + utcns_shared_containers="$utcns_shared_containers $c" + fi + fi + done + # We went through all the containers and found none with UTSMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_20" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers sharing host UTS namespace" "$utcns_shared_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.21 +check_5_21() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_21="5.21" + desc_5_21="Ensure the default seccomp profile is not Disabled" + check_5_21="$id_5_21 - $desc_5_21" + starttestjson "$id_5_21" "$desc_5_21" + + totalChecks=$((totalChecks + 1)) + + fail=0 + seccomp_disabled_containers="" + for c in $containers; do + if docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "$c" | \ + grep -E 'seccomp:unconfined|seccomp=unconfined' 2>/dev/null 1>&2; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_21" + warn " * Default seccomp profile disabled: $c" + seccomp_disabled_containers="$seccomp_disabled_containers $c" + fail=1 + else + warn " * Default seccomp profile disabled: $c" + seccomp_disabled_containers="$seccomp_disabled_containers $c" + fi + fi + done + # We went through all the containers and found none with default secomp profile disabled + if [ $fail -eq 0 ]; then + pass "$check_5_21" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers with default seccomp profile disabled" "$seccomp_disabled_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.22 +check_5_22() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_22="5.22" + desc_5_22="Ensure docker exec commands are not used with privileged option" + check_5_22="$id_5_22 - $desc_5_22" + starttestjson "$id_5_22" "$desc_5_22" + + totalChecks=$((totalChecks + 1)) + note "$check_5_22" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 5.23 +check_5_23() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_23="5.23" + desc_5_23="Ensure docker exec commands are not used with user option" + check_5_23="$id_5_23 - $desc_5_23" + starttestjson "$id_5_23" "$desc_5_23" + + totalChecks=$((totalChecks + 1)) + note "$check_5_23" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) +} + +# 5.24 +check_5_24() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_24="5.24" + desc_5_24="Ensure cgroup usage is confirmed" + check_5_24="$id_5_24 - $desc_5_24" + starttestjson "$id_5_24" "$desc_5_24" + + totalChecks=$((totalChecks + 1)) + + fail=0 + unexpected_cgroup_containers="" + for c in $containers; do + mode=$(docker inspect --format 'CgroupParent={{.HostConfig.CgroupParent }}x' "$c") + + if [ "$mode" != "CgroupParent=x" ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_24" + warn " * Confirm cgroup usage: $c" + unexpected_cgroup_containers="$unexpected_cgroup_containers $c" + fail=1 + else + warn " * Confirm cgroup usage: $c" + unexpected_cgroup_containers="$unexpected_cgroup_containers $c" + fi + fi + done + # We went through all the containers and found none with UTSMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_24" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers using unexpected cgroup" "$unexpected_cgroup_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.25 +check_5_25() { + if [ "$running_containers" -ne 1 ]; then + return + fi + id_5_25="5.25" + desc_5_25="Ensure the container is restricted from acquiring additional privileges" + check_5_25="$id_5_25 - $desc_5_25" + starttestjson "$id_5_25" "$desc_5_25" + + totalChecks=$((totalChecks + 1)) + + fail=0 + addprivs_containers="" + for c in $containers; do + if ! docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "$c" | grep 'no-new-privileges' 2>/dev/null 1>&2; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_25" + warn " * Privileges not restricted: $c" + addprivs_containers="$addprivs_containers $c" + fail=1 + else + warn " * Privileges not restricted: $c" + addprivs_containers="$addprivs_containers $c" + fi + fi + done + # We went through all the containers and found none with capability to acquire additional privileges + if [ $fail -eq 0 ]; then + pass "$check_5_25" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers without restricted privileges" "$addprivs_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.26 +check_5_26() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_26="5.26" + desc_5_26="Ensure container health is checked at runtime" + check_5_26="$id_5_26 - $desc_5_26" + starttestjson "$id_5_26" "$desc_5_26" + + totalChecks=$((totalChecks + 1)) + + fail=0 + nohealthcheck_containers="" + for c in $containers; do + if ! docker inspect --format '{{ .Id }}: Health={{ .State.Health.Status }}' "$c" 2>/dev/null 1>&2; then + if [ $fail -eq 0 ]; then + warn "$check_5_26" + warn " * Health check not set: $c" + nohealthcheck_containers="$nohealthcheck_containers $c" + fail=1 + else + warn " * Health check not set: $c" + nohealthcheck_containers="$nohealthcheck_containers $c" + fi + fi + done + if [ $fail -eq 0 ]; then + pass "$check_5_26" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers without health check" "$nohealthcheck_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.27 +check_5_27() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_27="5.27" + desc_5_27="Ensure docker commands always get the latest version of the image" + check_5_27="$id_5_27 - $desc_5_27" + starttestjson "$id_5_27" "$desc_5_27" + + totalChecks=$((totalChecks + 1)) + info "$check_5_27" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) +} + +# 5.28 +check_5_28() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_28="5.28" + desc_5_28="Ensure PIDs cgroup limit is used" + check_5_28="$id_5_28 - $desc_5_28" + starttestjson "$id_5_28" "$desc_5_28" + + totalChecks=$((totalChecks + 1)) + + fail=0 + nopids_limit_containers="" + for c in $containers; do + pidslimit=$(docker inspect --format '{{.HostConfig.PidsLimit }}' "$c") + + if [ "$pidslimit" -le 0 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_28" + warn " * PIDs limit not set: $c" + nopids_limit_containers="$nopids_limit_containers $c" + fail=1 + else + warn " * PIDs limit not set: $c" + nopids_limit_containers="$nopids_limit_containers $c" + fi + fi + done + # We went through all the containers and found all with PIDs limit + if [ $fail -eq 0 ]; then + pass "$check_5_28" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers without PIDs cgroup limit" "$nopids_limit_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.29 +check_5_29() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_29="5.29" + desc_5_29="Ensure Docker's default bridge docker0 is not used" + check_5_29="$id_5_29 - $desc_5_29" + starttestjson "$id_5_29" "$desc_5_29" + + totalChecks=$((totalChecks + 1)) + + fail=0 + docker_network_containers="" + networks=$(docker network ls -q 2>/dev/null) + for net in $networks; do + if docker network inspect --format '{{ .Options }}' "$net" 2>/dev/null | grep "com.docker.network.bridge.name:docker0" >/dev/null 2>&1; then + docker0Containers=$(docker network inspect --format='{{ range $k, $v := .Containers }} {{ $k }} {{ end }}' "$net" | \ + sed -e 's/^ //' -e 's/ /\n/g' 2>/dev/null) + + if [ -n "$docker0Containers" ]; then + if [ $fail -eq 0 ]; then + info "$check_5_29" + fail=1 + fi + for c in $docker0Containers; do + if [ -z "$exclude" ]; then + cName=$(docker inspect --format '{{.Name}}' "$c" 2>/dev/null | sed 's/\///g') + else + pattern=$(echo "$exclude" | sed 's/,/|/g') + cName=$(docker inspect --format '{{.Name}}' "$c" 2>/dev/null | sed 's/\///g' | grep -Ev "$pattern" ) + fi + if ! [ -z "$cName" ]; then + info " * Container in docker0 network: $cName" + docker_network_containers="$docker_network_containers $c:$cName" + fi + done + fi + currentScore=$((currentScore + 0)) + fi + done + # We went through all the containers and found none in docker0 network + if [ $fail -eq 0 ]; then + pass "$check_5_29" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "INFO" "Containers using docker0 network" "$docker_network_containers" + currentScore=$((currentScore + 0)) + fi +} + +# 5.30 +check_5_30() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_30="5.30" + desc_5_30="Ensure the host's user namespaces is not shared" + check_5_30="$id_5_30 - $desc_5_30" + starttestjson "$id_5_30" "$desc_5_30" + + totalChecks=$((totalChecks + 1)) + + fail=0 + hostns_shared_containers="" + for c in $containers; do + if docker inspect --format '{{ .HostConfig.UsernsMode }}' "$c" 2>/dev/null | grep -i 'host' >/dev/null 2>&1; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_30" + warn " * Namespace shared: $c" + hostns_shared_containers="$hostns_shared_containers $c" + fail=1 + else + warn " * Namespace shared: $c" + hostns_shared_containers="$hostns_shared_containers $c" + fi + fi + done + # We went through all the containers and found none with host's user namespace shared + if [ $fail -eq 0 ]; then + pass "$check_5_30" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers sharing host user namespace" "$hostns_shared_containers" + currentScore=$((currentScore - 1)) + fi +} + +# 5.31 +check_5_31() { + if [ "$running_containers" -ne 1 ]; then + return + fi + + id_5_31="5.31" + desc_5_31="Ensure the Docker socket is not mounted inside any containers" + check_5_31="$id_5_31 - $desc_5_31" + starttestjson "$id_5_31" "$desc_5_31" + + totalChecks=$((totalChecks + 1)) + + fail=0 + docker_sock_containers="" + for c in $containers; do + if docker inspect --format '{{ .Mounts }}' "$c" 2>/dev/null | grep 'docker.sock' >/dev/null 2>&1; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_31" + warn " * Docker socket shared: $c" + docker_sock_containers="$docker_sock_containers $c" + fail=1 + else + warn " * Docker socket shared: $c" + docker_sock_containers="$docker_sock_containers $c" + fi + fi + done + # We went through all the containers and found none with docker.sock shared + if [ $fail -eq 0 ]; then + pass "$check_5_31" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Containers sharing docker socket" "$docker_sock_containers" + currentScore=$((currentScore - 1)) + fi +} + +check_5_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/6_docker_security_operations.sh b/labs/lab7/hardening/docker-bench-src/tests/6_docker_security_operations.sh new file mode 100644 index 00000000..f2ec50ce --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/6_docker_security_operations.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +check_6() { + logit "\n" + id_6="6" + desc_6="Docker Security Operations" + check_6="$id_6 - $desc_6" + info "$check_6" + startsectionjson "$id_6" "$desc_6" +} + +# 6.1 +check_6_1() { + id_6_1="6.1" + desc_6_1="Avoid image sprawl" + check_6_1="$id_6_1 - $desc_6_1" + starttestjson "$id_6_1" "$desc_6_1" + + totalChecks=$((totalChecks + 1)) + images=$(docker images -q | sort -u | wc -l | awk '{print $1}') + active_images=0 + + for c in $(docker inspect --format "{{.Image}}" $(docker ps -qa) 2>/dev/null); do + if docker images --no-trunc -a | grep "$c" > /dev/null ; then + active_images=$(( active_images += 1 )) + fi + done + + info "$check_6_1" + info " * There are currently: $images images" + + if [ "$active_images" -lt "$((images / 2))" ]; then + info " * Only $active_images out of $images are in use" + fi + resulttestjson "INFO" "$active_images active/$images in use" + currentScore=$((currentScore + 0)) +} + +# 6.2 +check_6_2() { + id_6_2="6.2" + desc_6_2="Avoid container sprawl" + check_6_2="$id_6_2 - $desc_6_2" + starttestjson "$id_6_2" "$desc_6_2" + + totalChecks=$((totalChecks + 1)) + total_containers=$(docker info 2>/dev/null | grep "Containers" | awk '{print $2}') + running_containers=$(docker ps -q | wc -l | awk '{print $1}') + diff="$((total_containers - running_containers))" + if [ "$diff" -gt 25 ]; then + info "$check_6_2" + info " * There are currently a total of $total_containers containers, with only $running_containers of them currently running" + resulttestjson "INFO" "$total_containers total/$running_containers running" + else + info "$check_6_2" + info " * There are currently a total of $total_containers containers, with $running_containers of them currently running" + resulttestjson "INFO" "$total_containers total/$running_containers running" + fi + currentScore=$((currentScore + 0)) +} + +check_6_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/7_docker_swarm_configuration.sh b/labs/lab7/hardening/docker-bench-src/tests/7_docker_swarm_configuration.sh new file mode 100644 index 00000000..be1dd7b4 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/7_docker_swarm_configuration.sh @@ -0,0 +1,250 @@ +#!/bin/sh + +check_7() { + logit "\n" + id_7="7" + desc_7="Docker Swarm Configuration" + check_7="$id_7 - $desc_7" + info "$check_7" + startsectionjson "$id_7" "$desc_7" +} + +# 7.1 +check_7_1() { + id_7_1="7.1" + desc_7_1="Ensure swarm mode is not Enabled, if not needed" + check_7_1="$id_7_1 - $desc_7_1" + starttestjson "$id_7_1" "$desc_7_1" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:*\sinactive\s*" >/dev/null 2>&1; then + pass "$check_7_1" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_7_1" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi +} + +# 7.2 +check_7_2() { + id_7_2="7.2" + desc_7_2="Ensure the minimum number of manager nodes have been created in a swarm" + check_7_2="$id_7_2 - $desc_7_2" + starttestjson "$id_7_2" "$desc_7_2" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:*\sactive\s*" >/dev/null 2>&1; then + managernodes=$(docker node ls | grep -c "Leader") + if [ "$managernodes" -le 1 ]; then + pass "$check_7_2" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_7_2" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + pass "$check_7_2 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.3 +check_7_3() { + id_7_3="7.3" + desc_7_3="Ensure swarm services are binded to a specific host interface" + check_7_3="$id_7_3 - $desc_7_3" + starttestjson "$id_7_3" "$desc_7_3" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:*\sactive\s*" >/dev/null 2>&1; then + ss -lnt | grep -e '\[::]:2377 ' -e ':::2377' -e '*:2377 ' -e ' 0\.0\.0\.0:2377 ' >/dev/null 2>&1 + if [ $? -eq 1 ]; then + pass "$check_7_3" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + warn "$check_7_3" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + fi + else + pass "$check_7_3 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.4 +check_7_4() { + id_7_4="7.4" + desc_7_4="Ensure data exchanged between containers are encrypted on different nodes on the overlay network" + check_7_4="$id_7_4 - $desc_7_4" + starttestjson "$id_7_4" "$desc_7_4" + + totalChecks=$((totalChecks + 1)) + fail=0 + unencrypted_networks="" + for encnet in $(docker network ls --filter driver=overlay --quiet); do + if docker network inspect --format '{{.Name}} {{ .Options }}' "$encnet" | \ + grep -v 'encrypted:' 2>/dev/null 1>&2; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_7_4" + fail=1 + fi + warn " * Unencrypted overlay network: $(docker network inspect --format '{{ .Name }} ({{ .Scope }})' "$encnet")" + unencrypted_networks="$unencrypted_networks $(docker network inspect --format '{{ .Name }} ({{ .Scope }})' "$encnet")" + fi + done + # We went through all the networks and found none that are unencrypted + if [ $fail -eq 0 ]; then + pass "$check_7_4" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + resulttestjson "WARN" "Unencrypted overlay networks:" "$unencrypted_networks" + currentScore=$((currentScore - 1)) + fi +} + +# 7.5 +check_7_5() { + id_7_5="7.5" + desc_7_5="Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster" + check_7_5="$id_7_5 - $desc_7_5" + starttestjson "$id_7_5" "$desc_7_5" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + if [ "$(docker secret ls -q | wc -l)" -ge 1 ]; then + pass "$check_7_5" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + info "$check_7_5" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) + fi + else + pass "$check_7_5 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.6 +check_7_6() { + id_7_6="7.6" + desc_7_6="Ensure swarm manager is run in auto-lock mode" + check_7_6="$id_7_6 - $desc_7_6" + starttestjson "$id_7_6" "$desc_7_6" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + if ! docker swarm unlock-key 2>/dev/null | grep 'SWMKEY' 2>/dev/null 1>&2; then + warn "$check_7_6" + resulttestjson "WARN" + currentScore=$((currentScore - 1)) + else + pass "$check_7_6" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi + else + pass "$check_7_6 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.7 +check_7_7() { + id_7_7="7.7" + desc_7_7="Ensure swarm manager auto-lock key is rotated periodically" + check_7_7="$id_7_7 - $desc_7_7" + starttestjson "$id_7_7" "$desc_7_7" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + note "$check_7_7" + resulttestjson "NOTE" + currentScore=$((currentScore + 0)) + else + pass "$check_7_7 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.8 +check_7_8() { + id_7_8="7.8" + desc_7_8="Ensure node certificates are rotated as appropriate" + check_7_8="$id_7_8 - $desc_7_8" + starttestjson "$id_7_8" "$desc_7_8" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + if docker info 2>/dev/null | grep "Expiry Duration: 2 days"; then + pass "$check_7_8" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + else + info "$check_7_8" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) + fi + else + pass "$check_7_8 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.9 +check_7_9() { + id_7_9="7.9" + desc_7_9="Ensure CA certificates are rotated as appropriate" + check_7_9="$id_7_9 - $desc_7_9" + starttestjson "$id_7_9" "$desc_7_9" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + info "$check_7_9" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) + else + pass "$check_7_9 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +# 7.10 +check_7_10() { + id_7_10="7.10" + desc_7_10="Ensure management plane traffic has been separated from data plane traffic" + check_7_10="$id_7_10 - $desc_7_10" + starttestjson "$id_7_10" "$desc_7_10" + + totalChecks=$((totalChecks + 1)) + if docker info 2>/dev/null | grep -e "Swarm:\s*active\s*" >/dev/null 2>&1; then + info "$check_7_10" + resulttestjson "INFO" + currentScore=$((currentScore + 0)) + else + pass "$check_7_10 (Swarm mode not enabled)" + resulttestjson "PASS" + currentScore=$((currentScore + 1)) + fi +} + +check_7_end() { + endsectionjson +} diff --git a/labs/lab7/hardening/docker-bench-src/tests/99_community_checks.sh b/labs/lab7/hardening/docker-bench-src/tests/99_community_checks.sh new file mode 100644 index 00000000..ef9177f2 --- /dev/null +++ b/labs/lab7/hardening/docker-bench-src/tests/99_community_checks.sh @@ -0,0 +1,26 @@ +#!/bin/sh +check_c() { + logit "\n" + id_99="99" + desc_99="Community contributed checks" + check_99="$id_99 - $desc_99" + info "$check_99" + startsectionjson "$id_99" "$desc_99" +} + +# check_c_1 +check_c_1() { + check_c_1="C.1 - This is a example check" + totalChecks=$((totalChecks + 1)) + if docker info --format='{{ .Architecture }}' | grep 'x86_64' 2>/dev/null 1>&2; then + pass "$check_c_1" + resulttestjson "PASS" + else + warn "$check_c_1" + resulttestjson "WARN" + fi +} + +check_c_end() { + endsectionjson +} diff --git a/labs/lab7/scanning/dockle-results.txt b/labs/lab7/scanning/dockle-results.txt new file mode 100644 index 00000000..91b99573 --- /dev/null +++ b/labs/lab7/scanning/dockle-results.txt @@ -0,0 +1,9 @@ +SKIP - DKL-LI-0001: Avoid empty password + * failed to detect etc/shadow,etc/master.passwd +INFO - CIS-DI-0005: Enable Content trust for Docker + * export DOCKER_CONTENT_TRUST=1 before docker pull/build +INFO - CIS-DI-0006: Add HEALTHCHECK instruction to the container image + * not found HEALTHCHECK statement +INFO - DKL-LI-0003: Only put necessary files + * unnecessary file : juice-shop/node_modules/extglob/lib/.DS_Store + * unnecessary file : juice-shop/node_modules/micromatch/lib/.DS_Store diff --git a/labs/lab7/scanning/scout-cves.txt b/labs/lab7/scanning/scout-cves.txt new file mode 100644 index 00000000..7261f7a5 --- /dev/null +++ b/labs/lab7/scanning/scout-cves.txt @@ -0,0 +1,1179 @@ + + +## Overview + + │ Analyzed Image +───────────────────┼────────────────────────────────────────────── + Target │ bkimminich/juice-shop:v19.0.0 + digest │ 2765a26de764 + platform │ linux/arm64 + provenance │ https://github.com/juice-shop/juice-shop + │ https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7 + vulnerabilities │ 11C 65H 30M 5L 7? + size │ 157 MB + packages │ 1004 + │ + Base image │ gcr.io/distroless/nodejs22-debian12:latest + │ b1464cf00a5f + + +## Packages and Vulnerabilities + + 4C 0H 1M 0L vm2 3.9.17 +pkg:npm/vm2@3.9.17 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2026-22709 [Protection Mechanism Failure] + https://scout.docker.com/v/CVE-2026-22709?s=github&n=vm2&t=npm&vr=%3C%3D3.10.1 + Affected range : <=3.10.1 + Fixed version : 3.10.2 + CVSS Score : 9.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + + ✗ CRITICAL CVE-2023-37903 [Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')] + https://scout.docker.com/v/CVE-2023-37903?s=github&n=vm2&t=npm&vr=%3C%3D3.9.19 + Affected range : <=3.9.19 + Fixed version : not fixed + CVSS Score : 9.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + + ✗ CRITICAL CVE-2023-37466 [Improper Control of Generation of Code ('Code Injection')] + https://scout.docker.com/v/CVE-2023-37466?s=github&n=vm2&t=npm&vr=%3C%3D3.9.19 + Affected range : <=3.9.19 + Fixed version : 3.10.0 + CVSS Score : 9.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + + ✗ CRITICAL CVE-2023-32314 [Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')] + https://scout.docker.com/v/CVE-2023-32314?s=github&n=vm2&t=npm&vr=%3C3.9.18 + Affected range : <3.9.18 + Fixed version : 3.9.18 + CVSS Score : 9.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + + ✗ MEDIUM CVE-2023-32313 [Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')] + https://scout.docker.com/v/CVE-2023-32313?s=github&n=vm2&t=npm&vr=%3C3.9.18 + Affected range : <3.9.18 + Fixed version : 3.9.18 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N + + + 1C 4H 1M 0L node 22.18.0 +pkg:generic/node@22.18.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L22-L22 +FROM gcr.io/distroless/nodejs22-debian12 + + ✗ CRITICAL CVE-2025-55130 + https://scout.docker.com/v/CVE-2025-55130?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + ✗ HIGH CVE-2026-21637 + https://scout.docker.com/v/CVE-2026-21637?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + ✗ HIGH CVE-2025-59466 + https://scout.docker.com/v/CVE-2025-59466?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + ✗ HIGH CVE-2025-59465 + https://scout.docker.com/v/CVE-2025-59465?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + ✗ HIGH CVE-2025-55131 + https://scout.docker.com/v/CVE-2025-55131?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + ✗ MEDIUM CVE-2025-55132 + https://scout.docker.com/v/CVE-2025-55132?s=docker&n=node&t=generic&vr=%3E%3D22.0.0%2C%3C22.22.0 + Affected range : >=22.0.0 + : <22.22.0 + Fixed version : 22.22.0 + + + 1C 3H 1M 0L 1? lodash 2.4.2 +pkg:npm/lodash@2.4.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2019-10744 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2019-10744?s=github&n=lodash&t=npm&vr=%3C4.17.12 + Affected range : <4.17.12 + Fixed version : 4.17.12 + CVSS Score : 9.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H + + ✗ HIGH CVE-2020-8203 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/CVE-2020-8203?s=gitlab&n=lodash&t=npm&vr=%3C4.17.20 + Affected range : <4.17.20 + Fixed version : 4.17.20 + CVSS Score : 7.4 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H + + ✗ HIGH CVE-2021-23337 [Improper Neutralization of Special Elements used in a Command ('Command Injection')] + https://scout.docker.com/v/CVE-2021-23337?s=github&n=lodash&t=npm&vr=%3C4.17.21 + Affected range : <4.17.21 + Fixed version : 4.17.21 + CVSS Score : 7.2 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H + + ✗ HIGH CVE-2018-16487 [Uncontrolled Resource Consumption] + https://scout.docker.com/v/CVE-2018-16487?s=github&n=lodash&t=npm&vr=%3C4.17.11 + Affected range : <4.17.11 + Fixed version : 4.17.11 + + ✗ MEDIUM CVE-2018-3721 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2018-3721?s=github&n=lodash&t=npm&vr=%3C4.17.5 + Affected range : <4.17.5 + Fixed version : 4.17.5 + CVSS Score : 6.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N + + ✗ UNSPECIFIED GMS-2018-10 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2018-10?s=gitlab&n=lodash&t=npm&vr=%3C4.17.5 + Affected range : <4.17.5 + Fixed version : 4.17.5 + + + 1C 1H 2M 0L 1? jsonwebtoken 0.1.0 +pkg:npm/jsonwebtoken@0.1.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2015-9235 [Improper Input Validation] + https://scout.docker.com/v/CVE-2015-9235?s=github&n=jsonwebtoken&t=npm&vr=%3C4.2.2 + Affected range : <4.2.2 + Fixed version : 4.2.2 + + ✗ HIGH CVE-2022-23539 [Use of a Broken or Risky Cryptographic Algorithm] + https://scout.docker.com/v/CVE-2022-23539?s=github&n=jsonwebtoken&t=npm&vr=%3C%3D8.5.1 + Affected range : <=8.5.1 + Fixed version : 9.0.0 + CVSS Score : 8.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N + + ✗ MEDIUM CVE-2022-23540 [Improper Authentication] + https://scout.docker.com/v/CVE-2022-23540?s=github&n=jsonwebtoken&t=npm&vr=%3C9.0.0 + Affected range : <9.0.0 + Fixed version : 9.0.0 + CVSS Score : 6.4 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:L + + ✗ MEDIUM CVE-2022-23541 [Improper Restriction of Security Token Assignment] + https://scout.docker.com/v/CVE-2022-23541?s=github&n=jsonwebtoken&t=npm&vr=%3C%3D8.5.1 + Affected range : <=8.5.1 + Fixed version : 9.0.0 + CVSS Score : 5.0 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L + + ✗ UNSPECIFIED GMS-2015-4 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2015-4?s=gitlab&n=jsonwebtoken&t=npm&vr=%3C4.2.2 + Affected range : <4.2.2 + Fixed version : 4.2.2 + + + 1C 1H 2M 0L 1? jsonwebtoken 0.4.0 +pkg:npm/jsonwebtoken@0.4.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2015-9235 [Improper Input Validation] + https://scout.docker.com/v/CVE-2015-9235?s=github&n=jsonwebtoken&t=npm&vr=%3C4.2.2 + Affected range : <4.2.2 + Fixed version : 4.2.2 + + ✗ HIGH CVE-2022-23539 [Use of a Broken or Risky Cryptographic Algorithm] + https://scout.docker.com/v/CVE-2022-23539?s=github&n=jsonwebtoken&t=npm&vr=%3C%3D8.5.1 + Affected range : <=8.5.1 + Fixed version : 9.0.0 + CVSS Score : 8.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N + + ✗ MEDIUM CVE-2022-23540 [Improper Authentication] + https://scout.docker.com/v/CVE-2022-23540?s=github&n=jsonwebtoken&t=npm&vr=%3C9.0.0 + Affected range : <9.0.0 + Fixed version : 9.0.0 + CVSS Score : 6.4 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:L + + ✗ MEDIUM CVE-2022-23541 [Improper Restriction of Security Token Assignment] + https://scout.docker.com/v/CVE-2022-23541?s=github&n=jsonwebtoken&t=npm&vr=%3C%3D8.5.1 + Affected range : <=8.5.1 + Fixed version : 9.0.0 + CVSS Score : 5.0 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L + + ✗ UNSPECIFIED GMS-2015-4 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2015-4?s=gitlab&n=jsonwebtoken&t=npm&vr=%3C4.2.2 + Affected range : <4.2.2 + Fixed version : 4.2.2 + + + 1C 1H 0M 0L crypto-js 3.3.0 +pkg:npm/crypto-js@3.3.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2023-46233 [Use of a Broken or Risky Cryptographic Algorithm] + https://scout.docker.com/v/CVE-2023-46233?s=github&n=crypto-js&t=npm&vr=%3C4.2.0 + Affected range : <4.2.0 + Fixed version : 4.2.0 + CVSS Score : 9.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N + + ✗ HIGH GMS-2020-4 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2020-4?s=gitlab&n=crypto-js&t=npm&vr=%3E%3D3.3.0%2C%3C4.0.0 + Affected range : >=3.3.0 + : <4.0.0 + Fixed version : 3.2.1, 4.0.0 + CVSS Score : 7.5 + CVSS Vector : AV:N/AC:L/Au:N/C:P/I:P/A:P + + + 1C 0H 1M 0L minimist 0.2.4 +pkg:npm/minimist@0.2.4 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL CVE-2021-44906 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/CVE-2021-44906?s=gitlab&n=minimist&t=npm&vr=%3C1.2.6 + Affected range : <1.2.6 + Fixed version : 1.2.6 + CVSS Score : 9.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H + + ✗ MEDIUM CVE-2020-7598 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/CVE-2020-7598?s=gitlab&n=minimist&t=npm&vr=%3C1.2.2 + Affected range : <1.2.2 + Fixed version : 1.2.2 + CVSS Score : 5.6 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L + + + 1C 0H 0M 0L marsdb 0.6.11 +pkg:npm/marsdb@0.6.11 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ CRITICAL GHSA-5mrr-rgp6-x4gr [Improper Neutralization of Special Elements used in a Command ('Command Injection')] + https://scout.docker.com/v/GHSA-5mrr-rgp6-x4gr?s=github&n=marsdb&t=npm&vr=%3E%3D0.0.0 + Affected range : >=0.0.0 + Fixed version : not fixed + + + 0C 6H 1M 0L tar 4.4.19 +pkg:npm/tar@4.4.19 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-23950 [Improper Handling of Unicode Encoding] + https://scout.docker.com/v/CVE-2026-23950?s=github&n=tar&t=npm&vr=%3C%3D7.5.3 + Affected range : <=7.5.3 + Fixed version : 7.5.4 + CVSS Score : 8.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L + + ✗ HIGH CVE-2026-31802 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-31802?s=github&n=tar&t=npm&vr=%3C%3D7.5.10 + Affected range : <=7.5.10 + Fixed version : 7.5.11 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N + + ✗ HIGH CVE-2026-29786 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-29786?s=github&n=tar&t=npm&vr=%3C%3D7.5.9 + Affected range : <=7.5.9 + Fixed version : 7.5.10 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:L/SC:N/SI:H/SA:L + + ✗ HIGH CVE-2026-24842 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-24842?s=github&n=tar&t=npm&vr=%3C7.5.7 + Affected range : <7.5.7 + Fixed version : 7.5.7 + CVSS Score : 8.2 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N + + ✗ HIGH CVE-2026-23745 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-23745?s=github&n=tar&t=npm&vr=%3C%3D7.5.2 + Affected range : <=7.5.2 + Fixed version : 7.5.3 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N + + ✗ HIGH CVE-2026-26960 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-26960?s=github&n=tar&t=npm&vr=%3C7.5.8 + Affected range : <7.5.8 + Fixed version : 7.5.8 + CVSS Score : 7.1 + CVSS Vector : CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N + + ✗ MEDIUM CVE-2024-28863 [Uncontrolled Resource Consumption] + https://scout.docker.com/v/CVE-2024-28863?s=github&n=tar&t=npm&vr=%3C6.2.1 + Affected range : <6.2.1 + Fixed version : 6.2.1 + CVSS Score : 6.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H + + + 0C 6H 0M 0L tar 6.2.1 +pkg:npm/tar@6.2.1 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-23950 [Improper Handling of Unicode Encoding] + https://scout.docker.com/v/CVE-2026-23950?s=github&n=tar&t=npm&vr=%3C%3D7.5.3 + Affected range : <=7.5.3 + Fixed version : 7.5.4 + CVSS Score : 8.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L + + ✗ HIGH CVE-2026-31802 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-31802?s=github&n=tar&t=npm&vr=%3C%3D7.5.10 + Affected range : <=7.5.10 + Fixed version : 7.5.11 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N + + ✗ HIGH CVE-2026-29786 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-29786?s=github&n=tar&t=npm&vr=%3C%3D7.5.9 + Affected range : <=7.5.9 + Fixed version : 7.5.10 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:L/SC:N/SI:H/SA:L + + ✗ HIGH CVE-2026-24842 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-24842?s=github&n=tar&t=npm&vr=%3C7.5.7 + Affected range : <7.5.7 + Fixed version : 7.5.7 + CVSS Score : 8.2 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N + + ✗ HIGH CVE-2026-23745 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-23745?s=github&n=tar&t=npm&vr=%3C%3D7.5.2 + Affected range : <=7.5.2 + Fixed version : 7.5.3 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N + + ✗ HIGH CVE-2026-26960 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-26960?s=github&n=tar&t=npm&vr=%3C7.5.8 + Affected range : <7.5.8 + Fixed version : 7.5.8 + CVSS Score : 7.1 + CVSS Vector : CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N + + + 0C 6H 0M 0L tar 7.4.3 +pkg:npm/tar@7.4.3 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-23950 [Improper Handling of Unicode Encoding] + https://scout.docker.com/v/CVE-2026-23950?s=github&n=tar&t=npm&vr=%3C%3D7.5.3 + Affected range : <=7.5.3 + Fixed version : 7.5.4 + CVSS Score : 8.8 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:H/A:L + + ✗ HIGH CVE-2026-31802 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-31802?s=github&n=tar&t=npm&vr=%3C%3D7.5.10 + Affected range : <=7.5.10 + Fixed version : 7.5.11 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N + + ✗ HIGH CVE-2026-29786 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-29786?s=github&n=tar&t=npm&vr=%3C%3D7.5.9 + Affected range : <=7.5.9 + Fixed version : 7.5.10 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:H/VA:L/SC:N/SI:H/SA:L + + ✗ HIGH CVE-2026-24842 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-24842?s=github&n=tar&t=npm&vr=%3C7.5.7 + Affected range : <7.5.7 + Fixed version : 7.5.7 + CVSS Score : 8.2 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N + + ✗ HIGH CVE-2026-23745 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-23745?s=github&n=tar&t=npm&vr=%3C%3D7.5.2 + Affected range : <=7.5.2 + Fixed version : 7.5.3 + CVSS Score : 8.2 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:A/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N + + ✗ HIGH CVE-2026-26960 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2026-26960?s=github&n=tar&t=npm&vr=%3C7.5.8 + Affected range : <7.5.8 + Fixed version : 7.5.8 + CVSS Score : 7.1 + CVSS Vector : CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N + + + 0C 4H 0M 0L multer 1.4.5-lts.2 +pkg:npm/multer@1.4.5-lts.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-3520 [Uncontrolled Recursion] + https://scout.docker.com/v/CVE-2026-3520?s=github&n=multer&t=npm&vr=%3C2.1.1 + Affected range : <2.1.1 + Fixed version : 2.1.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-3304 [Incomplete Cleanup] + https://scout.docker.com/v/CVE-2026-3304?s=github&n=multer&t=npm&vr=%3C2.1.0 + Affected range : <2.1.0 + Fixed version : 2.1.0 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-2359 [Missing Release of Resource after Effective Lifetime] + https://scout.docker.com/v/CVE-2026-2359?s=github&n=multer&t=npm&vr=%3C2.1.0 + Affected range : <2.1.0 + Fixed version : 2.1.0 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2025-47935 [Missing Release of Memory after Effective Lifetime] + https://scout.docker.com/v/CVE-2025-47935?s=github&n=multer&t=npm&vr=%3C2.0.0 + Affected range : <2.0.0 + Fixed version : 2.0.0 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 3H 0M 0L minimatch 3.1.2 +pkg:npm/minimatch@3.1.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-26996 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-26996?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 10.2.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-27904 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-27904?s=github&n=minimatch&t=npm&vr=%3C3.1.4 + Affected range : <3.1.4 + Fixed version : 3.1.4 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ HIGH CVE-2026-27903 [Inefficient Algorithmic Complexity] + https://scout.docker.com/v/CVE-2026-27903?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 3.1.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 3H 0M 0L minimatch 3.0.5 +pkg:npm/minimatch@3.0.5 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-26996 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-26996?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 10.2.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-27904 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-27904?s=github&n=minimatch&t=npm&vr=%3C3.1.4 + Affected range : <3.1.4 + Fixed version : 3.1.4 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ HIGH CVE-2026-27903 [Inefficient Algorithmic Complexity] + https://scout.docker.com/v/CVE-2026-27903?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 3.1.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 3H 0M 0L minimatch 9.0.5 +pkg:npm/minimatch@9.0.5 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-26996 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-26996?s=github&n=minimatch&t=npm&vr=%3E%3D9.0.0%2C%3C9.0.6 + Affected range : >=9.0.0 + : <9.0.6 + Fixed version : 10.2.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-27904 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-27904?s=github&n=minimatch&t=npm&vr=%3E%3D9.0.0%2C%3C9.0.7 + Affected range : >=9.0.0 + : <9.0.7 + Fixed version : 9.0.7 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ HIGH CVE-2026-27903 [Inefficient Algorithmic Complexity] + https://scout.docker.com/v/CVE-2026-27903?s=github&n=minimatch&t=npm&vr=%3E%3D9.0.0%2C%3C9.0.7 + Affected range : >=9.0.0 + : <9.0.7 + Fixed version : 9.0.7 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 3H 0M 0L minimatch 3.0.8 +pkg:npm/minimatch@3.0.8 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-26996 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-26996?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 10.2.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-27904 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-27904?s=github&n=minimatch&t=npm&vr=%3C3.1.4 + Affected range : <3.1.4 + Fixed version : 3.1.4 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ HIGH CVE-2026-27903 [Inefficient Algorithmic Complexity] + https://scout.docker.com/v/CVE-2026-27903?s=github&n=minimatch&t=npm&vr=%3C3.1.3 + Affected range : <3.1.3 + Fixed version : 3.1.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 3H 0M 0L minimatch 5.1.6 +pkg:npm/minimatch@5.1.6 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-26996 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-26996?s=github&n=minimatch&t=npm&vr=%3E%3D5.0.0%2C%3C5.1.7 + Affected range : >=5.0.0 + : <5.1.7 + Fixed version : 10.2.1 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ HIGH CVE-2026-27904 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-27904?s=github&n=minimatch&t=npm&vr=%3E%3D5.0.0%2C%3C5.1.8 + Affected range : >=5.0.0 + : <5.1.8 + Fixed version : 5.1.8 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ HIGH CVE-2026-27903 [Inefficient Algorithmic Complexity] + https://scout.docker.com/v/CVE-2026-27903?s=github&n=minimatch&t=npm&vr=%3E%3D5.0.0%2C%3C5.1.8 + Affected range : >=5.0.0 + : <5.1.8 + Fixed version : 5.1.8 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 2H 1M 0L 1? moment 2.0.0 +pkg:npm/moment@2.0.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2022-24785 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2022-24785?s=github&n=moment&t=npm&vr=%3C2.29.2 + Affected range : <2.29.2 + Fixed version : 2.29.2 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N + + ✗ HIGH CVE-2017-18214 [Uncontrolled Resource Consumption] + https://scout.docker.com/v/CVE-2017-18214?s=github&n=moment&t=npm&vr=%3C2.19.3 + Affected range : <2.19.3 + Fixed version : 2.19.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ MEDIUM CVE-2016-4055 [Uncontrolled Resource Consumption] + https://scout.docker.com/v/CVE-2016-4055?s=github&n=moment&t=npm&vr=%3C2.11.2 + Affected range : <2.11.2 + Fixed version : 2.11.2 + CVSS Score : 6.5 + CVSS Vector : CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H + + ✗ UNSPECIFIED GMS-2017-332 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2017-332?s=gitlab&n=moment&t=npm&vr=%3C2.19.3 + Affected range : <2.19.3 + Fixed version : 2.19.3 + + + 0C 2H 0M 0L 1? jws 0.2.6 +pkg:npm/jws@0.2.6 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2016-1000223 + https://scout.docker.com/v/CVE-2016-1000223?s=github&n=jws&t=npm&vr=%3C3.0.0 + Affected range : <3.0.0 + Fixed version : 3.0.0 + CVSS Score : 8.7 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N + + ✗ HIGH CVE-2025-65945 [Improper Verification of Cryptographic Signature] + https://scout.docker.com/v/CVE-2025-65945?s=github&n=jws&t=npm&vr=%3C3.2.3 + Affected range : <3.2.3 + Fixed version : 3.2.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N + + ✗ UNSPECIFIED GMS-2016-54 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2016-54?s=gitlab&n=jws&t=npm&vr=%3C3.0.0 + Affected range : <3.0.0 + Fixed version : 3.0.0 + + + 0C 1H 6M 0L 2? sanitize-html 1.4.2 +pkg:npm/sanitize-html@1.4.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2022-25887 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2022-25887?s=github&n=sanitize-html&t=npm&vr=%3C2.7.1 + Affected range : <2.7.1 + Fixed version : 2.7.1 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + ✗ MEDIUM CVE-2019-25225 [Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')] + https://scout.docker.com/v/CVE-2019-25225?s=github&n=sanitize-html&t=npm&vr=%3C2.0.0-beta + Affected range : <2.0.0-beta + Fixed version : 2.0.0-beta + CVSS Score : 6.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N + + ✗ MEDIUM CVE-2016-1000237 [Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')] + https://scout.docker.com/v/CVE-2016-1000237?s=github&n=sanitize-html&t=npm&vr=%3C1.4.3 + Affected range : <1.4.3 + Fixed version : 1.4.3 + CVSS Score : 6.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N + + ✗ MEDIUM CVE-2024-21501 [Exposure of Sensitive Information to an Unauthorized Actor] + https://scout.docker.com/v/CVE-2024-21501?s=github&n=sanitize-html&t=npm&vr=%3C2.12.1 + Affected range : <2.12.1 + Fixed version : 2.12.1 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N + + ✗ MEDIUM CVE-2021-26540 [Improper Input Validation] + https://scout.docker.com/v/CVE-2021-26540?s=github&n=sanitize-html&t=npm&vr=%3C2.3.2 + Affected range : <2.3.2 + Fixed version : 2.3.2 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N + + ✗ MEDIUM CVE-2021-26539 [Improper Input Validation] + https://scout.docker.com/v/CVE-2021-26539?s=github&n=sanitize-html&t=npm&vr=%3C2.3.1 + Affected range : <2.3.1 + Fixed version : 2.3.1 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N + + ✗ MEDIUM CVE-2017-16016 [Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')] + https://scout.docker.com/v/CVE-2017-16016?s=github&n=sanitize-html&t=npm&vr=%3C%3D1.11.1 + Affected range : <=1.11.1 + Fixed version : 1.11.4 + + ✗ UNSPECIFIED GMS-2016-57 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2016-57?s=gitlab&n=sanitize-html&t=npm&vr=%3C%3D1.4.2 + Affected range : <=1.4.2 + Fixed version : 1.4.3 + + ✗ UNSPECIFIED GMS-2016-17 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GMS-2016-17?s=gitlab&n=sanitize-html&t=npm&vr=%3C1.11.4 + Affected range : <1.11.4 + Fixed version : 1.11.4 + + + 0C 1H 1M 0L validator 13.15.15 +pkg:npm/validator@13.15.15 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2025-12758 [Encoding Error] + https://scout.docker.com/v/CVE-2025-12758?s=github&n=validator&t=npm&vr=%3C13.15.22 + Affected range : <13.15.22 + Fixed version : 13.15.22 + CVSS Score : 7.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:P + + ✗ MEDIUM CVE-2025-56200 [Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')] + https://scout.docker.com/v/CVE-2025-56200?s=github&n=validator&t=npm&vr=%3C13.15.20 + Affected range : <13.15.20 + Fixed version : 13.15.20 + CVSS Score : 6.1 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N + + + 0C 1H 1M 0L socket.io-parser 4.0.5 +pkg:npm/socket.io-parser@4.0.5 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-33151 [Improper Check for Unusual or Exceptional Conditions] + https://scout.docker.com/v/CVE-2026-33151?s=github&n=socket.io-parser&t=npm&vr=%3E%3D4.0.0%2C%3C4.2.6 + Affected range : >=4.0.0 + : <4.2.6 + Fixed version : 4.2.6 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + ✗ MEDIUM CVE-2023-32695 [Improper Input Validation] + https://scout.docker.com/v/CVE-2023-32695?s=github&n=socket.io-parser&t=npm&vr=%3E%3D4.0.4%2C%3C4.2.3 + Affected range : >=4.0.4 + : <4.2.3 + Fixed version : 4.2.3 + CVSS Score : 6.9 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N + + + 0C 1H 1M 0L socket.io 3.1.2 +pkg:npm/socket.io@3.1.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH GHSA-25hc-qcg6-38wj [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/GHSA-25hc-qcg6-38wj?s=gitlab&n=socket.io&t=npm&vr=%3E%3D3.0.0%2C%3C4.6.2 + Affected range : >=3.0.0 + : <4.6.2 + Fixed version : 2.5.1, 4.6.2 + CVSS Score : 7.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L + + ✗ MEDIUM CVE-2024-38355 [Improper Input Validation] + https://scout.docker.com/v/CVE-2024-38355?s=github&n=socket.io&t=npm&vr=%3E%3D3.0.0%2C%3C4.6.2 + Affected range : >=3.0.0 + : <4.6.2 + Fixed version : 4.6.2 + CVSS Score : 6.9 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N + + + 0C 1H 0M 0L http-cache-semantics 3.8.1 +pkg:npm/http-cache-semantics@3.8.1 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2022-25881 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2022-25881?s=github&n=http-cache-semantics&t=npm&vr=%3C4.1.1 + Affected range : <4.1.1 + Fixed version : 4.1.1 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 1H 0M 0L lodash.set 4.3.2 +pkg:npm/lodash.set@4.3.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2020-8203 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2020-8203?s=github&n=lodash.set&t=npm&vr=%3E%3D3.7.0%2C%3C%3D4.3.2 + Affected range : >=3.7.0 + : <=4.3.2 + Fixed version : not fixed + CVSS Score : 7.4 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H + + + 0C 1H 0M 0L mout 1.2.4 +pkg:npm/mout@1.2.4 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2020-7792 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/CVE-2020-7792?s=gitlab&n=mout&t=npm&vr=%3E%3D0 + Affected range : >=0 + Fixed version : not fixed + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 1H 0M 0L tar-fs 2.1.3 +pkg:npm/tar-fs@2.1.3 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2025-59343 [Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')] + https://scout.docker.com/v/CVE-2025-59343?s=github&n=tar-fs&t=npm&vr=%3E%3D2.0.0%2C%3C2.1.4 + Affected range : >=2.0.0 + : <2.1.4 + Fixed version : 2.1.4 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N + + + 0C 1H 0M 0L glob 10.4.5 +pkg:npm/glob@10.4.5 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2025-64756 [Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')] + https://scout.docker.com/v/CVE-2025-64756?s=github&n=glob&t=npm&vr=%3E%3D10.2.0%2C%3C10.5.0 + Affected range : >=10.2.0 + : <10.5.0 + Fixed version : 11.1.0 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H + + + 0C 1H 0M 0L ip 2.0.1 +pkg:npm/ip@2.0.1 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2024-29415 [Server-Side Request Forgery (SSRF)] + https://scout.docker.com/v/CVE-2024-29415?s=github&n=ip&t=npm&vr=%3C%3D2.0.1 + Affected range : <=2.0.1 + Fixed version : not fixed + CVSS Score : 8.1 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H + + + 0C 1H 0M 0L sequelize 6.37.7 +pkg:npm/sequelize@6.37.7 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2026-30951 [Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')] + https://scout.docker.com/v/CVE-2026-30951?s=github&n=sequelize&t=npm&vr=%3E%3D6.0.0-beta.1%2C%3C%3D6.37.7 + Affected range : >=6.0.0-beta.1 + : <=6.37.7 + Fixed version : 6.37.8 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N + + + 0C 1H 0M 0L express-jwt 0.1.3 +pkg:npm/express-jwt@0.1.3 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2020-15084 [Improper Authorization] + https://scout.docker.com/v/CVE-2020-15084?s=github&n=express-jwt&t=npm&vr=%3C%3D5.3.3 + Affected range : <=5.3.3 + Fixed version : 6.0.0 + CVSS Score : 7.7 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:N + + + 0C 1H 0M 0L braces 2.3.2 +pkg:npm/braces@2.3.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2024-4068 [Excessive Platform Resource Consumption within a Loop] + https://scout.docker.com/v/CVE-2024-4068?s=github&n=braces&t=npm&vr=%3C3.0.3 + Affected range : <3.0.3 + Fixed version : 3.0.3 + CVSS Score : 7.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H + + + 0C 1H 0M 0L ws 7.4.6 +pkg:npm/ws@7.4.6 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ HIGH CVE-2024-37890 [NULL Pointer Dereference] + https://scout.docker.com/v/CVE-2024-37890?s=github&n=ws&t=npm&vr=%3E%3D7.0.0%2C%3C7.5.10 + Affected range : >=7.0.0 + : <7.5.10 + Fixed version : 7.5.10 + CVSS Score : 8.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N + + + 0C 0H 1M 1L qs 6.13.0 +pkg:npm/qs@6.13.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2025-15284 [Improper Input Validation] + https://scout.docker.com/v/CVE-2025-15284?s=github&n=qs&t=npm&vr=%3C6.14.1 + Affected range : <6.14.1 + Fixed version : 6.14.1 + CVSS Score : 6.3 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:L + + ✗ LOW CVE-2026-2391 [Improper Input Validation] + https://scout.docker.com/v/CVE-2026-2391?s=github&n=qs&t=npm&vr=%3E%3D6.7.0%2C%3C%3D6.14.1 + Affected range : >=6.7.0 + : <=6.14.1 + Fixed version : 6.14.2 + CVSS Score : 3.7 + CVSS Vector : CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L + + + 0C 0H 1M 0L lodash 4.17.21 +pkg:npm/lodash@4.17.21 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2025-13465 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2025-13465?s=github&n=lodash&t=npm&vr=%3E%3D4.0.0%2C%3C%3D4.17.22 + Affected range : >=4.0.0 + : <=4.17.22 + Fixed version : 4.17.23 + CVSS Score : 6.9 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:H/SI:H/SA:H/E:P + + + 0C 0H 1M 0L file-type 16.5.4 +pkg:npm/file-type@16.5.4 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2026-31808 [Loop with Unreachable Exit Condition ('Infinite Loop')] + https://scout.docker.com/v/CVE-2026-31808?s=github&n=file-type&t=npm&vr=%3E%3D13.0.0%2C%3C21.3.1 + Affected range : >=13.0.0 + : <21.3.1 + Fixed version : 21.3.1 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L + + + 0C 0H 1M 0L engine.io 4.1.2 +pkg:npm/engine.io@4.1.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2022-41940 [Uncaught Exception] + https://scout.docker.com/v/CVE-2022-41940?s=github&n=engine.io&t=npm&vr=%3E%3D4.0.0%2C%3C6.2.1 + Affected range : >=4.0.0 + : <6.2.1 + Fixed version : 6.2.1 + CVSS Score : 6.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H + + + 0C 0H 1M 0L dottie 2.0.6 +pkg:npm/dottie@2.0.6 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2026-27837 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2026-27837?s=github&n=dottie&t=npm&vr=%3E%3D2.0.4%2C%3C%3D2.0.6 + Affected range : >=2.0.4 + : <=2.0.6 + Fixed version : 2.0.7 + CVSS Score : 6.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:L + + + 0C 0H 1M 0L js-yaml 3.14.1 +pkg:npm/js-yaml@3.14.1 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2025-64718 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2025-64718?s=github&n=js-yaml&t=npm&vr=%3C3.14.2 + Affected range : <3.14.2 + Fixed version : 4.1.1 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N + + + 0C 0H 1M 0L micromatch 3.1.10 +pkg:npm/micromatch@3.1.10 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2024-4067 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2024-4067?s=github&n=micromatch&t=npm&vr=%3C4.0.8 + Affected range : <4.0.8 + Fixed version : 4.0.8 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L + + + 0C 0H 1M 0L hbs 4.2.0 +pkg:npm/hbs@4.2.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2021-32822 [OWASP Top Ten 2017 Category A9 - Using Components with Known Vulnerabilities] + https://scout.docker.com/v/CVE-2021-32822?s=gitlab&n=hbs&t=npm&vr=%3E%3D0 + Affected range : >=0 + Fixed version : not fixed + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N + + + 0C 0H 1M 0L notevil 1.3.3 +pkg:npm/notevil@1.3.3 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2021-23771 [Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')] + https://scout.docker.com/v/CVE-2021-23771?s=github&n=notevil&t=npm&vr=%3C%3D1.3.3 + Affected range : <=1.3.3 + Fixed version : not fixed + CVSS Score : 6.5 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N + + + 0C 0H 1M 0L base64url 0.0.6 +pkg:npm/base64url@0.0.6 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM GHSA-rvg8-pwq2-xj7q [Out-of-bounds Read] + https://scout.docker.com/v/GHSA-rvg8-pwq2-xj7q?s=github&n=base64url&t=npm&vr=%3C3.0.0 + Affected range : <3.0.0 + Fixed version : 3.0.0 + + + 0C 0H 1M 0L got 8.3.2 +pkg:npm/got@8.3.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ MEDIUM CVE-2022-33987 + https://scout.docker.com/v/CVE-2022-33987?s=github&n=got&t=npm&vr=%3C11.8.5 + Affected range : <11.8.5 + Fixed version : 11.8.5 + CVSS Score : 5.3 + CVSS Vector : CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N + + + 0C 0H 0M 1L cookie 0.4.2 +pkg:npm/cookie@0.4.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ LOW CVE-2024-47764 [Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')] + https://scout.docker.com/v/CVE-2024-47764?s=github&n=cookie&t=npm&vr=%3C0.7.0 + Affected range : <0.7.0 + Fixed version : 0.7.0 + + + 0C 0H 0M 1L diff 4.0.2 +pkg:npm/diff@4.0.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ LOW CVE-2026-24001 [Inefficient Regular Expression Complexity] + https://scout.docker.com/v/CVE-2026-24001?s=github&n=diff&t=npm&vr=%3E%3D4.0.0%2C%3C4.0.4 + Affected range : >=4.0.0 + : <4.0.4 + Fixed version : 4.0.4 + CVSS Score : 2.7 + CVSS Vector : CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:U + + + 0C 0H 0M 1L @tootallnate/once 1.1.2 +pkg:npm/%40tootallnate/once@1.1.2 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ LOW CVE-2026-3449 [Incorrect Control Flow Scoping] + https://scout.docker.com/v/CVE-2026-3449?s=github&n=once&ns=%40tootallnate&t=npm&vr=%3C3.0.1 + Affected range : <3.0.1 + Fixed version : 3.0.1 + CVSS Score : 1.9 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P + + + 0C 0H 0M 1L @tootallnate/once 2.0.0 +pkg:npm/%40tootallnate/once@2.0.0 + +https://github.com/juice-shop/juice-shop/blob/36870cbbdfe7864698e1adf644c7bf772f67ebb7/Dockerfile#L38-L38 +COPY --from=installer --chown=65532:0 /juice-shop . + + ✗ LOW CVE-2026-3449 [Incorrect Control Flow Scoping] + https://scout.docker.com/v/CVE-2026-3449?s=github&n=once&ns=%40tootallnate&t=npm&vr=%3C3.0.1 + Affected range : <3.0.1 + Fixed version : 3.0.1 + CVSS Score : 1.9 + CVSS Vector : CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P + + + +118 vulnerabilities found in 48 packages + CRITICAL 11 + HIGH 65 + MEDIUM 30 + LOW 5 + UNSPECIFIED 7 + diff --git a/labs/lab7/scanning/snyk-results-amd64.txt b/labs/lab7/scanning/snyk-results-amd64.txt new file mode 100644 index 00000000..388f5334 --- /dev/null +++ b/labs/lab7/scanning/snyk-results-amd64.txt @@ -0,0 +1,80 @@ +Unable to find image 'snyk/snyk:docker' locally +docker: Pulling from snyk/snyk +13647075a508: Pulling fs layer +9df03e33ed49: Pulling fs layer +92fbd819f55b: Pulling fs layer +a524d6d9cccd: Pulling fs layer +8b4da28e0bc4: Pulling fs layer +589002ba0eae: Pulling fs layer +5b446eb8f05b: Pulling fs layer +d3f1c7a58cda: Pulling fs layer +a2730091713c: Pulling fs layer +503f0e2071dd: Pulling fs layer +4f4fb700ef54: Pulling fs layer +7a5cd3b91051: Pulling fs layer +bcdcbaa55207: Pulling fs layer +7ba58ca1e42e: Pulling fs layer +31bd52174687: Pulling fs layer +04a9731c772a: Pulling fs layer +34962ed90623: Pulling fs layer +a4a294ee3d24: Pulling fs layer +6e8d156e10c7: Pulling fs layer +12f8c3243c30: Pulling fs layer +89e9c4ffbcbf: Pulling fs layer +4f4fb700ef54: Already exists +efc96099a71d: Download complete +7ba58ca1e42e: Download complete +8b4da28e0bc4: Download complete +9df03e33ed49: Download complete +5b446eb8f05b: Download complete +a4a294ee3d24: Download complete +04a9731c772a: Download complete +bcdcbaa55207: Download complete +6e8d156e10c7: Download complete +31bd52174687: Download complete +a524d6d9cccd: Download complete +589002ba0eae: Download complete +589002ba0eae: Pull complete +92fbd819f55b: Download complete +7a5cd3b91051: Download complete +13647075a508: Download complete +a2730091713c: Download complete +d3f1c7a58cda: Download complete +4f4fb700ef54: Pull complete +7ba58ca1e42e: Pull complete +d3f1c7a58cda: Pull complete +34962ed90623: Download complete +12f8c3243c30: Download complete +12f8c3243c30: Pull complete +89e9c4ffbcbf: Download complete +89e9c4ffbcbf: Pull complete +9df03e33ed49: Pull complete +a4a294ee3d24: Pull complete +04a9731c772a: Pull complete +34962ed90623: Pull complete +8b4da28e0bc4: Pull complete +a524d6d9cccd: Pull complete +92fbd819f55b: Pull complete +503f0e2071dd: Download complete +5b446eb8f05b: Pull complete +bcdcbaa55207: Pull complete +6e8d156e10c7: Pull complete +31bd52174687: Pull complete +7a5cd3b91051: Pull complete +a2730091713c: Pull complete +503f0e2071dd: Pull complete +13647075a508: Pull complete +Digest: sha256:f7e762e86b24872fc65aa73844198112655a10c8d6f38cb1ef75f563b423c446 +Status: Downloaded newer image for snyk/snyk:docker + + ERROR Authentication error (SNYK-0005) + Authentication credentials not recognized, or user access is not provisioned. + Revise credentials and try again, or request access from your Snyk + administrator. + + Use `snyk auth` to authenticate. + +Status: 401 Unauthorized +Docs: https://docs.snyk.io/scan-with-snyk/error-catalog#snyk-0005 + +ID: urn:snyk:interaction:cd25d665-638f-4965-a023-60cb544cc214 diff --git a/labs/lab7/scanning/snyk-results.txt b/labs/lab7/scanning/snyk-results.txt new file mode 100644 index 00000000..388f5334 --- /dev/null +++ b/labs/lab7/scanning/snyk-results.txt @@ -0,0 +1,80 @@ +Unable to find image 'snyk/snyk:docker' locally +docker: Pulling from snyk/snyk +13647075a508: Pulling fs layer +9df03e33ed49: Pulling fs layer +92fbd819f55b: Pulling fs layer +a524d6d9cccd: Pulling fs layer +8b4da28e0bc4: Pulling fs layer +589002ba0eae: Pulling fs layer +5b446eb8f05b: Pulling fs layer +d3f1c7a58cda: Pulling fs layer +a2730091713c: Pulling fs layer +503f0e2071dd: Pulling fs layer +4f4fb700ef54: Pulling fs layer +7a5cd3b91051: Pulling fs layer +bcdcbaa55207: Pulling fs layer +7ba58ca1e42e: Pulling fs layer +31bd52174687: Pulling fs layer +04a9731c772a: Pulling fs layer +34962ed90623: Pulling fs layer +a4a294ee3d24: Pulling fs layer +6e8d156e10c7: Pulling fs layer +12f8c3243c30: Pulling fs layer +89e9c4ffbcbf: Pulling fs layer +4f4fb700ef54: Already exists +efc96099a71d: Download complete +7ba58ca1e42e: Download complete +8b4da28e0bc4: Download complete +9df03e33ed49: Download complete +5b446eb8f05b: Download complete +a4a294ee3d24: Download complete +04a9731c772a: Download complete +bcdcbaa55207: Download complete +6e8d156e10c7: Download complete +31bd52174687: Download complete +a524d6d9cccd: Download complete +589002ba0eae: Download complete +589002ba0eae: Pull complete +92fbd819f55b: Download complete +7a5cd3b91051: Download complete +13647075a508: Download complete +a2730091713c: Download complete +d3f1c7a58cda: Download complete +4f4fb700ef54: Pull complete +7ba58ca1e42e: Pull complete +d3f1c7a58cda: Pull complete +34962ed90623: Download complete +12f8c3243c30: Download complete +12f8c3243c30: Pull complete +89e9c4ffbcbf: Download complete +89e9c4ffbcbf: Pull complete +9df03e33ed49: Pull complete +a4a294ee3d24: Pull complete +04a9731c772a: Pull complete +34962ed90623: Pull complete +8b4da28e0bc4: Pull complete +a524d6d9cccd: Pull complete +92fbd819f55b: Pull complete +503f0e2071dd: Download complete +5b446eb8f05b: Pull complete +bcdcbaa55207: Pull complete +6e8d156e10c7: Pull complete +31bd52174687: Pull complete +7a5cd3b91051: Pull complete +a2730091713c: Pull complete +503f0e2071dd: Pull complete +13647075a508: Pull complete +Digest: sha256:f7e762e86b24872fc65aa73844198112655a10c8d6f38cb1ef75f563b423c446 +Status: Downloaded newer image for snyk/snyk:docker + + ERROR Authentication error (SNYK-0005) + Authentication credentials not recognized, or user access is not provisioned. + Revise credentials and try again, or request access from your Snyk + administrator. + + Use `snyk auth` to authenticate. + +Status: 401 Unauthorized +Docs: https://docs.snyk.io/scan-with-snyk/error-catalog#snyk-0005 + +ID: urn:snyk:interaction:cd25d665-638f-4965-a023-60cb544cc214 diff --git a/labs/submission7.md b/labs/submission7.md new file mode 100644 index 00000000..9907c6c0 --- /dev/null +++ b/labs/submission7.md @@ -0,0 +1,550 @@ +# Lab 7 Submission - Container Security: Image Scanning & Deployment Hardening + +## Student / Context +- Name: `Danil Fishchenko` +- Branch: `feature/lab7` +- Scan date: `2026-03-23 19:21:21 MSK` +- Repository root: `DevSecOps-Intro/` +- Target image: `bkimminich/juice-shop:v19.0.0` +- Host OS: `macOS` +- Docker platform context: + - Docker Desktop `4.63.0` + - Docker Engine `29.2.1` + - Docker Scout `v1.20.0` + - Engine security options: `seccomp=builtin`, `cgroupns` +- Tools used: + - `docker scout` + - `goodwithtech/dockle:latest` + - `snyk/snyk:docker` + - `docker/docker-bench-security` official scripts + - `docker:cli` as a compatibility wrapper for the official `docker-bench-security` scripts on Docker Desktop + +## Scope And Method +This lab evaluates container security at three layers: + +1. Image layer - known vulnerabilities and image-level security posture. +2. Docker host / daemon layer - CIS benchmark style hardening checks. +3. Runtime layer - how different `docker run` security profiles change blast radius if the app is exploited. + +Important environment notes: + +- The required `Docker Scout` scan worked normally on this host. +- The required `Dockle` scan worked normally on this host. +- The `Snyk` container image did not provide an `arm64` manifest, so I reran it with `--platform linux/amd64`. +- After architecture emulation was fixed, `Snyk` still failed with `401 Unauthorized`, which confirms that a valid `SNYK_TOKEN` or prior `snyk auth` is required in this environment. +- The stock lab command for `docker-bench-security` failed on Docker Desktop because `-v /etc:/etc:ro` conflicted with Docker's own `/etc/hostname` mount, and the old bundled Docker CLI in that image could not talk to the current daemon reliably. To keep the benchmark method intact, I extracted the official benchmark scripts from the image and ran them inside a modern `docker:cli` container with the same host mounts. The working benchmark output is saved as `labs/lab7/hardening/docker-bench-results.txt`, and the stock failure log is preserved separately. +- The CIS benchmark reflects the full local Docker Desktop environment and all running containers at scan time, not only Juice Shop. + +## Task 1 - Image Vulnerability And Configuration Analysis + +### 1.1 Environment Setup +```bash +mkdir -p labs/lab7/scanning labs/lab7/hardening labs/lab7/analysis +docker pull bkimminich/juice-shop:v19.0.0 +``` + +### 1.2 Commands Used +```bash +docker scout cves bkimminich/juice-shop:v19.0.0 > labs/lab7/scanning/scout-cves.txt + +docker run --rm \ + -v /var/run/docker.sock:/var/run/docker.sock \ + goodwithtech/dockle:latest \ + bkimminich/juice-shop:v19.0.0 > labs/lab7/scanning/dockle-results.txt + +# First run from the lab instructions failed because the image has no linux/arm64 manifest. +docker run --rm \ + -e SNYK_TOKEN \ + -v /var/run/docker.sock:/var/run/docker.sock \ + snyk/snyk:docker snyk test --docker bkimminich/juice-shop:v19.0.0 \ + --severity-threshold=high > labs/lab7/scanning/snyk-results.txt 2>&1 + +# Architecture-adjusted rerun to confirm the real blocker. +docker run --rm --platform linux/amd64 \ + -e SNYK_TOKEN \ + -v /var/run/docker.sock:/var/run/docker.sock \ + snyk/snyk:docker snyk test --docker bkimminich/juice-shop:v19.0.0 \ + --severity-threshold=high > labs/lab7/scanning/snyk-results-amd64.txt 2>&1 + +docker image inspect bkimminich/juice-shop:v19.0.0 \ + --format 'User={{json .Config.User}} Healthcheck={{json .Config.Healthcheck}}' +``` + +### 1.3 Docker Scout Overview +`Docker Scout` reported: + +- `11 Critical` +- `65 High` +- `30 Medium` +- `5 Low` +- `7 Unspecified` +- `1004` packages analyzed +- Base image: `gcr.io/distroless/nodejs22-debian12:latest` + +This is a strong example of why "distroless" does not automatically mean "safe". The runtime is slimmer and non-root, but the application dependency tree still contains many severe npm findings. + +### 1.4 Top 5 Critical / High Vulnerabilities + +| CVE / Advisory | Package | Severity | Fixed Version | Why It Matters | +|---|---|---:|---|---| +| `CVE-2026-22709` | `vm2 3.9.17` | Critical | `3.10.2` | Protection mechanism failure in a sandbox library. If the app relies on `vm2` for isolation, a sandbox escape can turn user-controlled code into arbitrary code execution. | +| `CVE-2023-37903` | `vm2 3.9.17` | Critical | `not fixed` | OS command injection in `vm2`. This is especially severe because the dependency exists exactly to contain untrusted code, so failure defeats the trust boundary. | +| `CVE-2019-10744` | `lodash 2.4.2` | Critical | `4.17.12` | Prototype pollution can corrupt application object state and, depending on the code path, lead to privilege bypass, logic abuse, or denial of service. | +| `CVE-2023-46233` | `crypto-js 3.3.0` | Critical | `4.2.0` | Broken or risky cryptographic behavior weakens confidentiality and integrity guarantees. Any feature that depends on this library can inherit insecure crypto assumptions. | +| `CVE-2021-44906` | `minimist 0.2.4` | Critical | `1.2.6` | Another severe object/prototype manipulation issue. Even "small" utility packages can become high-value attack surfaces when they are deeply reused transitively. | + +Additional high-risk observation: + +- The base Node runtime itself (`node 22.18.0`) carries `1 Critical` and `4 High` findings in Scout, with the fixed version reported as `22.22.0`. That means even before touching application code, the image should be rebuilt on a newer Node patch level. + +### 1.5 Dockle Configuration Findings +`Dockle` did not report any `FATAL` or `WARN` findings for this image. That is notable and positive. The output only contained: + +- `INFO CIS-DI-0005`: Docker Content Trust is not enabled. +- `INFO CIS-DI-0006`: no `HEALTHCHECK` instruction in the image. +- `INFO DKL-LI-0003`: unnecessary files present, including `.DS_Store` files in `node_modules`. +- `SKIP DKL-LI-0001`: password file checks could not be evaluated, which is not surprising for a distroless-style image. + +Security interpretation: + +- Missing `HEALTHCHECK` is an operational security issue because unhealthy containers may stay in service longer than they should, which increases mean time to detect failure and can hide exploitation side effects. +- Disabled content trust means image authenticity is not being enforced at pull time. In a stronger supply-chain posture, this would be replaced by signature verification and admission policy checks. +- Unnecessary files are low severity, but they still represent avoidable attack surface and sloppy build hygiene. + +### 1.6 Snyk Comparison Status +The Snyk comparison could not be completed successfully in this environment for two separate, reproducible reasons: + +1. The default `snyk/snyk:docker` image pull failed on `arm64` because no matching manifest was available. +2. The `amd64` rerun under emulation reached the scanner, but then failed with: + - `ERROR Authentication error (SNYK-0005)` + - `Status: 401 Unauthorized` + +Conclusion: + +- The remaining blocker is credentials, not connectivity. +- To complete the Snyk portion fully on this host, I would need a valid `SNYK_TOKEN` or a prior authenticated `snyk auth` session. + +### 1.7 Security Posture Assessment +The image posture is mixed: + +- Positive: + - The image runs as non-root user `65532`. + - The base image is distroless, which reduces some surface area compared with a full distro image. + - `Dockle` found no `FATAL` or `WARN` image-configuration issues. +- Negative: + - `Docker Scout` still reports many serious dependency vulnerabilities. + - The runtime base (`node 22.18.0`) is itself behind on security fixes. + - There is no `HEALTHCHECK`. + - There is no integrity enforcement such as content trust / signature verification at pull time. + +Recommended improvements: + +1. Rebuild on a patched Node 22 image level. +2. Upgrade or replace vulnerable npm dependencies, especially `vm2`, `lodash`, `crypto-js`, `jsonwebtoken`, and `minimist`. +3. Add a `HEALTHCHECK` to make failures visible to the platform. +4. Enable signature verification / trusted image promotion in CI/CD. +5. Generate an SBOM and enforce a "no Critical vulnerabilities" release gate. + +## Task 2 - Docker Host Security Benchmarking + +### 2.1 Commands Used +Stock lab command attempted first: + +```bash +docker run --rm --net host --pid host --userns host --cap-add audit_control \ + -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ + -v /var/lib:/var/lib:ro \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /usr/lib/systemd:/usr/lib/systemd:ro \ + -v /etc:/etc:ro --label docker_bench_security \ + docker/docker-bench-security > labs/lab7/hardening/docker-bench-results-stock-failure.txt 2>&1 +``` + +Observed stock failure on Docker Desktop: + +- read-only `/etc` bind prevented Docker from mounting `/etc/hostname` +- the old CLI bundled in `docker/docker-bench-security` also had daemon-compatibility issues + +Working execution using the official extracted benchmark scripts: + +```bash +docker run --rm --net host --pid host --userns host --cap-add audit_control \ + -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ + -v "$(pwd)/labs/lab7/hardening/docker-bench-src":/src \ + -v /var/lib:/var/lib:ro \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /usr/lib/systemd:/usr/lib/systemd:ro \ + -v /etc:/hostetc:ro \ + docker:cli sh -lc ' + apk add --no-cache iproute2 procps util-linux coreutils >/dev/null && + cp -a /hostetc/. /etc/ 2>/dev/null || true && + cd /src && + sh docker-bench-security.sh + ' > labs/lab7/hardening/docker-bench-results.txt 2>&1 +``` + +### 2.2 Summary Statistics + +| Metric | Count | +|---|---:| +| `PASS` | `28` | +| `WARN` | `30` | +| `FAIL` | `0` | +| `INFO` | `44` | +| `NOTE` | `10` | +| Total checks | `105` | +| Score | `-3` | + +Important interpretation: + +- There were no formal `FAIL` results in this run. +- The host still has many meaningful `WARN` results, so "no FAIL" does not mean "secure". +- A number of `INFO` lines were caused by Docker Desktop abstraction layers, for example missing `/etc/docker/daemon.json` or other Linux host files not exposed in the same way as on a native Linux host. + +### 2.3 High-Value Warning Analysis +I focused on warnings that represent real security risk instead of platform noise. + +#### 1. `2.8 - Enable user namespace support` +Impact: + +- Without user namespaces, container UIDs map more directly to host-root semantics. +- This increases the impact of container breakout or daemon misconfiguration. + +Remediation: + +- Enable user namespace remapping or move to a rootless runtime / rootless Docker where practical. + +#### 2. `2.12 - Ensure centralized and remote logging is configured` +Impact: + +- Local-only logs are easier to tamper with and harder to preserve during incident response. +- Distributed services become difficult to investigate after crashes or compromise. + +Remediation: + +- Send container logs to centralized logging infrastructure such as Loki, Elasticsearch, or a cloud logging service. + +#### 3. `2.18` and `5.25 - Ensure containers are restricted from acquiring new privileges` +Impact: + +- Containers without `no-new-privileges` can still gain privileges through `setuid` binaries or similar exec paths. +- This matters during post-exploitation, especially when an attacker can execute arbitrary binaries inside a compromised container. + +Remediation: + +- Set `--security-opt=no-new-privileges` for standalone containers. +- In Kubernetes, set `allowPrivilegeEscalation: false`. + +#### 4. `5.4 - Ensure privileged containers are not used` +Impact: + +- Privileged containers remove most isolation guarantees and are a classic container-escape risk multiplier. +- In my benchmark run, the active `kind` nodes triggered this warning. + +Remediation: + +- Avoid `--privileged` unless there is a strict infrastructure reason. +- For Kubernetes test clusters, isolate them from normal application workloads and do not reuse the same Docker host for sensitive services. + +#### 5. `5.31 - Ensure the Docker socket is not mounted inside any containers` +Impact: + +- Mounting `/var/run/docker.sock` effectively gives the container control over the Docker daemon. +- That can lead to host compromise through container creation, volume mounts, or image execution. + +Remediation: + +- Remove direct socket mounts from application containers. +- Use scoped APIs, sidecars, or dedicated build runners instead of exposing the daemon socket broadly. + +#### 6. `5.10`, `5.11`, `5.12` - missing resource limits and read-only root filesystem +Impact: + +- Unlimited CPU and memory enable denial-of-service from a single compromised or buggy container. +- Writable root filesystems make persistence and tampering easier. + +Remediation: + +- Set explicit CPU, memory, and PID limits. +- Use `--read-only` plus `tmpfs` / named volumes only where write access is truly required. + +### 2.4 Benchmark Interpretation +The benchmark surfaced real weaknesses in the local Docker estate: + +- missing `no-new-privileges` +- containers with no CPU / memory limits +- writable root filesystems +- privileged containers +- Docker socket mounts +- missing health checks + +These are exactly the kinds of findings that matter in production because they increase blast radius after an exploit. Even though this host is Docker Desktop and not a dedicated Linux server, the benchmark still usefully shows that runtime hardening is inconsistent across the environment. + +## Task 3 - Deployment Security Configuration Analysis + +### 3.1 Commands Used +I first ran the profiles from the lab instructions. The `production` command needed one platform-specific adjustment: + +- the lab uses `--security-opt=seccomp=default` +- on this Docker Engine, that value was interpreted as a file path and failed +- the equivalent working form was `--security-opt=seccomp=builtin` + +Executed profiles: + +```bash +docker run -d --name juice-default -p 3001:3000 \ + bkimminich/juice-shop:v19.0.0 + +docker run -d --name juice-hardened -p 3002:3000 \ + --cap-drop=ALL \ + --security-opt=no-new-privileges \ + --memory=512m \ + --cpus=1.0 \ + bkimminich/juice-shop:v19.0.0 + +docker run -d --name juice-production -p 3003:3000 \ + --cap-drop=ALL \ + --cap-add=NET_BIND_SERVICE \ + --security-opt=no-new-privileges \ + --security-opt=seccomp=builtin \ + --memory=512m \ + --memory-swap=512m \ + --cpus=1.0 \ + --pids-limit=100 \ + --restart=on-failure:3 \ + bkimminich/juice-shop:v19.0.0 +``` + +Evidence collection: + +```bash +curl -s -o /dev/null -w 'Default: HTTP %{http_code}\n' http://localhost:3001 +curl -s -o /dev/null -w 'Hardened: HTTP %{http_code}\n' http://localhost:3002 +curl -s -o /dev/null -w 'Production: HTTP %{http_code}\n' http://localhost:3003 + +docker stats --no-stream --format 'table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}' \ + juice-default juice-hardened juice-production + +docker inspect --format '...' +``` + +### 3.2 Functional Verification +All three profiles stayed functional: + +| Profile | HTTP Result | Observation | +|---|---:|---| +| `default` | `200` | App worked with no runtime restrictions. | +| `hardened` | `200` | Basic security restrictions did not break the app. | +| `production` | `200` | Stronger hardening still preserved normal service behavior. | + +Observed resource snapshot: + +| Container | CPU | Memory Usage | +|---|---:|---:| +| `juice-default` | `0.64%` | `156.6 MiB / 5.786 GiB` | +| `juice-hardened` | `0.63%` | `105.8 MiB / 512 MiB` | +| `juice-production` | `0.59%` | `92.55 MiB / 512 MiB` | + +### 3.3 Configuration Comparison Table + +| Setting | `default` | `hardened` | `production` | +|---|---|---|---| +| Image user | `65532` | `65532` | `65532` | +| `CapDrop` | `null` | `["ALL"]` | `["ALL"]` | +| `CapAdd` | `null` | `null` | `["CAP_NET_BIND_SERVICE"]` | +| `SecurityOpt` | `null` | `["no-new-privileges"]` | `["no-new-privileges","seccomp=builtin"]` | +| Memory | unlimited | `512 MiB` | `512 MiB` | +| Memory swap | unlimited | `1 GiB` effective default | `512 MiB` | +| CPU | unlimited | `1 CPU` | `1 CPU` | +| PID limit | none | none | `100` | +| Restart policy | `no` | `no` | `on-failure:3` | +| Read-only rootfs | `false` | `false` | `false` | + +### 3.4 Security Measure Analysis + +#### a) `--cap-drop=ALL` and `--cap-add=NET_BIND_SERVICE` +Linux capabilities split "root-like" powers into smaller privileges such as raw networking, mount operations, process tracing, and binding low ports. + +Why drop all capabilities: + +- It minimizes what a compromised process can do against the kernel and surrounding system. +- This reduces post-exploitation options such as packet capture, raw socket abuse, or other privileged kernel interactions. + +Why add back `NET_BIND_SERVICE`: + +- In general, it is only needed when an application must bind to ports below `1024`. +- In this specific Juice Shop setup the app listens on `3000`, so the capability is not functionally required here. In this lab it mostly demonstrates the principle "remove everything, add back only what is actually necessary". + +Security trade-off: + +- Fewer capabilities improve isolation. +- Adding back a capability should be justified by a real runtime requirement, otherwise it is unnecessary privilege. + +#### b) `--security-opt=no-new-privileges` +This flag prevents the process and its children from gaining extra privileges during execution, including via `setuid` / `setgid` transitions. + +What attack it helps prevent: + +- Privilege escalation after code execution inside the container. +- It limits damage from malicious binaries or abused helper tools that would otherwise elevate privilege on exec. + +Downsides: + +- Some legacy software that expects `setuid` behavior can break. +- It may surface hidden assumptions in older images or admin/debug workflows. + +#### c) `--memory=512m` and `--cpus=1.0` +Without resource limits: + +- one container can starve the host +- a memory leak can trigger OOM conditions +- a malicious process can create noisy-neighbor denial of service + +Memory limiting specifically helps contain: + +- intentional heap exhaustion +- accidental leaks +- abuse patterns that try to crash or destabilize the node + +Risk of setting limits too low: + +- legitimate requests can fail +- the app can restart repeatedly +- latency may spike under normal load + +The goal is not "lowest possible limit", but "measured limit plus safety margin". + +#### d) `--pids-limit=100` +A fork bomb is a process-spawning loop that rapidly consumes all available process IDs and scheduler capacity. + +Why PID limiting helps: + +- it caps process explosion inside the container +- it protects the host and neighboring workloads from process-table exhaustion + +How to choose the value: + +- measure normal steady-state process and thread count +- include expected burst headroom +- keep the cap tight enough to stop abuse but high enough not to break normal runtime behavior + +#### e) `--restart=on-failure:3` +This policy restarts the container only after a non-zero exit, and only up to three times. + +When auto-restart is useful: + +- transient crashes +- timing issues during dependency startup +- short-lived infrastructure hiccups + +When it is risky: + +- crash loops can hide persistent bugs +- repeated retries can amplify load or repeat unsafe side effects + +`on-failure` vs `always`: + +- `on-failure` is narrower and usually safer for application workloads because it only reacts to abnormal exit. +- `always` also restarts cleanly stopped containers, which can be undesirable during debugging, maintenance, or failure analysis. + +### 3.5 Critical Thinking Questions + +#### 1. Which profile for development? +I would choose `hardened` for development. + +Reason: + +- It kept the app fully functional (`HTTP 200`). +- It already enforces least privilege and basic resource limits. +- It is less likely than the production profile to interfere with developer workflows during debugging. + +I would not choose `default` unless I specifically needed to reproduce a compatibility issue caused by hardening. + +#### 2. Which profile for production? +I would choose `production`. + +Reason: + +- It preserves availability (`HTTP 200`). +- It drops all capabilities and only re-adds a narrow one. +- It enforces `no-new-privileges`. +- It limits CPU, memory, swap, and PIDs. +- It adds bounded restart behavior. +- It explicitly pins the built-in seccomp profile on this engine. + +#### 3. What real-world problem do resource limits solve? +They solve multi-tenant stability and denial-of-service risk. + +In real platforms, the common failure mode is not only "the app is hacked", but also: + +- one bad release leaks memory +- one service pegs CPU +- one compromised workload deliberately exhausts host resources + +Limits keep a single container from taking down the whole node. + +#### 4. If an attacker exploits Default vs Production, what actions are blocked in Production? +Compared with `default`, an exploited process in `production` is constrained in several ways: + +- it cannot rely on the default capability set because all capabilities are dropped first +- it cannot gain new privileges through exec transitions because of `no-new-privileges` +- it cannot consume unlimited memory or CPU +- it cannot spawn unlimited processes because of `--pids-limit=100` +- it is subject to the built-in seccomp policy explicitly +- restart behavior is bounded instead of uncontrolled + +Important nuance: + +- All three profiles still run as image user `65532`, so the image was already non-root before runtime hardening. +- The big difference is not "root vs non-root", but "unbounded default runtime vs explicitly constrained runtime". + +#### 5. What additional hardening would I add? +I would add: + +1. `--read-only` root filesystem. +2. `--tmpfs /tmp` or dedicated writable mounts for only the paths the app needs. +3. Explicit interface binding if public exposure is not required. +4. Custom AppArmor / SELinux policy where the platform supports it. +5. Image pinning by digest plus signature verification. +6. Health checks in the image. +7. Rootless container runtime where possible. +8. Network segmentation / firewalling so the container only reaches what it actually needs. + +## Challenges Encountered + +### 1. Snyk architecture and authentication blockers +- `snyk/snyk:docker` had no native `arm64` manifest. +- After switching to `--platform linux/amd64`, the scan reached Snyk but failed with `401 Unauthorized`. +- This is a real credentials issue, not a Docker networking issue. + +### 2. `docker-bench-security` on Docker Desktop +- The stock lab command failed because a read-only bind of `/etc` blocked Docker's own hostname mount. +- The old Docker CLI bundled inside `docker/docker-bench-security` was also a poor fit for the modern Docker Desktop daemon. +- Rehosting the official scripts inside `docker:cli` preserved the benchmark logic while making it runnable on this machine. + +### 3. `seccomp=default` vs `seccomp=builtin` +- The lab instruction uses `seccomp=default`. +- On this engine, the accepted built-in profile identifier is `builtin`. +- The working production run therefore used `seccomp=builtin`, which is the local equivalent of "use the default built-in seccomp profile". + +## Evidence Files +- `labs/lab7/scanning/scout-cves.txt` +- `labs/lab7/scanning/dockle-results.txt` +- `labs/lab7/scanning/snyk-results.txt` +- `labs/lab7/scanning/snyk-results-amd64.txt` +- `labs/lab7/hardening/docker-bench-results.txt` +- `labs/lab7/hardening/docker-bench-results-stock-failure.txt` +- `labs/lab7/hardening/docker-bench-results-adapted.txt` +- `labs/lab7/hardening/docker-bench-results-workaround.txt` +- `labs/lab7/hardening/docker-bench-results-rehosted.txt` +- `labs/lab7/hardening/docker-bench-src/` +- `labs/lab7/analysis/deployment-comparison.txt` + +## Final Conclusion +This lab demonstrates a realistic DevSecOps lesson: + +- A container image can be non-root and still be highly vulnerable because of outdated dependencies. +- A host can look "mostly fine" but still expose dangerous runtime patterns such as privileged containers, Docker socket mounts, and missing `no-new-privileges`. +- Runtime hardening matters because it constrains blast radius even when the application itself is compromised. + +The strongest practical takeaway is that container security is layered. Image scanning, daemon / host hardening, and runtime restrictions each solve different parts of the problem, and none of them is enough on its own. From 598b34868f5903a757d601072c516ad5788c6267 Mon Sep 17 00:00:00 2001 From: pepegx Date: Mon, 23 Mar 2026 19:46:51 +0300 Subject: [PATCH 3/4] docs(task1): correct Snyk evidence and blocker analysis --- labs/lab7/scanning/snyk-manifest-inspect.txt | 24 ++++++++++++++ labs/submission7.md | 34 +++++++++++--------- 2 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 labs/lab7/scanning/snyk-manifest-inspect.txt diff --git a/labs/lab7/scanning/snyk-manifest-inspect.txt b/labs/lab7/scanning/snyk-manifest-inspect.txt new file mode 100644 index 00000000..c8e01b28 --- /dev/null +++ b/labs/lab7/scanning/snyk-manifest-inspect.txt @@ -0,0 +1,24 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 4288, + "digest": "sha256:ee546fc8c002ee2a0cabb30552e31ccb17d49ee2f621e8d8a302dcae470f913d", + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 565, + "digest": "sha256:4d4947f896f4e9a651de387b6dc861dd765249e6f418bbe1f2c3663bf2e3f366", + "platform": { + "architecture": "unknown", + "os": "unknown" + } + } + ] +} diff --git a/labs/submission7.md b/labs/submission7.md index 9907c6c0..7dd4a3fc 100644 --- a/labs/submission7.md +++ b/labs/submission7.md @@ -30,8 +30,9 @@ Important environment notes: - The required `Docker Scout` scan worked normally on this host. - The required `Dockle` scan worked normally on this host. -- The `Snyk` container image did not provide an `arm64` manifest, so I reran it with `--platform linux/amd64`. -- After architecture emulation was fixed, `Snyk` still failed with `401 Unauthorized`, which confirms that a valid `SNYK_TOKEN` or prior `snyk auth` is required in this environment. +- `docker manifest inspect snyk/snyk:docker` showed a `linux/amd64` manifest but no native `linux/arm64` variant, so the preserved Snyk scan was rerun with `--platform linux/amd64`. +- The saved Snyk scan artifacts `labs/lab7/scanning/snyk-results.txt` and `labs/lab7/scanning/snyk-results-amd64.txt` are identical and both show the real blocker: `401 Unauthorized`. +- That means platform compatibility had to be handled separately, but the actual reason Task 1.3 could not be completed is missing Snyk authentication. - The stock lab command for `docker-bench-security` failed on Docker Desktop because `-v /etc:/etc:ro` conflicted with Docker's own `/etc/hostname` mount, and the old bundled Docker CLI in that image could not talk to the current daemon reliably. To keep the benchmark method intact, I extracted the official benchmark scripts from the image and ran them inside a modern `docker:cli` container with the same host mounts. The working benchmark output is saved as `labs/lab7/hardening/docker-bench-results.txt`, and the stock failure log is preserved separately. - The CIS benchmark reflects the full local Docker Desktop environment and all running containers at scan time, not only Juice Shop. @@ -52,19 +53,17 @@ docker run --rm \ goodwithtech/dockle:latest \ bkimminich/juice-shop:v19.0.0 > labs/lab7/scanning/dockle-results.txt -# First run from the lab instructions failed because the image has no linux/arm64 manifest. -docker run --rm \ - -e SNYK_TOKEN \ - -v /var/run/docker.sock:/var/run/docker.sock \ - snyk/snyk:docker snyk test --docker bkimminich/juice-shop:v19.0.0 \ - --severity-threshold=high > labs/lab7/scanning/snyk-results.txt 2>&1 +docker manifest inspect snyk/snyk:docker > labs/lab7/scanning/snyk-manifest-inspect.txt -# Architecture-adjusted rerun to confirm the real blocker. +# Final preserved Snyk attempt, executed with amd64 emulation because the manifest list +# does not expose a native linux/arm64 image. docker run --rm --platform linux/amd64 \ -e SNYK_TOKEN \ -v /var/run/docker.sock:/var/run/docker.sock \ snyk/snyk:docker snyk test --docker bkimminich/juice-shop:v19.0.0 \ - --severity-threshold=high > labs/lab7/scanning/snyk-results-amd64.txt 2>&1 + --severity-threshold=high > labs/lab7/scanning/snyk-results.txt 2>&1 + +cp labs/lab7/scanning/snyk-results.txt labs/lab7/scanning/snyk-results-amd64.txt docker image inspect bkimminich/juice-shop:v19.0.0 \ --format 'User={{json .Config.User}} Healthcheck={{json .Config.Healthcheck}}' @@ -112,17 +111,21 @@ Security interpretation: - Unnecessary files are low severity, but they still represent avoidable attack surface and sloppy build hygiene. ### 1.6 Snyk Comparison Status -The Snyk comparison could not be completed successfully in this environment for two separate, reproducible reasons: +The Snyk comparison could not be completed successfully in this environment, and the preserved evidence shows exactly where it stopped. + +What is verifiably present in the artifacts: -1. The default `snyk/snyk:docker` image pull failed on `arm64` because no matching manifest was available. -2. The `amd64` rerun under emulation reached the scanner, but then failed with: +1. `labs/lab7/scanning/snyk-manifest-inspect.txt` shows a `linux/amd64` manifest but no native `linux/arm64` manifest for `snyk/snyk:docker`. +2. `labs/lab7/scanning/snyk-results.txt` and `labs/lab7/scanning/snyk-results-amd64.txt` are identical. +3. Both saved Snyk scan outputs reach the scanner and then fail with: - `ERROR Authentication error (SNYK-0005)` - `Status: 401 Unauthorized` Conclusion: -- The remaining blocker is credentials, not connectivity. -- To complete the Snyk portion fully on this host, I would need a valid `SNYK_TOKEN` or a prior authenticated `snyk auth` session. +- Platform compatibility is a separate concern, but it is not the final blocker in the preserved scan outputs. +- The actual blocker to completing Task 1.3 is missing credentials. +- To finish the Snyk comparison fully on this host, I would need a valid `SNYK_TOKEN` or a prior authenticated `snyk auth` session. ### 1.7 Security Posture Assessment The image posture is mixed: @@ -530,6 +533,7 @@ I would add: ## Evidence Files - `labs/lab7/scanning/scout-cves.txt` - `labs/lab7/scanning/dockle-results.txt` +- `labs/lab7/scanning/snyk-manifest-inspect.txt` - `labs/lab7/scanning/snyk-results.txt` - `labs/lab7/scanning/snyk-results-amd64.txt` - `labs/lab7/hardening/docker-bench-results.txt` From 6ad8c0e0b5d0eb7906c62a5638defc3db2478779 Mon Sep 17 00:00:00 2001 From: pepegx Date: Mon, 23 Mar 2026 20:58:18 +0300 Subject: [PATCH 4/4] docs(task2): rerun docker bench with helper exclusion label --- labs/lab7/hardening/docker-bench-results.txt | 42 +- .../docker-bench-security.sh.log | 913 ++++++++++++++++++ .../docker-bench-security.sh.log.json | 40 +- labs/submission7.md | 29 +- 4 files changed, 971 insertions(+), 53 deletions(-) diff --git a/labs/lab7/hardening/docker-bench-results.txt b/labs/lab7/hardening/docker-bench-results.txt index bb3ccb5f..4877364a 100644 --- a/labs/lab7/hardening/docker-bench-results.txt +++ b/labs/lab7/hardening/docker-bench-results.txt @@ -7,7 +7,7 @@ # Inspired by the CIS Docker Community Edition Benchmark v1.1.0. # ------------------------------------------------------------------------------ -Initializing Mon Mar 23 16:12:56 UTC 2026 +Initializing Mon Mar 23 17:47:04 UTC 2026 [INFO] 1 - Host Configuration @@ -103,7 +103,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [INFO] 4 - Container Images and Build File [WARN] 4.1 - Ensure a user for the container has been created -[WARN] * Running as root: elegant_sammet [WARN] * Running as root: devops-lab9-control-plane [WARN] * Running as root: devops-lab9-worker [WARN] * Running as root: promtail @@ -112,12 +111,13 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches [WARN] 4.5 - Ensure Content trust for Docker is Enabled [WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image -[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [snyk/snyk:docker] [WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] -[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] [WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] [WARN] * No Healthcheck found: [devops-info-service:lab07] [WARN] * No Healthcheck found: [devops-info-service-go:lab07] [WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] @@ -129,6 +129,7 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * No Healthcheck found: [checkmarx/kics:latest] [WARN] * No Healthcheck found: [semgrep/semgrep:latest] [WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [alpine:3.22] [WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] [WARN] * No Healthcheck found: [grafana/grafana:12.3.1] [WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] @@ -141,9 +142,10 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * No Healthcheck found: [grafana/loki:3.0.0] [WARN] * No Healthcheck found: [registry:2] [INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile -[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [snyk/snyk:docker] [INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] [INFO] * Update instruction found: [devops-info-service:lab07] [INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] [INFO] * Update instruction found: [bridgecrew/checkov:latest] @@ -153,11 +155,13 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [INFO] * Update instruction found: [grafana/promtail:3.0.0] [NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images [INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [snyk/snyk:docker] [INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] [INFO] * ADD in image history: [docker:cli] [INFO] * ADD in image history: [projectdiscovery/nuclei:latest] [INFO] * ADD in image history: [semgrep/semgrep:latest] [INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [alpine:3.22] [INFO] * ADD in image history: [grafana/grafana:12.3.1] [INFO] * ADD in image history: [aquasec/tfsec:latest] [INFO] * ADD in image history: [sh1co/wikifet_arm:latest] @@ -172,7 +176,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [INFO] 5 - Container Runtime [WARN] 5.1 - Ensure AppArmor Profile is Enabled -[WARN] * No AppArmorProfile Found: elegant_sammet [WARN] * No AppArmorProfile Found: devops-lab9-control-plane [WARN] * No AppArmorProfile Found: devops-lab9-worker [WARN] * No AppArmorProfile Found: prometheus @@ -188,8 +191,7 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * No SecurityOptions Found: devops-go [WARN] * No SecurityOptions Found: devops-python [WARN] * No SecurityOptions Found: loki -[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers -[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to elegant_sammet +[PASS] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers [WARN] 5.4 - Ensure privileged containers are not used [WARN] * Container running in Privileged mode: devops-lab9-control-plane [WARN] * Container running in Privileged mode: devops-lab9-worker @@ -199,14 +201,11 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * Privileged Port in use: 80 in devops-lab9-control-plane [WARN] * Privileged Port in use: 443 in devops-lab9-control-plane [NOTE] 5.8 - Ensure only needed ports are open on the container -[WARN] 5.9 - Ensure the host's network namespace is not shared -[WARN] * Container running with networking mode 'host': elegant_sammet +[PASS] 5.9 - Ensure the host's network namespace is not shared [WARN] 5.10 - Ensure memory usage for container is limited -[WARN] * Container running without memory restrictions: elegant_sammet [WARN] * Container running without memory restrictions: devops-lab9-control-plane [WARN] * Container running without memory restrictions: devops-lab9-worker [WARN] 5.11 - Ensure CPU priority is set appropriately on the container -[WARN] * Container running without CPU restrictions: elegant_sammet [WARN] * Container running without CPU restrictions: devops-lab9-control-plane [WARN] * Container running without CPU restrictions: devops-lab9-worker [WARN] * Container running without CPU restrictions: prometheus @@ -216,7 +215,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * Container running without CPU restrictions: devops-python [WARN] * Container running without CPU restrictions: loki [WARN] 5.12 - Ensure the container's root filesystem is mounted as read only -[WARN] * Container running with root FS mounted R/W: elegant_sammet [WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane [WARN] * Container running with root FS mounted R/W: devops-lab9-worker [WARN] * Container running with root FS mounted R/W: prometheus @@ -235,7 +233,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python [WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki [WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' -[WARN] * MaximumRetryCount is not set to 5: elegant_sammet [WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane [WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker [WARN] * MaximumRetryCount is not set to 5: prometheus @@ -244,12 +241,10 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * MaximumRetryCount is not set to 5: devops-go [WARN] * MaximumRetryCount is not set to 5: devops-python [WARN] * MaximumRetryCount is not set to 5: loki -[WARN] 5.15 - Ensure the host's process namespace is not shared -[WARN] * Host PID namespace being shared with: elegant_sammet +[PASS] 5.15 - Ensure the host's process namespace is not shared [PASS] 5.16 - Ensure the host's IPC namespace is not shared [PASS] 5.17 - Ensure host devices are not directly exposed to containers [INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed -[INFO] * Container no default ulimit override: elegant_sammet [INFO] * Container no default ulimit override: devops-lab9-control-plane [INFO] * Container no default ulimit override: devops-lab9-worker [INFO] * Container no default ulimit override: prometheus @@ -267,7 +262,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [NOTE] 5.23 - Ensure docker exec commands are not used with user option [PASS] 5.24 - Ensure cgroup usage is confirmed [WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges -[WARN] * Privileges not restricted: elegant_sammet [WARN] * Privileges not restricted: devops-lab9-control-plane [WARN] * Privileges not restricted: devops-lab9-worker [WARN] * Privileges not restricted: prometheus @@ -277,7 +271,6 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [WARN] * Privileges not restricted: devops-python [WARN] * Privileges not restricted: loki [WARN] 5.26 - Ensure container health is checked at runtime -[WARN] * Health check not set: elegant_sammet [WARN] * Health check not set: devops-lab9-control-plane [WARN] * Health check not set: devops-lab9-worker [INFO] 5.27 - Ensure docker commands always get the latest version of the image @@ -289,20 +282,17 @@ sh: : out of range sh: : out of range sh: : out of range sh: : out of range -sh: : out of range [PASS] 5.28 - Ensure PIDs cgroup limit is used [PASS] 5.29 - Ensure Docker's default bridge docker0 is not used -[WARN] 5.30 - Ensure the host's user namespaces is not shared -[WARN] * Namespace shared: elegant_sammet +[PASS] 5.30 - Ensure the host's user namespaces is not shared [WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers -[WARN] * Docker socket shared: elegant_sammet [WARN] * Docker socket shared: promtail [INFO] 6 - Docker Security Operations [INFO] 6.1 - Avoid image sprawl -[INFO] * There are currently: 28 images -[INFO] * Only 12 out of 28 are in use +[INFO] * There are currently: 30 images +[INFO] * Only 12 out of 30 are in use [INFO] 6.2 - Avoid container sprawl [INFO] * There are currently a total of 12 containers, with 9 of them currently running @@ -320,4 +310,4 @@ sh: : out of range [PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) [INFO] Checks: 105 -[INFO] Score: -3 +[INFO] Score: 4 diff --git a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log index b9c8be08..39eb0cff 100644 --- a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log +++ b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log @@ -302,3 +302,916 @@ Initializing Mon Mar 23 16:12:56 UTC 2026 [PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) [INFO] Checks: 105 [INFO] Score: -3 +Initializing Mon Mar 23 16:39:12 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: frosty_villani +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [snyk/snyk:docker] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [alpine:3.22] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [snyk/snyk:docker] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [snyk/snyk:docker] +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [alpine:3.22] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: frosty_villani +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to frosty_villani +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[WARN] 5.9 - Ensure the host's network namespace is not shared +[WARN] * Container running with networking mode 'host': frosty_villani +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: frosty_villani +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: frosty_villani +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: frosty_villani +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: frosty_villani +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[WARN] 5.15 - Ensure the host's process namespace is not shared +[WARN] * Host PID namespace being shared with: frosty_villani +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: frosty_villani +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: frosty_villani +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: frosty_villani +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[WARN] 5.30 - Ensure the host's user namespaces is not shared +[WARN] * Namespace shared: frosty_villani +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: frosty_villani +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 30 images +[INFO] * Only 12 out of 30 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) +[INFO] Checks: 105 +[INFO] Score: -3 +Initializing Mon Mar 23 16:52:13 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: naughty_tesla +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [snyk/snyk:docker] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [alpine:3.22] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [snyk/snyk:docker] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [snyk/snyk:docker] +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [alpine:3.22] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: naughty_tesla +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[WARN] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] * Capabilities added: CapAdd=[CAP_AUDIT_CONTROL] to naughty_tesla +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[WARN] 5.9 - Ensure the host's network namespace is not shared +[WARN] * Container running with networking mode 'host': naughty_tesla +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: naughty_tesla +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: naughty_tesla +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: naughty_tesla +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: naughty_tesla +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[WARN] 5.15 - Ensure the host's process namespace is not shared +[WARN] * Host PID namespace being shared with: naughty_tesla +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: naughty_tesla +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: naughty_tesla +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: naughty_tesla +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[WARN] 5.30 - Ensure the host's user namespaces is not shared +[WARN] * Namespace shared: naughty_tesla +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: naughty_tesla +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 30 images +[INFO] * Only 12 out of 30 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) +[INFO] Checks: 105 +[INFO] Score: -3 +Initializing Mon Mar 23 17:47:04 UTC 2026 + + +[INFO] 1 - Host Configuration +[WARN] 1.1 - Ensure a separate partition for containers has been created +[NOTE] 1.2 - Ensure the container host has been Hardened +[PASS] 1.3 - Ensure Docker is up to date +[INFO] * Using 29.2.1 which is current +[INFO] * Check with your operating system vendor for support and security maintenance for Docker +[INFO] 1.4 - Ensure only trusted users are allowed to control Docker daemon +[WARN] 1.5 - Ensure auditing is configured for the Docker daemon +[INFO] 1.6 - Ensure auditing is configured for Docker files and directories - /var/lib/docker +[INFO] * Directory not found +[INFO] 1.7 - Ensure auditing is configured for Docker files and directories - /etc/docker +[INFO] * Directory not found +[INFO] 1.8 - Ensure auditing is configured for Docker files and directories - docker.service +[INFO] * File not found +[INFO] 1.9 - Ensure auditing is configured for Docker files and directories - docker.socket +[INFO] * File not found +[INFO] 1.10 - Ensure auditing is configured for Docker files and directories - /etc/default/docker +[INFO] * File not found +[INFO] 1.11 - Ensure auditing is configured for Docker files and directories - /etc/docker/daemon.json +[INFO] * File not found +[INFO] 1.12 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-containerd +[INFO] * File not found +[INFO] 1.13 - Ensure auditing is configured for Docker files and directories - /usr/bin/docker-runc +[INFO] * File not found + + +[INFO] 2 - Docker daemon configuration +[WARN] 2.1 - Ensure network traffic is restricted between containers on the default bridge +[PASS] 2.2 - Ensure the logging level is set to 'info' +[PASS] 2.3 - Ensure Docker is allowed to make changes to iptables +[PASS] 2.4 - Ensure insecure registries are not used +[PASS] 2.5 - Ensure aufs storage driver is not used +[INFO] 2.6 - Ensure TLS authentication for Docker daemon is configured +[INFO] * Docker daemon not listening on TCP +[INFO] 2.7 - Ensure the default ulimit is configured appropriately +[INFO] * Default ulimit doesn't appear to be set +[WARN] 2.8 - Enable user namespace support +[PASS] 2.9 - Ensure the default cgroup usage has been confirmed +[PASS] 2.10 - Ensure base device size is not changed until needed +[WARN] 2.11 - Ensure that authorization for Docker client commands is enabled +[WARN] 2.12 - Ensure centralized and remote logging is configured +[INFO] 2.13 - Ensure operations on legacy registry (v1) are Disabled (Deprecated) +[WARN] 2.14 - Ensure live restore is Enabled +[WARN] 2.15 - Ensure Userland Proxy is Disabled +[INFO] 2.16 - Ensure daemon-wide custom seccomp profile is applied, if needed +[PASS] 2.17 - Ensure experimental features are avoided in production +[WARN] 2.18 - Ensure containers are restricted from acquiring new privileges + + +[INFO] 3 - Docker daemon configuration files +[INFO] 3.1 - Ensure that docker.service file ownership is set to root:root +[INFO] * File not found +[INFO] 3.2 - Ensure that docker.service file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.3 - Ensure that docker.socket file ownership is set to root:root +[INFO] * File not found +[INFO] 3.4 - Ensure that docker.socket file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.5 - Ensure that /etc/docker directory ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.6 - Ensure that /etc/docker directory permissions are set to 755 or more restrictive +[INFO] * Directory not found +[INFO] 3.7 - Ensure that registry certificate file ownership is set to root:root +[INFO] * Directory not found +[INFO] 3.8 - Ensure that registry certificate file permissions are set to 444 or more restrictive +[INFO] * Directory not found +[INFO] 3.9 - Ensure that TLS CA certificate file ownership is set to root:root +[INFO] * No TLS CA certificate found +[INFO] 3.10 - Ensure that TLS CA certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS CA certificate found +[INFO] 3.11 - Ensure that Docker server certificate file ownership is set to root:root +[INFO] * No TLS Server certificate found +[INFO] 3.12 - Ensure that Docker server certificate file permissions are set to 444 or more restrictive +[INFO] * No TLS Server certificate found +[INFO] 3.13 - Ensure that Docker server certificate key file ownership is set to root:root +[INFO] * No TLS Key found +[INFO] 3.14 - Ensure that Docker server certificate key file permissions are set to 400 +[INFO] * No TLS Key found +[WARN] 3.15 - Ensure that Docker socket file ownership is set to root:docker +[WARN] * Wrong ownership for /var/run/docker.sock +[PASS] 3.16 - Ensure that Docker socket file permissions are set to 660 or more restrictive +[INFO] 3.17 - Ensure that daemon.json file ownership is set to root:root +[INFO] * File not found +[INFO] 3.18 - Ensure that daemon.json file permissions are set to 644 or more restrictive +[INFO] * File not found +[INFO] 3.19 - Ensure that /etc/default/docker file ownership is set to root:root +[INFO] * File not found +[INFO] 3.20 - Ensure that /etc/default/docker file permissions are set to 644 or more restrictive +[INFO] * File not found + + +[INFO] 4 - Container Images and Build File +[WARN] 4.1 - Ensure a user for the container has been created +[WARN] * Running as root: devops-lab9-control-plane +[WARN] * Running as root: devops-lab9-worker +[WARN] * Running as root: promtail +[NOTE] 4.2 - Ensure that containers use trusted base images +[NOTE] 4.3 - Ensure unnecessary packages are not installed in the container +[NOTE] 4.4 - Ensure images are scanned and rebuilt to include security patches +[WARN] 4.5 - Ensure Content trust for Docker is Enabled +[WARN] 4.6 - Ensure HEALTHCHECK instructions have been added to the container image +[WARN] * No Healthcheck found: [snyk/snyk:docker] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [pepegx/devops-info-service:lab02] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [devops-info-go:lab02] +[WARN] * No Healthcheck found: [devops-info-service:lab07] +[WARN] * No Healthcheck found: [devops-info-service-go:lab07] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service:latest] +[WARN] * No Healthcheck found: [host.docker.internal:5001/devops-info-service-go:latest] +[WARN] * No Healthcheck found: [bridgecrew/checkov:latest] +[WARN] * No Healthcheck found: [geerlingguy/docker-ubuntu2404-ansible:latest] +[WARN] * No Healthcheck found: [docker:cli] +[WARN] * No Healthcheck found: [projectdiscovery/nuclei:latest] +[WARN] * No Healthcheck found: [checkmarx/kics:latest] +[WARN] * No Healthcheck found: [semgrep/semgrep:latest] +[WARN] * No Healthcheck found: [ghcr.io/sullo/nikto:latest] +[WARN] * No Healthcheck found: [alpine:3.22] +[WARN] * No Healthcheck found: [prom/prometheus:v3.9.0] +[WARN] * No Healthcheck found: [grafana/grafana:12.3.1] +[WARN] * No Healthcheck found: [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] +[WARN] * No Healthcheck found: [bkimminich/juice-shop:v19.0.0] +[WARN] * No Healthcheck found: [aquasec/tfsec:latest] +[WARN] * No Healthcheck found: [sh1co/wikifet_arm:latest] +[WARN] * No Healthcheck found: [goodwithtech/dockle:latest] +[WARN] * No Healthcheck found: [tenable/terrascan:latest] +[WARN] * No Healthcheck found: [grafana/promtail:3.0.0] +[WARN] * No Healthcheck found: [grafana/loki:3.0.0] +[WARN] * No Healthcheck found: [registry:2] +[INFO] 4.7 - Ensure update instructions are not use alone in the Dockerfile +[INFO] * Update instruction found: [snyk/snyk:docker] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [devops-info-service:lab08 localhost:5001/devops-info-service:latest] +[INFO] * Update instruction found: [pepegx/devops-info-service:lab02] +[INFO] * Update instruction found: [devops-info-service:lab07] +[INFO] * Update instruction found: [host.docker.internal:5001/devops-info-service:latest] +[INFO] * Update instruction found: [bridgecrew/checkov:latest] +[INFO] * Update instruction found: [checkmarx/kics:latest] +[INFO] * Update instruction found: [ghcr.io/sullo/nikto:latest] +[INFO] * Update instruction found: [sh1co/wikifet_arm:latest] +[INFO] * Update instruction found: [grafana/promtail:3.0.0] +[NOTE] 4.8 - Ensure setuid and setgid permissions are removed in the images +[INFO] 4.9 - Ensure COPY is used instead of ADD in Dockerfile +[INFO] * ADD in image history: [snyk/snyk:docker] +[INFO] * ADD in image history: [geerlingguy/docker-ubuntu2404-ansible:latest] +[INFO] * ADD in image history: [docker:cli] +[INFO] * ADD in image history: [projectdiscovery/nuclei:latest] +[INFO] * ADD in image history: [semgrep/semgrep:latest] +[INFO] * ADD in image history: [ghcr.io/sullo/nikto:latest] +[INFO] * ADD in image history: [alpine:3.22] +[INFO] * ADD in image history: [grafana/grafana:12.3.1] +[INFO] * ADD in image history: [aquasec/tfsec:latest] +[INFO] * ADD in image history: [sh1co/wikifet_arm:latest] +[INFO] * ADD in image history: [goodwithtech/dockle:latest] +[INFO] * ADD in image history: [grafana/promtail:3.0.0] +[INFO] * ADD in image history: [grafana/loki:3.0.0] +[INFO] * ADD in image history: [registry:2] +[INFO] * ADD in image history: [docker/docker-bench-security:latest] +[NOTE] 4.10 - Ensure secrets are not stored in Dockerfiles +[NOTE] 4.11 - Ensure verified packages are only Installed + + +[INFO] 5 - Container Runtime +[WARN] 5.1 - Ensure AppArmor Profile is Enabled +[WARN] * No AppArmorProfile Found: devops-lab9-control-plane +[WARN] * No AppArmorProfile Found: devops-lab9-worker +[WARN] * No AppArmorProfile Found: prometheus +[WARN] * No AppArmorProfile Found: grafana +[WARN] * No AppArmorProfile Found: promtail +[WARN] * No AppArmorProfile Found: devops-go +[WARN] * No AppArmorProfile Found: devops-python +[WARN] * No AppArmorProfile Found: loki +[WARN] 5.2 - Ensure SELinux security options are set, if applicable +[WARN] * No SecurityOptions Found: prometheus +[WARN] * No SecurityOptions Found: grafana +[WARN] * No SecurityOptions Found: promtail +[WARN] * No SecurityOptions Found: devops-go +[WARN] * No SecurityOptions Found: devops-python +[WARN] * No SecurityOptions Found: loki +[PASS] 5.3 - Ensure Linux Kernel Capabilities are restricted within containers +[WARN] 5.4 - Ensure privileged containers are not used +[WARN] * Container running in Privileged mode: devops-lab9-control-plane +[WARN] * Container running in Privileged mode: devops-lab9-worker +[PASS] 5.5 - Ensure sensitive host system directories are not mounted on containers +[PASS] 5.6 - Ensure ssh is not run within containers +[WARN] 5.7 - Ensure privileged ports are not mapped within containers +[WARN] * Privileged Port in use: 80 in devops-lab9-control-plane +[WARN] * Privileged Port in use: 443 in devops-lab9-control-plane +[NOTE] 5.8 - Ensure only needed ports are open on the container +[PASS] 5.9 - Ensure the host's network namespace is not shared +[WARN] 5.10 - Ensure memory usage for container is limited +[WARN] * Container running without memory restrictions: devops-lab9-control-plane +[WARN] * Container running without memory restrictions: devops-lab9-worker +[WARN] 5.11 - Ensure CPU priority is set appropriately on the container +[WARN] * Container running without CPU restrictions: devops-lab9-control-plane +[WARN] * Container running without CPU restrictions: devops-lab9-worker +[WARN] * Container running without CPU restrictions: prometheus +[WARN] * Container running without CPU restrictions: grafana +[WARN] * Container running without CPU restrictions: promtail +[WARN] * Container running without CPU restrictions: devops-go +[WARN] * Container running without CPU restrictions: devops-python +[WARN] * Container running without CPU restrictions: loki +[WARN] 5.12 - Ensure the container's root filesystem is mounted as read only +[WARN] * Container running with root FS mounted R/W: devops-lab9-control-plane +[WARN] * Container running with root FS mounted R/W: devops-lab9-worker +[WARN] * Container running with root FS mounted R/W: prometheus +[WARN] * Container running with root FS mounted R/W: grafana +[WARN] * Container running with root FS mounted R/W: promtail +[WARN] * Container running with root FS mounted R/W: devops-go +[WARN] * Container running with root FS mounted R/W: devops-python +[WARN] * Container running with root FS mounted R/W: loki +[WARN] 5.13 - Ensure incoming container traffic is binded to a specific host interface +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-lab9-control-plane +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in prometheus +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in grafana +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in promtail +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-go +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in devops-python +[WARN] * Port being bound to wildcard IP: 0.0.0.0 in loki +[WARN] 5.14 - Ensure 'on-failure' container restart policy is set to '5' +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-control-plane +[WARN] * MaximumRetryCount is not set to 5: devops-lab9-worker +[WARN] * MaximumRetryCount is not set to 5: prometheus +[WARN] * MaximumRetryCount is not set to 5: grafana +[WARN] * MaximumRetryCount is not set to 5: promtail +[WARN] * MaximumRetryCount is not set to 5: devops-go +[WARN] * MaximumRetryCount is not set to 5: devops-python +[WARN] * MaximumRetryCount is not set to 5: loki +[PASS] 5.15 - Ensure the host's process namespace is not shared +[PASS] 5.16 - Ensure the host's IPC namespace is not shared +[PASS] 5.17 - Ensure host devices are not directly exposed to containers +[INFO] 5.18 - Ensure the default ulimit is overwritten at runtime, only if needed +[INFO] * Container no default ulimit override: devops-lab9-control-plane +[INFO] * Container no default ulimit override: devops-lab9-worker +[INFO] * Container no default ulimit override: prometheus +[INFO] * Container no default ulimit override: grafana +[INFO] * Container no default ulimit override: promtail +[INFO] * Container no default ulimit override: devops-go +[INFO] * Container no default ulimit override: devops-python +[INFO] * Container no default ulimit override: loki +[PASS] 5.19 - Ensure mount propagation mode is not set to shared +[PASS] 5.20 - Ensure the host's UTS namespace is not shared +[WARN] 5.21 - Ensure the default seccomp profile is not Disabled +[WARN] * Default seccomp profile disabled: devops-lab9-control-plane +[WARN] * Default seccomp profile disabled: devops-lab9-worker +[NOTE] 5.22 - Ensure docker exec commands are not used with privileged option +[NOTE] 5.23 - Ensure docker exec commands are not used with user option +[PASS] 5.24 - Ensure cgroup usage is confirmed +[WARN] 5.25 - Ensure the container is restricted from acquiring additional privileges +[WARN] * Privileges not restricted: devops-lab9-control-plane +[WARN] * Privileges not restricted: devops-lab9-worker +[WARN] * Privileges not restricted: prometheus +[WARN] * Privileges not restricted: grafana +[WARN] * Privileges not restricted: promtail +[WARN] * Privileges not restricted: devops-go +[WARN] * Privileges not restricted: devops-python +[WARN] * Privileges not restricted: loki +[WARN] 5.26 - Ensure container health is checked at runtime +[WARN] * Health check not set: devops-lab9-control-plane +[WARN] * Health check not set: devops-lab9-worker +[INFO] 5.27 - Ensure docker commands always get the latest version of the image +[PASS] 5.28 - Ensure PIDs cgroup limit is used +[PASS] 5.29 - Ensure Docker's default bridge docker0 is not used +[PASS] 5.30 - Ensure the host's user namespaces is not shared +[WARN] 5.31 - Ensure the Docker socket is not mounted inside any containers +[WARN] * Docker socket shared: promtail + + +[INFO] 6 - Docker Security Operations +[INFO] 6.1 - Avoid image sprawl +[INFO] * There are currently: 30 images +[INFO] * Only 12 out of 30 are in use +[INFO] 6.2 - Avoid container sprawl +[INFO] * There are currently a total of 12 containers, with 9 of them currently running + + +[INFO] 7 - Docker Swarm Configuration +[PASS] 7.1 - Ensure swarm mode is not Enabled, if not needed +[PASS] 7.2 - Ensure the minimum number of manager nodes have been created in a swarm (Swarm mode not enabled) +[PASS] 7.3 - Ensure swarm services are binded to a specific host interface (Swarm mode not enabled) +[PASS] 7.4 - Ensure data exchanged between containers are encrypted on different nodes on the overlay network +[PASS] 7.5 - Ensure Docker's secret management commands are used for managing secrets in a Swarm cluster (Swarm mode not enabled) +[PASS] 7.6 - Ensure swarm manager is run in auto-lock mode (Swarm mode not enabled) +[PASS] 7.7 - Ensure swarm manager auto-lock key is rotated periodically (Swarm mode not enabled) +[PASS] 7.8 - Ensure node certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.9 - Ensure CA certificates are rotated as appropriate (Swarm mode not enabled) +[PASS] 7.10 - Ensure management plane traffic has been separated from data plane traffic (Swarm mode not enabled) +[INFO] Checks: 105 +[INFO] Score: 4 diff --git a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json index 18730fea..5c45c306 100644 --- a/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json +++ b/labs/lab7/hardening/docker-bench-src/docker-bench-security.sh.log.json @@ -1,6 +1,6 @@ { "dockerbenchsecurity": "1.3.4", - "start": 1774282376, + "start": 1774288024, "tests": [ {"id": "1", "desc": "Host Configuration", "results": [ {"id": "1.1", "desc": "Ensure a separate partition for containers has been created", "result": "WARN"}, @@ -60,53 +60,53 @@ {"id": "3.20", "desc": "Ensure that /etc/default/docker file permissions are set to 644 or more restrictive", "result": "INFO", "details": "File not found"} ]}, {"id": "4", "desc": "Container Images and Build File", "results": [ - {"id": "4.1", "desc": "Ensure a user for the container has been created", "result": "WARN", "details": "running as root: elegant_sammet devops-lab9-control-plane devops-lab9-worker promtail", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","promtail"]}, + {"id": "4.1", "desc": "Ensure a user for the container has been created", "result": "WARN", "details": "running as root: devops-lab9-control-plane devops-lab9-worker promtail", "items": ["devops-lab9-control-plane","devops-lab9-worker","promtail"]}, {"id": "4.2", "desc": "Ensure that containers use trusted base images", "result": "NOTE"}, {"id": "4.3", "desc": "Ensure unnecessary packages are not installed in the container", "result": "NOTE"}, {"id": "4.4", "desc": "Ensure images are scanned and rebuilt to include security patches", "result": "NOTE"}, {"id": "4.5", "desc": "Ensure Content trust for Docker is Enabled", "result": "WARN"}, - {"id": "4.6", "desc": "Ensure HEALTHCHECK instructions have been added to the container image", "result": "WARN", "details": "Images w/o HEALTHCHECK: [pepegx/devops-info-service:lab02] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-go:lab02] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-service:lab07] [devops-info-service-go:lab07] [host.docker.internal:5001/devops-info-service:latest] [host.docker.internal:5001/devops-info-service-go:latest] [bridgecrew/checkov:latest] [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [checkmarx/kics:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [prom/prometheus:v3.9.0] [grafana/grafana:12.3.1] [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] [bkimminich/juice-shop:v19.0.0] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [tenable/terrascan:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2]", "items": ["[pepegx/devops-info-service:lab02]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-go:lab02]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-service:lab07]","[devops-info-service-go:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[host.docker.internal:5001/devops-info-service-go:latest]","[bridgecrew/checkov:latest]","[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[checkmarx/kics:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[prom/prometheus:v3.9.0]","[grafana/grafana:12.3.1]","[kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48]","[bkimminich/juice-shop:v19.0.0]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[tenable/terrascan:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]"]}, - {"id": "4.7", "desc": "Ensure update instructions are not use alone in the Dockerfile", "result": "INFO", "details": "Update instructions found: [pepegx/devops-info-service:lab02] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab07] [host.docker.internal:5001/devops-info-service:latest] [bridgecrew/checkov:latest] [checkmarx/kics:latest] [ghcr.io/sullo/nikto:latest] [sh1co/wikifet_arm:latest] [grafana/promtail:3.0.0]", "items": ["[pepegx/devops-info-service:lab02]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[bridgecrew/checkov:latest]","[checkmarx/kics:latest]","[ghcr.io/sullo/nikto:latest]","[sh1co/wikifet_arm:latest]","[grafana/promtail:3.0.0]"]}, + {"id": "4.6", "desc": "Ensure HEALTHCHECK instructions have been added to the container image", "result": "WARN", "details": "Images w/o HEALTHCHECK: [snyk/snyk:docker] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [pepegx/devops-info-service:lab02] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-service-go:lab08 localhost:5001/devops-info-service-go:latest] [devops-info-go:lab02] [devops-info-service:lab07] [devops-info-service-go:lab07] [host.docker.internal:5001/devops-info-service:latest] [host.docker.internal:5001/devops-info-service-go:latest] [bridgecrew/checkov:latest] [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [checkmarx/kics:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [alpine:3.22] [prom/prometheus:v3.9.0] [grafana/grafana:12.3.1] [kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48] [bkimminich/juice-shop:v19.0.0] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [tenable/terrascan:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2]", "items": ["[snyk/snyk:docker]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[pepegx/devops-info-service:lab02]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-service-go:lab08","localhost:5001/devops-info-service-go:latest]","[devops-info-go:lab02]","[devops-info-service:lab07]","[devops-info-service-go:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[host.docker.internal:5001/devops-info-service-go:latest]","[bridgecrew/checkov:latest]","[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[checkmarx/kics:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[alpine:3.22]","[prom/prometheus:v3.9.0]","[grafana/grafana:12.3.1]","[kindest/node@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48]","[bkimminich/juice-shop:v19.0.0]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[tenable/terrascan:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]"]}, + {"id": "4.7", "desc": "Ensure update instructions are not use alone in the Dockerfile", "result": "INFO", "details": "Update instructions found: [snyk/snyk:docker] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [devops-info-service:lab08 localhost:5001/devops-info-service:latest] [pepegx/devops-info-service:lab02] [devops-info-service:lab07] [host.docker.internal:5001/devops-info-service:latest] [bridgecrew/checkov:latest] [checkmarx/kics:latest] [ghcr.io/sullo/nikto:latest] [sh1co/wikifet_arm:latest] [grafana/promtail:3.0.0]", "items": ["[snyk/snyk:docker]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[devops-info-service:lab08","localhost:5001/devops-info-service:latest]","[pepegx/devops-info-service:lab02]","[devops-info-service:lab07]","[host.docker.internal:5001/devops-info-service:latest]","[bridgecrew/checkov:latest]","[checkmarx/kics:latest]","[ghcr.io/sullo/nikto:latest]","[sh1co/wikifet_arm:latest]","[grafana/promtail:3.0.0]"]}, {"id": "4.8", "desc": "Ensure setuid and setgid permissions are removed in the images", "result": "NOTE"}, - {"id": "4.9", "desc": "Ensure COPY is used instead of ADD in Dockerfile", "result": "INFO", "details": "Images using ADD: [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [grafana/grafana:12.3.1] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2] [docker/docker-bench-security:latest]", "items": ["[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[grafana/grafana:12.3.1]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]","[docker/docker-bench-security:latest]"]}, + {"id": "4.9", "desc": "Ensure COPY is used instead of ADD in Dockerfile", "result": "INFO", "details": "Images using ADD: [snyk/snyk:docker] [geerlingguy/docker-ubuntu2404-ansible:latest] [docker:cli] [projectdiscovery/nuclei:latest] [semgrep/semgrep:latest] [ghcr.io/sullo/nikto:latest] [alpine:3.22] [grafana/grafana:12.3.1] [aquasec/tfsec:latest] [sh1co/wikifet_arm:latest] [goodwithtech/dockle:latest] [grafana/promtail:3.0.0] [grafana/loki:3.0.0] [registry:2] [docker/docker-bench-security:latest]", "items": ["[snyk/snyk:docker]","[geerlingguy/docker-ubuntu2404-ansible:latest]","[docker:cli]","[projectdiscovery/nuclei:latest]","[semgrep/semgrep:latest]","[ghcr.io/sullo/nikto:latest]","[alpine:3.22]","[grafana/grafana:12.3.1]","[aquasec/tfsec:latest]","[sh1co/wikifet_arm:latest]","[goodwithtech/dockle:latest]","[grafana/promtail:3.0.0]","[grafana/loki:3.0.0]","[registry:2]","[docker/docker-bench-security:latest]"]}, {"id": "4.10", "desc": "Ensure secrets are not stored in Dockerfiles", "result": "NOTE"}, {"id": "4.11", "desc": "Ensure verified packages are only Installed", "result": "NOTE"} ]}, {"id": "5", "desc": "Container Runtime", "results": [ - {"id": "5.1", "desc": "Ensure AppArmor Profile is Enabled", "result": "WARN", "details": "Containers with no AppArmorProfile: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.1", "desc": "Ensure AppArmor Profile is Enabled", "result": "WARN", "details": "Containers with no AppArmorProfile: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, {"id": "5.2", "desc": "Ensure SELinux security options are set, if applicable", "result": "WARN", "details": "Containers with no SecurityOptions: prometheus grafana promtail devops-go devops-python loki", "items": ["prometheus","grafana","promtail","devops-go","devops-python","loki"]}, - {"id": "5.3", "desc": "Ensure Linux Kernel Capabilities are restricted within containers", "result": "WARN", "details": "Capabilities added for containers: elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.3", "desc": "Ensure Linux Kernel Capabilities are restricted within containers", "result": "PASS"}, {"id": "5.4", "desc": "Ensure privileged containers are not used", "result": "WARN", "details": "Containers running in privileged mode: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, {"id": "5.5", "desc": "Ensure sensitive host system directories are not mounted on containers", "result": "PASS"}, {"id": "5.6", "desc": "Ensure ssh is not run within containers", "result": "PASS"}, {"id": "5.7", "desc": "Ensure privileged ports are not mapped within containers", "result": "WARN", "details": "Containers using privileged ports: devops-lab9-control-plane:80 devops-lab9-control-plane:443", "items": ["devops-lab9-control-plane:80","devops-lab9-control-plane:443"]}, {"id": "5.8", "desc": "Ensure only needed ports are open on the container", "result": "NOTE"}, - {"id": "5.9", "desc": "Ensure the host's network namespace is not shared", "result": "WARN", "details": "Containers running with networking mode 'host': elegant_sammet", "items": ["elegant_sammet"]}, - {"id": "5.10", "desc": "Ensure memory usage for container is limited", "result": "WARN", "details": "Container running without memory restrictions: elegant_sammet devops-lab9-control-plane devops-lab9-worker", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker"]}, - {"id": "5.11", "desc": "Ensure CPU priority is set appropriately on the container", "result": "WARN", "details": "Containers running without CPU restrictions: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, - {"id": "5.12", "desc": "Ensure the container's root filesystem is mounted as read only", "result": "WARN", "details": "Containers running with root FS mounted R/W: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.9", "desc": "Ensure the host's network namespace is not shared", "result": "PASS"}, + {"id": "5.10", "desc": "Ensure memory usage for container is limited", "result": "WARN", "details": "Container running without memory restrictions: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.11", "desc": "Ensure CPU priority is set appropriately on the container", "result": "WARN", "details": "Containers running without CPU restrictions: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.12", "desc": "Ensure the container's root filesystem is mounted as read only", "result": "WARN", "details": "Containers running with root FS mounted R/W: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, {"id": "5.13", "desc": "Ensure incoming container traffic is binded to a specific host interface", "result": "WARN", "details": "Containers with port bound to wildcard IP: devops-lab9-control-plane:0.0.0.0 devops-lab9-control-plane:0.0.0.0 prometheus:0.0.0.0 grafana:0.0.0.0 promtail:0.0.0.0 devops-go:0.0.0.0 devops-python:0.0.0.0 loki:0.0.0.0", "items": ["devops-lab9-control-plane:0.0.0.0","devops-lab9-control-plane:0.0.0.0","prometheus:0.0.0.0","grafana:0.0.0.0","promtail:0.0.0.0","devops-go:0.0.0.0","devops-python:0.0.0.0","loki:0.0.0.0"]}, - {"id": "5.14", "desc": "Ensure 'on-failure' container restart policy is set to '5'", "result": "WARN", "details": "Containers with MaximumRetryCount not set to 5: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, - {"id": "5.15", "desc": "Ensure the host's process namespace is not shared", "result": "WARN", "details": "Containers sharing host PID namespace: elegant_sammet", "items": ["elegant_sammet"]}, + {"id": "5.14", "desc": "Ensure 'on-failure' container restart policy is set to '5'", "result": "WARN", "details": "Containers with MaximumRetryCount not set to 5: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.15", "desc": "Ensure the host's process namespace is not shared", "result": "PASS"}, {"id": "5.16", "desc": "Ensure the host's IPC namespace is not shared", "result": "PASS"}, {"id": "5.17", "desc": "Ensure host devices are not directly exposed to containers", "result": "PASS"}, - {"id": "5.18", "desc": "Ensure the default ulimit is overwritten at runtime, only if needed", "result": "INFO", "details": "Containers with no default ulimit override: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.18", "desc": "Ensure the default ulimit is overwritten at runtime, only if needed", "result": "INFO", "details": "Containers with no default ulimit override: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, {"id": "5.19", "desc": "Ensure mount propagation mode is not set to shared", "result": "PASS"}, {"id": "5.20", "desc": "Ensure the host's UTS namespace is not shared", "result": "PASS"}, {"id": "5.21", "desc": "Ensure the default seccomp profile is not Disabled", "result": "WARN", "details": "Containers with default seccomp profile disabled: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, {"id": "5.22", "desc": "Ensure docker exec commands are not used with privileged option", "result": "NOTE"}, {"id": "5.23", "desc": "Ensure docker exec commands are not used with user option", "result": "NOTE"}, {"id": "5.24", "desc": "Ensure cgroup usage is confirmed", "result": "PASS"}, - {"id": "5.25", "desc": "Ensure the container is restricted from acquiring additional privileges", "result": "WARN", "details": "Containers without restricted privileges: elegant_sammet devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, - {"id": "5.26", "desc": "Ensure container health is checked at runtime", "result": "WARN", "details": "Containers without health check: elegant_sammet devops-lab9-control-plane devops-lab9-worker", "items": ["elegant_sammet","devops-lab9-control-plane","devops-lab9-worker"]}, + {"id": "5.25", "desc": "Ensure the container is restricted from acquiring additional privileges", "result": "WARN", "details": "Containers without restricted privileges: devops-lab9-control-plane devops-lab9-worker prometheus grafana promtail devops-go devops-python loki", "items": ["devops-lab9-control-plane","devops-lab9-worker","prometheus","grafana","promtail","devops-go","devops-python","loki"]}, + {"id": "5.26", "desc": "Ensure container health is checked at runtime", "result": "WARN", "details": "Containers without health check: devops-lab9-control-plane devops-lab9-worker", "items": ["devops-lab9-control-plane","devops-lab9-worker"]}, {"id": "5.27", "desc": "Ensure docker commands always get the latest version of the image", "result": "INFO"}, {"id": "5.28", "desc": "Ensure PIDs cgroup limit is used", "result": "PASS"}, {"id": "5.29", "desc": "Ensure Docker's default bridge docker0 is not used", "result": "PASS"}, - {"id": "5.30", "desc": "Ensure the host's user namespaces is not shared", "result": "WARN", "details": "Containers sharing host user namespace: elegant_sammet", "items": ["elegant_sammet"]}, - {"id": "5.31", "desc": "Ensure the Docker socket is not mounted inside any containers", "result": "WARN", "details": "Containers sharing docker socket: elegant_sammet promtail", "items": ["elegant_sammet","promtail"]} + {"id": "5.30", "desc": "Ensure the host's user namespaces is not shared", "result": "PASS"}, + {"id": "5.31", "desc": "Ensure the Docker socket is not mounted inside any containers", "result": "WARN", "details": "Containers sharing docker socket: promtail", "items": ["promtail"]} ]}, {"id": "6", "desc": "Docker Security Operations", "results": [ - {"id": "6.1", "desc": "Avoid image sprawl", "result": "INFO", "details": "12 active/28 in use"}, + {"id": "6.1", "desc": "Avoid image sprawl", "result": "INFO", "details": "12 active/30 in use"}, {"id": "6.2", "desc": "Avoid container sprawl", "result": "INFO", "details": "12 total/9 running"} ]}, {"id": "7", "desc": "Docker Swarm Configuration", "results": [ @@ -121,5 +121,5 @@ {"id": "7.9", "desc": "Ensure CA certificates are rotated as appropriate", "result": "PASS"}, {"id": "7.10", "desc": "Ensure management plane traffic has been separated from data plane traffic", "result": "PASS"} ]} - ], "checks": 105, "score": -3, "end": 1774282382 + ], "checks": 105, "score": 4, "end": 1774288030 } diff --git a/labs/submission7.md b/labs/submission7.md index 7dd4a3fc..d7f099fa 100644 --- a/labs/submission7.md +++ b/labs/submission7.md @@ -125,6 +125,8 @@ Conclusion: - Platform compatibility is a separate concern, but it is not the final blocker in the preserved scan outputs. - The actual blocker to completing Task 1.3 is missing credentials. +- I was not able to obtain a valid `SNYK_TOKEN` for this lab environment, so the Snyk comparison could not be completed successfully. +- Because of that, Task 1.3 remains formally incomplete on this machine. - To finish the Snyk comparison fully on this host, I would need a valid `SNYK_TOKEN` or a prior authenticated `snyk auth` session. ### 1.7 Security Posture Assessment @@ -171,7 +173,8 @@ Observed stock failure on Docker Desktop: Working execution using the official extracted benchmark scripts: ```bash -docker run --rm --net host --pid host --userns host --cap-add audit_control \ +docker run --rm --label docker_bench_security \ + --net host --pid host --userns host --cap-add audit_control \ -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \ -v "$(pwd)/labs/lab7/hardening/docker-bench-src":/src \ -v /var/lib:/var/lib:ro \ @@ -190,19 +193,27 @@ docker run --rm --net host --pid host --userns host --cap-add audit_control \ | Metric | Count | |---|---:| -| `PASS` | `28` | -| `WARN` | `30` | +| `PASS` | `32` | +| `WARN` | `26` | | `FAIL` | `0` | -| `INFO` | `44` | -| `NOTE` | `10` | +| `INFO` | `38` | +| `NOTE` | `9` | | Total checks | `105` | -| Score | `-3` | +| Score | `4` | Important interpretation: - There were no formal `FAIL` results in this run. - The host still has many meaningful `WARN` results, so "no FAIL" does not mean "secure". - A number of `INFO` lines were caused by Docker Desktop abstraction layers, for example missing `/etc/docker/daemon.json` or other Linux host files not exposed in the same way as on a native Linux host. +- The authoritative per-check totals above come from the structured benchmark log `docker-bench-src/docker-bench-security.sh.log.json`, not from counting every human-readable output line in `docker-bench-results.txt`. +- The final Task 2 totals above come from the corrected labeled rerun. Adding `--label docker_bench_security` was necessary so the benchmark's own helper container was excluded by the official script logic. + +Reproducibility note: + +- The human-readable report used for analysis is `labs/lab7/hardening/docker-bench-results.txt`. +- The auxiliary `.log` and `.log.json` files under `docker-bench-src/` can drift slightly across reruns because Docker Bench records the exact currently-running container names and image inventory at execution time. +- Those rerun-sensitive details do not change the main benchmark conclusion or the priority remediation items called out below. ### 2.3 High-Value Warning Analysis I focused on warnings that represent real security risk instead of platform noise. @@ -518,7 +529,7 @@ I would add: ### 1. Snyk architecture and authentication blockers - `snyk/snyk:docker` had no native `arm64` manifest. - After switching to `--platform linux/amd64`, the scan reached Snyk but failed with `401 Unauthorized`. -- This is a real credentials issue, not a Docker networking issue. +- I was not able to obtain a valid `SNYK_TOKEN`, so this remained a real credentials issue rather than a Docker networking issue. ### 2. `docker-bench-security` on Docker Desktop - The stock lab command failed because a read-only bind of `/etc` blocked Docker's own hostname mount. @@ -552,3 +563,7 @@ This lab demonstrates a realistic DevSecOps lesson: - Runtime hardening matters because it constrains blast radius even when the application itself is compromised. The strongest practical takeaway is that container security is layered. Image scanning, daemon / host hardening, and runtime restrictions each solve different parts of the problem, and none of them is enough on its own. + +Honest completion note: + +- Task 1.3 with Snyk was attempted and documented, but it was not completed successfully because I did not have a valid `SNYK_TOKEN` in this environment.