From 8cb6a2f256151a0cbe3d72d6eb3eb6b13c34e206 Mon Sep 17 00:00:00 2001 From: Jake Huneau Date: Sun, 25 Oct 2026 03:16:19 +0000 Subject: [PATCH] Fix the Docker image build and run OpenAS2 in it as a non-root user The Dockerfile built on eclipse-temurin 11, but since the minimum Java version was raised the project compiles for Java 17, so "docker build" failed in the build stage with "invalid target release: 17". Both stages now use eclipse-temurin 21, the newer LTS release CI already tests. The server also ran as root inside the container (issue #367), which matters for a service that is normally exposed to the internet. It now runs as the user "openas2" with the numeric UID 1000, so Kubernetes can verify runAsNonRoot, in group 0. Everything under /opt/openas2 is owned by that user and group writable, so platforms that run containers under an arbitrary UID in group 0 work too. A volume owned by root is not writable by the new user, so an existing deployment whose config or data volume was created as root fails to start with "Permission denied" until it is changed. The README now explains the ownership needed and how to keep running as root while volumes are migrated, the Kubernetes guide's example sets a pod securityContext, and the release notes carry an upgrade note. The README's description of the image's environment, which still gave Tomcat paths and Java 11, is corrected. Verified with the built image: the server runs as uid 1000 and starts, the config template is copied into an empty config folder, docker stop shuts it down cleanly, a named volume is created with the right owner, and a root-owned bind mount fails as described and starts with either fix. --- Dockerfile | 16 ++++++++++++---- README.md | 24 ++++++++++++++++++++---- RELEASE-NOTES.md | 17 +++++++++++++++++ changes.txt | 10 ++++++++++ k8s-deployment.md | 8 ++++++++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/Dockerfile b/Dockerfile index 97d71f1f..141d03e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,8 @@ -FROM eclipse-temurin:11.0.22_7-jdk-jammy AS builder +FROM eclipse-temurin:21-jdk-jammy AS builder COPY . /usr/src/openas2 WORKDIR /usr/src/openas2 # To test Locally builder environment: -# docker run --rm -it -v $(pwd):/usr/src/openas2 openjdk:11 bash +# docker run --rm -it -v $(pwd):/usr/src/openas2 eclipse-temurin:21-jdk-jammy bash RUN apt update RUN apt-get install -y unzip RUN rm -f Server/dist/* @@ -18,7 +18,7 @@ RUN cd /usr/src/openas2/Runtime/bin && \ mv config config_template -FROM eclipse-temurin:11.0.22_7-jre-jammy +FROM eclipse-temurin:21-jre-jammy ENV OPENAS2_BASE=/opt/openas2 ENV OPENAS2_HOME=/opt/openas2 ENV OPENAS2_TMPDIR=/opt/openas2/temp @@ -26,6 +26,14 @@ COPY --from=builder /usr/src/openas2/Runtime/bin ${OPENAS2_BASE}/bin COPY --from=builder /usr/src/openas2/Runtime/lib ${OPENAS2_BASE}/lib COPY --from=builder /usr/src/openas2/Runtime/resources ${OPENAS2_BASE}/resources COPY --from=builder /usr/src/openas2/Runtime/config_template ${OPENAS2_HOME}/config_template -RUN mkdir ${OPENAS2_BASE}/config +# Run as an unprivileged user rather than root, since the server is usually exposed to the internet. +# The user is in group 0 and everything it writes is group writable, so platforms that run the +# container under an arbitrary user ID in group 0 work as well. The ID is numeric in USER so that +# Kubernetes can verify runAsNonRoot. +RUN useradd --uid 1000 --gid 0 --home-dir ${OPENAS2_HOME} --no-create-home --shell /usr/sbin/nologin openas2 \ + && mkdir -p ${OPENAS2_BASE}/config ${OPENAS2_BASE}/data ${OPENAS2_BASE}/logs ${OPENAS2_TMPDIR} \ + && chown -R 1000:0 ${OPENAS2_BASE} \ + && chmod -R g=u ${OPENAS2_BASE} +USER 1000 WORKDIR $OPENAS2_HOME ENTRYPOINT ["/opt/openas2/bin/start-container.sh"] \ No newline at end of file diff --git a/README.md b/README.md index 76441116..ec53e60c 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,26 @@ You can then go to `http://localhost:4080` or `http://host-ip:4080` in a browser The default OpenAS2 environment in the image is: - OPENAS2_BASE: /usr/local/tomcat - OPENAS2_HOME: /usr/local/tomcat - OPENAS2_TMPDIR: /usr/local/tomcat/temp - JAVA_HOME: /usr/local/openjdk-11 + OPENAS2_BASE: /opt/openas2 + OPENAS2_HOME: /opt/openas2 + OPENAS2_TMPDIR: /opt/openas2/temp + JAVA_HOME: /opt/java/openjdk (Java 21) + +### Running as a non-root user + +The image runs OpenAS2 as user `openas2` (UID `1000`) in group `0`, not as root. Anything mounted into the +container that OpenAS2 writes to, normally `/opt/openas2/config` and `/opt/openas2/data`, must be writable by that +user, or the server fails to start with `Permission denied`. A directory created by Docker for a bind mount is owned +by root, so give it to the container user: + +```console +$ sudo chown -R 1000:0 ./config ./data +``` + +A named Docker volume is initialised from the image the first time it is used, so it already has the right owner. + +To keep running as root instead, for example while existing volumes are migrated, override the user with +`docker run --user 0`, or `user: "0"` in docker-compose.yml. The configuration files are available in `/opt/openas2/config/`. diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 489af3a6..b8afee8f 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -9,6 +9,16 @@ The release download file is: OpenAS2Server-4.13.0.zip The zip file contains a PDF document (OpenAS2HowTo.pdf) providing information on installing and using the application. ## NOTE: Testing covers Java 17 to 21. +Version UNRELEASED +=========================== + +This is a minor enhancement and bugfix release. +1. Fix the Docker image failing to build, and run OpenAS2 in it as an unprivileged user instead of root (issue #367). + The image built on Java 11 but the project compiles for Java 17, so "docker build" failed with "invalid target + release: 17". Both stages now use Java 21. The server now runs as the user "openas2" (UID 1000, group 0), since it + is usually exposed to the internet. Volumes mounted for the config and data folders must be writable by that user: + see the upgrade notes in RELEASE-NOTES.md and the README. + Version 4.14.0 2026-10-13 =========================== @@ -41,6 +51,13 @@ This is a minor enhancement and bugfix release. **You must review all notes for the relevant intermediate versions from your version to this release version.** +### Upgrading to a version after 4.14.0 if you run the OpenAS2 Docker image: + 1. The image now runs OpenAS2 as the unprivileged user "openas2" (UID 1000, group 0) instead of root. Volumes + mounted for /opt/openas2/config and /opt/openas2/data must be writable by that user or the server will fail to + start with "Permission denied". Change their ownership (for example "chown -R 1000:0 ./config ./data"), set + fsGroup in a Kubernetes pod securityContext, or keep running as root with "docker run --user 0" while the + volumes are migrated. See the README for details. + ### Upgrading to 4.6.1 or newer from any older version if using parallel processing mode: 1. Ensure you change the property for enabling parallel mode by removing the 3rd consecutive "l" from "process_files_in_paralllel". diff --git a/changes.txt b/changes.txt index fdb885bf..b33c5d24 100644 --- a/changes.txt +++ b/changes.txt @@ -1,5 +1,15 @@ **IMPORTANT NOTE**: Please review upgrade notes in the RELEASE-NOTES.md if you are upgrading +Version UNRELEASED +=========================== + +This is a minor enhancement and bugfix release. +1. Fix the Docker image failing to build, and run OpenAS2 in it as an unprivileged user instead of root (issue #367). + The image built on Java 11 but the project compiles for Java 17, so "docker build" failed with "invalid target + release: 17". Both stages now use Java 21. The server now runs as the user "openas2" (UID 1000, group 0), since it + is usually exposed to the internet. Volumes mounted for the config and data folders must be writable by that user: + see the upgrade notes in RELEASE-NOTES.md and the README. + Version 4.14.0 2026-10-13 =========================== diff --git a/k8s-deployment.md b/k8s-deployment.md index f4ea28ab..3685aa63 100644 --- a/k8s-deployment.md +++ b/k8s-deployment.md @@ -63,6 +63,14 @@ spec: labels: app: openas2 spec: + # The image runs as the unprivileged UID 1000. fsGroup makes block storage volumes writable by it; + # Azure Files and other SMB or NFS volumes ignore fsGroup, so set their ownership or permissions + # in the storage class mount options instead (for example uid=1000,gid=0). + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 0 + fsGroup: 1000 containers: - name: openas2 image: myrepo/openas2app:latest