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