-
Notifications
You must be signed in to change notification settings - Fork 251
chore: add Trivy security scanning and fix non-root container users #3082
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7a15f82
f237083
d4b15fc
62e68bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # Security scanning with Trivy (https://trivy.dev) | ||
|
|
||
| trivy_image := "aquasec/trivy:latest" | ||
| trivy_severity := env("TRIVY_SEVERITY", "CRITICAL,HIGH") | ||
| trivy_cache_volume := "trivy-cache" | ||
| scan_images := env("SCAN_IMAGES", "evstack:local-dev") | ||
|
|
||
| trivy_run := "docker run --rm -v " + trivy_cache_volume + ":/root/.cache/ -e TRIVY_SEVERITY=" + trivy_severity | ||
|
|
||
| # Run all Trivy security scans (filesystem + Docker images) | ||
| trivy-scan: trivy-scan-fs trivy-scan-image | ||
|
|
||
| # Scan repo for dependency vulnerabilities, misconfigs, and secrets | ||
| trivy-scan-fs: | ||
| @echo "--> Scanning repository filesystem with Trivy" | ||
| @{{trivy_run}} \ | ||
| -v {{justfile_directory()}}:/workspace \ | ||
| {{trivy_image}} \ | ||
| fs --scanners vuln,misconfig,secret \ | ||
| --severity {{trivy_severity}} \ | ||
| /workspace | ||
| @echo "--> Filesystem scan complete" | ||
|
|
||
| # Scan built Docker images for vulnerabilities | ||
| trivy-scan-image: | ||
| @echo "--> Scanning Docker images with Trivy" | ||
| @for img in {{scan_images}}; do \ | ||
| echo "--> Scanning image: $img"; \ | ||
| {{trivy_run}} \ | ||
| -v /var/run/docker.sock:/var/run/docker.sock \ | ||
| {{trivy_image}} \ | ||
| image --severity {{trivy_severity}} \ | ||
| $img; \ | ||
| done | ||
|
Comment on lines
+11
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Trivy’s default behavior is to exit with code CLI (Trivy)
GitHub Action (
|
||
| @echo "--> Image scan complete" | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||||
| ### Changes | ||||||
|
|
||||||
| - Store pending blocks separately from executed blocks key. [#3073](https://github.com/evstack/ev-node/pull/3073) | ||||||
| - **BREAKING:** Docker images for `evm`, `testapp`, and `local-da` now run as non-root user `ev-node` (uid 1000) instead of `root`. Existing volumes or bind mounts with root-owned files may require a `chown` to uid 1000. See the [migration guide](https://ev.xyz/guides/migrate-docker-nonroot). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add PR reference for changelog traceability. Line 27 should include the PR link like neighboring entries. 🔧 Suggested fix-- **BREAKING:** Docker images for `evm`, `testapp`, and `local-da` now run as non-root user `ev-node` (uid 1000) instead of `root`. Existing volumes or bind mounts with root-owned files may require a `chown` to uid 1000. See the [migration guide](https://ev.xyz/guides/migrate-docker-nonroot).
+- **BREAKING:** Docker images for `evm`, `testapp`, and `local-da` now run as non-root user `ev-node` (uid 1000) instead of `root`. Existing volumes or bind mounts with root-owned files may require a `chown` to uid 1000. See the [migration guide](https://ev.xyz/guides/migrate-docker-nonroot). [`#3082`](https://github.com/evstack/ev-node/pull/3082)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| - Fixes issues with force inclusion verification on sync nodes. [#3057](https://github.com/evstack/ev-node/pull/3057) | ||||||
| - Add flag to `local-da` to produce empty DA blocks (closer to the real system). [#3057](https://github.com/evstack/ev-node/pull/3057) | ||||||
| - Validate P2P DA height hints against the latest known DA height to prevent malicious peers from triggering runaway catchup . [#3128](https://github.com/evstack/ev-node/pull/3128) | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -17,10 +17,16 @@ FROM alpine:3.22.2 | |||||||||||||||||||||||||||||||||||||
| #hadolint ignore=DL3018 | ||||||||||||||||||||||||||||||||||||||
| RUN apk --no-cache add ca-certificates curl | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| WORKDIR /root | ||||||||||||||||||||||||||||||||||||||
| RUN addgroup -g 1000 ev-node && \ | ||||||||||||||||||||||||||||||||||||||
| adduser -u 1000 -G ev-node -s /bin/sh -D ev-node | ||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| WORKDIR /home/ev-node | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| COPY --from=build-env /src/apps/evm/evm /usr/bin/evm | ||||||||||||||||||||||||||||||||||||||
| COPY apps/evm/entrypoint.sh /usr/bin/entrypoint.sh | ||||||||||||||||||||||||||||||||||||||
| RUN chmod +x /usr/bin/entrypoint.sh | ||||||||||||||||||||||||||||||||||||||
| RUN chmod +x /usr/bin/entrypoint.sh && \ | ||||||||||||||||||||||||||||||||||||||
| chown -R ev-node:ev-node /home/ev-node | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| USER ev-node | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+30
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Verify entrypoint depends on HOME and Dockerfile state
echo "=== Checking entrypoint.sh for HOME usage ==="
rg -n '\$HOME|--home|\.evm' apps/evm/entrypoint.sh
echo ""
echo "=== Checking Dockerfile for WORKDIR, USER, ENV HOME ==="
rg -n 'WORKDIR|USER|ENV HOME' apps/evm/DockerfileRepository: evstack/ev-node Length of output: 571 Set The entrypoint.sh defaults to 🔧 Suggested fix WORKDIR /home/ev-node
+ENV HOME=/home/ev-node
COPY --from=build-env /src/apps/evm/evm /usr/bin/evm
COPY apps/evm/entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod +x /usr/bin/entrypoint.sh && \
chown -R ev-node:ev-node /home/ev-node
USER ev-node📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ENTRYPOINT ["/usr/bin/entrypoint.sh"] | ||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # Migrating Docker Containers to Non-Root User | ||
|
|
||
| Starting with this release, the `evm`, `testapp`, and `local-da` Docker images run as a non-root user (`ev-node`, uid/gid 1000) instead of `root`. This aligns with the `grpc` image, which already ran as non-root. | ||
|
|
||
| If you are running any of these containers with **persistent volumes or bind mounts**, you need to fix file ownership before upgrading. Containers running without persistent storage (ephemeral) require no action. | ||
|
|
||
| ## Who is affected | ||
|
|
||
| You are affected if **all** of the following are true: | ||
|
|
||
| - You run `evm`, `testapp`, or `local-da` via Docker (or docker-compose / Kubernetes) | ||
| - You use a volume or bind mount for the container's data directory | ||
| - The files in that volume were created by a previous (root-based) image | ||
|
|
||
| ## Migration steps | ||
|
|
||
| ### 1. Stop the running container | ||
|
|
||
| ```bash | ||
| docker stop <container-name> | ||
| ``` | ||
|
|
||
| ### 2. Fix file ownership on the volume | ||
|
|
||
| For **bind mounts** (host directory), run `chown` directly on the host: | ||
|
|
||
| ```bash | ||
| # Replace /path/to/data with your actual data directory | ||
| sudo chown -R 1000:1000 /path/to/data | ||
| ``` | ||
|
|
||
| For **named Docker volumes**, use a temporary container: | ||
|
|
||
| ```bash | ||
| # Replace <volume-name> with your Docker volume name | ||
| docker run --rm -v <volume-name>:/data alpine chown -R 1000:1000 /data | ||
| ``` | ||
|
|
||
| ### 3. Pull the new image and restart | ||
|
|
||
| ```bash | ||
| docker pull <image> | ||
| docker start <container-name> | ||
| ``` | ||
|
Comment on lines
+39
to
+44
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No.
To use the newly pulled image, you must recreate the container (e.g., Citations:
Step 3 must recreate the container to use the newly pulled image. The current step pulls a new image but then runs 🔧 Suggested fix ### 3. Pull the new image and restart
```bash
docker pull <image>
-docker start <container-name>
+# Recreate container to use the newly pulled image
+docker rm <container-name>
+docker run ... <image> ...Verify each finding against the current code and only fix it if needed. In |
||
|
|
||
| ### Kubernetes / docker-compose | ||
|
|
||
| If you manage containers through orchestration, you have two options: | ||
|
|
||
| **Option A: Init container (recommended for Kubernetes)** | ||
|
|
||
| Add an init container that fixes ownership before the main container starts: | ||
|
|
||
| ```yaml | ||
| initContainers: | ||
| - name: fix-permissions | ||
| image: alpine:3.22 | ||
| command: ["chown", "-R", "1000:1000", "/home/ev-node"] | ||
| volumeMounts: | ||
| - name: data | ||
| mountPath: /home/ev-node | ||
| securityContext: | ||
| runAsUser: 1000 | ||
| runAsGroup: 1000 | ||
| fsGroup: 1000 | ||
| ``` | ||
|
|
||
| **Option B: Set `fsGroup` in the pod security context** | ||
|
|
||
| If your volume driver supports it, setting `fsGroup: 1000` will automatically fix ownership on mount: | ||
|
|
||
| ```yaml | ||
| securityContext: | ||
| runAsUser: 1000 | ||
| runAsGroup: 1000 | ||
| fsGroup: 1000 | ||
| ``` | ||
|
|
||
| **docker-compose**: update your `docker-compose.yml` to set the user: | ||
|
|
||
| ```yaml | ||
| services: | ||
| evm: | ||
| image: evm:latest | ||
| user: "1000:1000" | ||
| volumes: | ||
| - evm-data:/home/ev-node | ||
| ``` | ||
|
|
||
| ## Verifying the migration | ||
|
|
||
| After restarting, confirm the container runs as the correct user: | ||
|
|
||
| ```bash | ||
| docker exec <container-name> id | ||
| # Expected: uid=1000(ev-node) gid=1000(ev-node) | ||
| ``` | ||
|
|
||
| Check that the process can read and write its data directory: | ||
|
|
||
| ```bash | ||
| docker exec <container-name> ls -la /home/ev-node | ||
| # All files should be owned by ev-node:ev-node | ||
| ``` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| | Symptom | Cause | Fix | | ||
| |---|---|---| | ||
| | `Permission denied` on startup | Volume files still owned by root | Re-run the `chown` step above | | ||
| | Container exits immediately | Data directory not writable | Check ownership and directory permissions (`drwxr-xr-x` or more permissive for uid 1000) | | ||
| | Application writes to wrong path | Old `WORKDIR` was `/root` or `/apps` | Update any custom volume mounts to target `/home/ev-node` instead | | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Trivy container image latest tag best practices CI/CD reproducibility💡 Result:
Recommendation: don’t use
:latestfor Trivy in CI/CD if you care about reproducibilitylatest(and other floating tags) can be retagged to different image contents over time, which breaks determinism. Docker explicitly recommends pinning by digest when you need fully repeatable builds/scans. [1]Best practices for reproducible Trivy container-image scanning
Pin the Trivy tool version (and ideally the image digest)
latest.version: latestorversion: vX.Y.Z; don’t uselatestfor reproducibility). [2]Control the vulnerability database lifecycle (this is a major source of “same image, different results”)
--skip-db-updateto prevent pulling a newer DB during the scan. [4]If you need strict repeatability across networks/regions, self-host or mirror the Trivy DB
Separate “reproducible security gate” from “continuously updated security signal”
Sources: Docker pinning/digest guidance for determinism [1]; Trivy Action supports explicit Trivy version pinning [2]; Trivy warns about canary builds [3]; Trivy DB update frequency + skip-update + caching [4]; Trivy DB distribution via GHCR / offline control concepts [5].
🏁 Script executed:
# Examine the .just/security.just file to understand context cat -n .just/security.justRepository: evstack/ev-node
Length of output: 1566
🏁 Script executed:
Repository: evstack/ev-node
Length of output: 693
Pin Trivy version and consider database update strategy.
Line 3 uses
:latest, which allows the Trivy vulnerability database to update automatically (~every 6 hours), causing identical image scans to produce different results. Pin an explicit Trivy version (and optionally the image digest) for reproducible security scans.The project already caches the Trivy database via
trivy_cache_volume, which helps. For further reproducibility, consider:aquasec/trivy:0.48.0) or digest--skip-db-updatein PR/gate scans to freeze results; enable DB updates only in scheduled/continuous jobs🔧 Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents