Skip to content

Fixes #6819: harden Dockerfiles for example and integrated test images - #7152

Open
BobSong-dev wants to merge 9 commits into
apache:masterfrom
BobSong-dev:fix/6819-dockerfile-hardening
Open

BobSong-dev wants to merge 9 commits into
apache:masterfrom
BobSong-dev:fix/6819-dockerfile-hardening

Conversation

@BobSong-dev

@BobSong-dev BobSong-dev commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Background

The Dockerfiles under shenyu-examples and shenyu-integrated-test run as root and do not provide OCI image metadata or container liveness checks. This makes the example and integration-test images harder to operate safely and consistently.

Fixes #6819

Changes

  • Add OCI image title, source, and license labels to all 39 target Dockerfiles.
  • Create a dedicated shenyu system user and run each image as that non-root user.
  • Add a portable process-level liveness HEALTHCHECK without introducing extra runtime packages.
  • Keep the upload-plugin custom artifact image buildable by checking the packaged plugin artifact instead of treating it as an application service.

Verification

  • git diff --check
  • Static validation confirmed all 39 target Dockerfiles contain LABEL, USER shenyu, and HEALTHCHECK, with no build-stage RUN instructions after the user switch.
  • Dockerfile BuildKit checks were attempted for representative CentOS, Alpine, and custom-plugin images

useradd --system --gid shenyu --create-home --home-dir /home/shenyu shenyu && \
chown -R shenyu:shenyu ${LOCAL_PATH}
USER shenyu
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 CMD ["sh", "-c", "kill -0 1"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kill -0 1 asserts nothing about the application.

PID 1 is by definition the container main process, so while the container is up this probe can never fail; it reports "the process exists", which Docker guarantees anyway. I verified this locally (Docker 29.4.0) with the same USER + HEALTHCHECK pattern on a busybox image:

$ docker inspect hc1 --format '{{.State.Health.Status}}'
healthy
$ docker exec hc1 sh -c 'nc -z 127.0.0.1 8189 || echo "port 8189 NOT listening (app not ready/broken)"'
port 8189 NOT listening (app not ready/broken)

The container reports healthy while nothing is listening on the service port, and it stays healthy through startup, a deadlock, or a refusing JVM.

That is a false signal rather than no signal, because this repository already consumes health status: docker compose up --wait is being adopted as a readiness gate (#7151 for the storage e2e scripts) and healthcheck.sh gates the integrated tests. Once one of these images is pulled into a --wait-based pipeline, this probe declares it ready ~30s after start regardless of what the JVM is doing.

Please either make it a real readiness probe (the actuator health endpoint where the image exposes one, otherwise a TCP connect against the application port using whatever the base image provides), or drop the HEALTHCHECK line and land only the non-root user + labels. Note that the custom-plugin image uses test -s /opt/shenyu-custom-plugin.jar, which is a build-time invariant - same tautology, different flavour.

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running these images as a non-root user and adding OCI metadata labels is a genuine improvement, and doing it consistently across all 22 example and integrated-test images (addgroup/adduser on the alpine bases, groupadd/useradd on the centos7 ones) is what a hardening PR should look like. Ownership of ${LOCAL_PATH} looks complete, the copied entries is chowned where relevant, and every exposed port is > 1024 so nothing needs root.

I am requesting changes on exactly one point.

[blocking] HEALTHCHECK ... CMD ["sh", "-c", "kill -0 1"] asserts nothing

PID 1 is the container main process by definition, so this probe can never fail while the container is up - it says "a process exists", which Docker already guarantees. Locally (Docker 29.4.0) with the same USER + HEALTHCHECK pattern:

$ docker inspect hc1 --format '{{.State.Health.Status}}'
healthy
$ docker exec hc1 sh -c 'nc -z 127.0.0.1 8189 || echo "port 8189 NOT listening (app not ready/broken)"'
port 8189 NOT listening (app not ready/broken)

Healthy while nothing is listening on the service port, and equally healthy through startup, a deadlock or a refusing JVM.

That matters because this repository already reads health status: docker compose up --wait is being adopted as the readiness gate (#7151 for the storage e2e scripts) and healthcheck.sh gates the integrated tests. The moment one of these images enters such a pipeline it is declared ready ~30s after start whatever the JVM is doing - a false signal, which is worse than none.

Please choose one:
(a) a real readiness probe per image - the actuator health endpoint where the image exposes one, otherwise a TCP connect against the application port using whatever the base image provides (and keep it working under USER shenyu);
(b) drop the HEALTHCHECK lines and land only the non-root user + labels.

Same issue in a different flavour in shenyu-integrated-test-upload-plugin/shenyu-custom-plugin: test -s /opt/shenyu-custom-plugin.jar is a build-time invariant and cannot fail at runtime either.

[nit] The two alpine images (shenyu-examples-apache-dubbo-service-xml and shenyu-examples-apache-dubbo-service) create the user with adduser -S shenyu -G shenyu without a home directory, while the centos7 variants use --create-home --home-dir /home/shenyu. Please add -h /home/shenyu so $HOME is not / and behaviour matches across images.

CI note (not a blocker): the integrated-test jobs are red here, but not because of these changes. The failure is

Unable to build image [apache/shenyu-integrated-test-upload-plugin-case:latest]:
"invalid from flag value apache/shenyu-integrated-test-custom: pull access denied for
 apache/shenyu-integrated-test-custom, repository does not exist or may require 'docker login'"

plus fail-fast cancellation of the remaining cases - an image publishing/environment problem, please just re-run. The e2e-case jobs that do not depend on that base image are green.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Task] — Integrated-test & examples Dockerfiles run as root, no HEALTHCHECK, no LABEL

4 participants