diff --git a/.github/workflows/build-apache2-rootless.yaml b/.github/workflows/build-apache2-rootless.yaml new file mode 100644 index 0000000..c5d01b9 --- /dev/null +++ b/.github/workflows/build-apache2-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the Apache2 (rootless) container + +on: + push: + paths: + - "apache2-rootless/**" + - ".github/workflows/build-apache2-rootless.yaml" + schedule: + - cron: '15 7 * * *' + workflow_dispatch: + +jobs: + build-push-apache2-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./apache2-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/apache2-rootless:latest + ghcr.io/openconext/openconext-basecontainers/apache2-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-apache2-shibboleth-rootless.yaml b/.github/workflows/build-apache2-shibboleth-rootless.yaml new file mode 100644 index 0000000..5378dbe --- /dev/null +++ b/.github/workflows/build-apache2-shibboleth-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the Apache2 Shibboleth (rootless) container + +on: + push: + paths: + - "apache2-shibboleth-rootless/**" + - ".github/workflows/build-apache2-shibboleth-rootless.yaml" + schedule: + - cron: '15 7 * * *' + workflow_dispatch: + +jobs: + build-push-apache2-shibboleth-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./apache2-shibboleth-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/apache2-shibboleth-rootless:latest + ghcr.io/openconext/openconext-basecontainers/apache2-shibboleth-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-haproxy28-rootless.yaml b/.github/workflows/build-haproxy28-rootless.yaml new file mode 100644 index 0000000..8133914 --- /dev/null +++ b/.github/workflows/build-haproxy28-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the HAProxy 2.8 (rootless) container + +on: + push: + paths: + - "haproxy28-rootless/**" + - ".github/workflows/build-haproxy28-rootless.yaml" + schedule: + - cron: '45 7 * * *' + workflow_dispatch: + +jobs: + build-push-haproxy28-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./haproxy28-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/haproxy28-rootless:latest + ghcr.io/openconext/openconext-basecontainers/haproxy28-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-java21-rootless.yaml b/.github/workflows/build-java21-rootless.yaml new file mode 100644 index 0000000..9b90938 --- /dev/null +++ b/.github/workflows/build-java21-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the JAVA (rootless) container + +on: + push: + paths: + - "java21-rootless/**" + - ".github/workflows/build-java21-rootless.yaml" + schedule: + - cron: '30 7 * * *' + workflow_dispatch: + +jobs: + build-push-java21-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./java21-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/java21-rootless:latest + ghcr.io/openconext/openconext-basecontainers/java21-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-php82-apache2-node20-composer2-rootless.yaml b/.github/workflows/build-php82-apache2-node20-composer2-rootless.yaml new file mode 100644 index 0000000..0f3d745 --- /dev/null +++ b/.github/workflows/build-php82-apache2-node20-composer2-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the PHP 8.2 Apache2 node20 composer2 (rootless) container + +on: + push: + paths: + - "php82-apache2-node20-composer2-rootless/**" + - ".github/workflows/build-php82-apache2-node20-composer2-rootless.yaml" + schedule: + - cron: '15 7 * * *' + workflow_dispatch: + +jobs: + build-push-php82-apache2-node20-composer2-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./php82-apache2-node20-composer2-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/php82-apache2-node20-composer2-rootless:latest + ghcr.io/openconext/openconext-basecontainers/php82-apache2-node20-composer2-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-php82-apache2-rootless.yaml b/.github/workflows/build-php82-apache2-rootless.yaml new file mode 100644 index 0000000..eda0c91 --- /dev/null +++ b/.github/workflows/build-php82-apache2-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the PHP 8.2 Apache2 (rootless) container + +on: + push: + paths: + - "php82-apache2-rootless/**" + - ".github/workflows/build-php82-apache2-rootless.yaml" + schedule: + - cron: '45 7 * * *' + workflow_dispatch: + +jobs: + build-push-php82-apache2-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./php82-apache2-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/php82-apache2-rootless:latest + ghcr.io/openconext/openconext-basecontainers/php82-apache2-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-php85-apache2-node24-rootless.yaml b/.github/workflows/build-php85-apache2-node24-rootless.yaml new file mode 100644 index 0000000..d52b906 --- /dev/null +++ b/.github/workflows/build-php85-apache2-node24-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the PHP 8.5 Apache2 node24 (rootless) container + +on: + push: + paths: + - "php85-apache2-node24-rootless/**" + - ".github/workflows/build-php85-apache2-node24-rootless.yaml" + schedule: + - cron: '45 7 * * *' + workflow_dispatch: + +jobs: + build-push-php85-apache2-node24-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./php85-apache2-node24-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/php85-apache2-node24-rootless:latest + ghcr.io/openconext/openconext-basecontainers/php85-apache2-node24-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-php85-apache2-rootless.yaml b/.github/workflows/build-php85-apache2-rootless.yaml new file mode 100644 index 0000000..37dc32c --- /dev/null +++ b/.github/workflows/build-php85-apache2-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the PHP 8.5 Apache2 (rootless) container + +on: + push: + paths: + - "php85-apache2-rootless/**" + - ".github/workflows/build-php85-apache2-rootless.yaml" + schedule: + - cron: '45 7 * * *' + workflow_dispatch: + +jobs: + build-push-php85-apache2-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./php85-apache2-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/php85-apache2-rootless:latest + ghcr.io/openconext/openconext-basecontainers/php85-apache2-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-python3-rootless.yaml b/.github/workflows/build-python3-rootless.yaml new file mode 100644 index 0000000..e6c7b7e --- /dev/null +++ b/.github/workflows/build-python3-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the Python 3 (rootless) container + +on: + push: + paths: + - "python3-rootless/**" + - ".github/workflows/build-python3-rootless.yaml" + schedule: + - cron: '15 7 * * *' + workflow_dispatch: + +jobs: + build-push-python3-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./python3-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/python3-rootless:latest + ghcr.io/openconext/openconext-basecontainers/python3-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/.github/workflows/build-satosa-rootless.yaml b/.github/workflows/build-satosa-rootless.yaml new file mode 100644 index 0000000..b70509a --- /dev/null +++ b/.github/workflows/build-satosa-rootless.yaml @@ -0,0 +1,46 @@ +--- +name: Build the SATOSA (rootless) container + +on: + push: + paths: + - "satosa-rootless/**" + - ".github/workflows/build-satosa-rootless.yaml" + schedule: + - cron: '15 7 * * *' + workflow_dispatch: + +jobs: + build-push-satosa-rootless: + runs-on: ubuntu-latest + permissions: + packages: write + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v7 + with: + context: ./satosa-rootless + platforms: linux/amd64,linux/arm64 + # only push the latest tag on the main branch + push: "${{ github.ref == 'refs/heads/main' }}" + tags: | + ghcr.io/openconext/openconext-basecontainers/satosa-rootless:latest + ghcr.io/openconext/openconext-basecontainers/satosa-rootless:${{ github.sha }} + cache-from: type=gha + cache-to: type=gha diff --git a/README.md b/README.md index 6395b06..ce3def9 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,21 @@ We provide the following base containers which can be used in downstream project **Plain JAVA 21** ![Build status for plain JAVA 21 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-java21.yaml/badge.svg) +**Rootless JAVA 21** +![Build status for rootless JAVA 21 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-java21-rootless.yaml/badge.svg) + +The rootless image runs the JVM as an unprivileged user instead of root, and the container itself never needs to start as root either. An "openconext" user is created at build time and is the default user of the container. By default it has uid/gid 1001, this can be changed at build time with the USER_UID and USER_GID build args so it matches the uid/gid of your mounted files: + +```sh +docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +``` + +For a one-off override without rebuilding, use `--user`: + +```sh +docker run --user #1337:#1337 ghcr.io/openconext/openconext-basecontainers/java21-rootless:latest java -jar /app/app.jar +``` + ## PHP 72 images **PROD image:** @@ -38,6 +53,113 @@ We provide the following base containers which can be used in downstream project ![Build status for php82 apache2 node20 image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-php82-apache2-node20-composer2.yaml/badge.svg) +## Rootless variants + +Rootless variants of the base containers run their service as an unprivileged "openconext" user instead of root, and the container itself never needs to start as root either (running one as root only produces a warning). An "openconext" user is created at build time with uid/gid 1001 by default. This can be changed at build time with the USER_UID and USER_GID build args so it matches the uid/gid of your mounted files: + +```sh +docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +``` + +For a one-off override without rebuilding, use `--user`: + +```sh +docker run --user #1337:#1337 ghcr.io/openconext/openconext-basecontainers/-rootless:latest +``` + +### Differences from the regular images + +- The service runs as the unprivileged `openconext` user (default uid/gid 1001) and the container never needs to start as root. If you do start it as root, the entrypoint prints a warning but allows it, e.g. for debugging. +- The apache based images listen on port **8080** instead of **80** (and the vhost is `*:8080`), because an unprivileged process cannot bind to port 80. All other ports must be >= 1024 as well. +- The runtime uid/gid environment variables of the regular images are **not** used: `APACHE_UID`/`APACHE_GID` (apache and php images) and `RUNAS_UID`/`RUNAS_GID` (python3 and satosa images) are ignored. The uid/gid are fixed at build time with the `USER_UID`/`USER_GID` build args, or per-run with `docker run --user #:#`. +- The regular images import `/config/haproxy/haproxy.crt` into the CA bundle at start, which requires root. In the rootless images add the certificate at build time instead (see below). +- The php images still run `bin/console cache:clear` at every start, but since they no longer chown the app directories at start, the app files must be writable by the `openconext` user (see below). + +### Adapting a downstream image + +The rootless base images set `USER openconext`, which affects both the build and the runtime of your image: + +1. **Build steps need root again.** Steps that install packages (`apt-get`, `pecl`, `pip install`, `npm install -g`, ...) must run as root, so set `USER root` before them and restore `USER openconext` at the end. If you don't set `USER` at all, the unprivileged user of the base image is inherited and that's fine. +2. **Make writable files owned by the service user.** Files and directories the service writes to at runtime must be owned by (or writable by) `openconext`. Use `COPY --chown=openconext:openconext` for application code that contains writable dirs (e.g. the Symfony `var/cache` and `var/log` directories), and `chown` in a build step for anything else (logs, data, temp dirs). +3. **Listen on ports >= 1024.** Update `EXPOSE`, the app config and anything that assumes port 80 (vhosts, proxy configs, health checks). +4. **Certificates and other root-only setup at build time.** Anything the regular entrypoints did as root at start (importing the haproxy CA certificate, `update-ca-certificates`) has to be done in a `RUN` step instead, e.g.: + + ```dockerfile + COPY ./config/haproxy.crt /usr/local/share/ca-certificates/haproxy.crt + RUN update-ca-certificates + ``` + +5. **Entrypoint/CMD.** If you override `ENTRYPOINT`, remember that doing so clears the `CMD` inherited from the base image, so set both. You can keep the base entrypoint (which warns when run as root) by only overriding `CMD`, e.g. `CMD ["java","-jar","/app/app.jar"]`. + +Example for a Java application: + +```dockerfile +FROM ghcr.io/openconext/openconext-basecontainers/java21-rootless:latest + +# build steps need root +USER root +# ... apt-get / other installation ... + +# the jar is in the workdir, owned by the service user +COPY --chown=openconext:openconext target/*.jar /app/app.jar + +# back to the unprivileged user. Setting CMD keeps the base image's +# ENTRYPOINT (which warns when run as root) in the chain. +USER openconext +CMD ["java", "-jar", "/app/app.jar"] +``` + +Example for a Symfony / PHP application: + +```dockerfile +FROM ghcr.io/openconext/openconext-basecontainers/php85-apache2-rootless:latest + +# build steps need root +USER root +RUN apt-get update && apt-get -y install && rm -rf /var/lib/apt/lists/* + +# the app writes to var/cache and var/log at runtime, so it must be +# owned by the service user +COPY --chown=openconext:openconext --chmod=755 ./var /var/www/html/var +COPY --chown=openconext:openconext ./ /var/www/html/ + +USER openconext +``` + +### Deploying rootless containers + +- **Volume ownership.** Mounted volumes must be readable (and writable, where the service writes) by the `openconext` user. Either make the host files owned by uid/gid 1001, or rebuild the image with `--build-arg USER_UID= --build-arg USER_GID=` so the in-container user matches the existing host files. +- **Ports and routing.** The apache based images serve on 8080 instead of 80, so update `ports:`, `expose:`, proxy and loadbalancer configs, and application URLs that contain the port. +- **Kubernetes.** Containers now start as a non-root user automatically; if your PodSecurityContext enforces `runAsNonRoot`, these images satisfy it. Set `fsGroup` to the gid of the image (1001 by default) for writable volumes. +- **One-off uid/gid.** `docker run --user #:#` works without rebuilding. Note that the default workdir/app dirs are then owned by the image's build-time user, so pick a matching uid/gid if you need write access to them. + +**Rootless Apache 2** +![Build status for rootless apache2 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-apache2-rootless.yaml/badge.svg) + +**Rootless Apache 2 with shibboleth** +![Build status for rootless apache2 shibboleth production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-apache2-shibboleth-rootless.yaml/badge.svg) + +**Rootless HAProxy 2.8** (already non-root in the upstream image; this variant uses the same "openconext" user as the other rootless images. The container does not ship a config, downstream images must provide one that only binds ports >= 1024.) +![Build status for rootless haproxy28 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-haproxy28-rootless.yaml/badge.svg) + +**Rootless PHP 8.2 Apache 2** +![Build status for rootless php82 apache2 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-php82-apache2-rootless.yaml/badge.svg) + +**Rootless PHP 8.2 Apache 2 node20 composer2** +![Build status for rootless php82 apache2 node20 image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-php82-apache2-node20-composer2-rootless.yaml/badge.svg) + +**Rootless PHP 8.5 Apache 2** +![Build status for rootless php85 apache2 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-php85-apache2-rootless.yaml/badge.svg) + +**Rootless PHP 8.5 Apache 2 node24** +![Build status for rootless php85 apache2 node24 image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-php85-apache2-node24-rootless.yaml/badge.svg) + +**Rootless Python 3** (the scripts in /container-init and /container-init-post are run as the unprivileged user; the RUNAS_UID / RUNAS_GID env vars of the non-rootless image are not used, the uid/gid are set at build time instead.) +![Build status for rootless python3 production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-python3-rootless.yaml/badge.svg) + +**Rootless SATOSA** (the RUNAS_UID / RUNAS_GID env vars of the non-rootless image are not used, the uid/gid are set at build time instead.) +![Build status for rootless satosa production image](https://github.com/OpenConext/OpenConext-BaseContainers/actions/workflows/build-satosa-rootless.yaml/badge.svg) + ## Features - At every start, the php containers will recreate the symfony cache dir. diff --git a/apache2-rootless/Dockerfile b/apache2-rootless/Dockerfile new file mode 100644 index 0000000..f0c32e9 --- /dev/null +++ b/apache2-rootless/Dockerfile @@ -0,0 +1,69 @@ +FROM docker.io/library/debian:bookworm-slim + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +# Update and install packages needed +ENV DEBIAN_FRONTEND=noninteractive +RUN apt update && \ + apt -y upgrade && \ + apt -y install \ + apache2 \ + curl \ + xz-utils \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# Create the unprivileged user Apache2 will run as +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext + +# Run Apache2 as the unprivileged user on an unprivileged port +RUN sed -i \ + -e 's/^export APACHE_RUN_USER=.*/export APACHE_RUN_USER=openconext/' \ + -e 's/^export APACHE_RUN_GROUP=.*/export APACHE_RUN_GROUP=openconext/' \ + /etc/apache2/envvars \ + && printf 'Listen 8080\n' > /etc/apache2/ports.conf \ + && mkdir -p /var/run/apache2 \ + && chown -R openconext:openconext /run /var/log/apache2 /var/www/html + +# Copy the default apache config +COPY ./conf/security.conf ./conf/openconext.conf /etc/apache2/conf-available/ +COPY ./conf/default-vhost.conf /etc/apache2/sites-available/000-default.conf + +# Enable the apache2 modules we need and disable the one we don't want. +RUN a2dismod \ + status && \ + a2enmod \ + authz_groupfile \ + headers \ + proxy \ + proxy_connect \ + proxy_http \ + rewrite \ + remoteip \ + ssl \ + xml2enc && \ + a2enconf openconext + +# Copy the startup script +COPY ./bin/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh && \ + rm -f /var/www/html/index.html + +# Set the default workdir +WORKDIR /var/www/html + +# Default port to listen on +EXPOSE 8080 + +# Run as the unprivileged user +USER openconext + +# Start Apache +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apachectl", "-D", "FOREGROUND"] \ No newline at end of file diff --git a/apache2-rootless/bin/entrypoint.sh b/apache2-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..b9a5042 --- /dev/null +++ b/apache2-rootless/bin/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Delete any leftover pid files +rm -f /var/run/apache2/apache2.pid + +# If we do not define the HTTPD_CSP env var, let's set an empty one so +# Apache stops complaining in the logs +if [ -z "${HTTPD_CSP:-}" ]; then + export HTTPD_CSP='' +fi + +# Note: the non-rootless image copies /config/haproxy/haproxy.crt into +# the CA bundle at start. That requires root, so in this image add the +# certificate at build time instead. + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/apache2-rootless/conf/default-vhost.conf b/apache2-rootless/conf/default-vhost.conf new file mode 100644 index 0000000..88c09a2 --- /dev/null +++ b/apache2-rootless/conf/default-vhost.conf @@ -0,0 +1,6 @@ + + ServerAdmin webmaster@localhost + DocumentRoot /var/www/html + + +# vim: syntax=apache ts=4 sw=4 sts=4 sr noet diff --git a/apache2-rootless/conf/openconext.conf b/apache2-rootless/conf/openconext.conf new file mode 100644 index 0000000..07b0732 --- /dev/null +++ b/apache2-rootless/conf/openconext.conf @@ -0,0 +1,15 @@ +RemoteIPHeader X-Forwarded-For + +# OpenConext logformat logs the X-Real-IP, which is set by Traefik. It is the ip address of the loadbalancer in front of Traefik +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Real-IP}i\"" openconext + +ErrorLog /proc/self/fd/2 +CustomLog /proc/self/fd/1 openconext + +# Set the CSP header if it was defined as an env variable +PassEnv HTTPD_CSP + + Header unset Content-Security-Policy + Header unset X-Content-Security-Policy + Header always set Content-Security-Policy "${HTTPD_CSP}" + diff --git a/apache2-rootless/conf/security.conf b/apache2-rootless/conf/security.conf new file mode 100644 index 0000000..4bd1f5f --- /dev/null +++ b/apache2-rootless/conf/security.conf @@ -0,0 +1,3 @@ +ServerTokens Prod +ServerSignature On +TraceEnable Off diff --git a/apache2-shibboleth-rootless/Dockerfile b/apache2-shibboleth-rootless/Dockerfile new file mode 100644 index 0000000..1c52e42 --- /dev/null +++ b/apache2-shibboleth-rootless/Dockerfile @@ -0,0 +1,44 @@ +FROM ghcr.io/openconext/openconext-basecontainers/apache2-rootless:latest + +# The base image runs as the unprivileged user; build steps need root +USER root + +# Install the Shibboleth daemon and Apache2 modules +ENV DEBIAN_FRONTEND=noninteractive +RUN \ + apt update && \ + apt install -y \ + shibboleth-sp-common \ + shibboleth-sp-utils \ + libapache2-mod-shib \ + gettext \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# Enable Shib and make sure the config, socket/pid, session and log +# directories are usable by the unprivileged user (both apache and +# shibd run as that user, and the entrypoint rewrites the shibboleth2.xml +# config at start) +RUN mkdir -p /run/shibboleth /var/lib/shibboleth /var/log/shibboleth \ + && chown -R openconext:openconext /run/shibboleth /etc/shibboleth /var/lib/shibboleth /var/log/shibboleth +RUN a2enmod shib + +# Copy config files +COPY ./conf/shibboleth/* /etc/shibboleth/ + +# Copy the startup script +COPY --chmod=755 ./bin/entrypoint.sh /entrypoint.sh +COPY --chmod=755 ./bin/shib.sh /usr/local/bin/shib.sh + +# Set the default workdir +WORKDIR /var/www/html + +# Default port to listen on +EXPOSE 8080 + +# Start Shibboleth and Apache +ENTRYPOINT ["/entrypoint.sh"] +CMD ["/usr/local/bin/shib.sh"] + +# Back to the unprivileged user +USER openconext \ No newline at end of file diff --git a/apache2-shibboleth-rootless/bin/entrypoint.sh b/apache2-shibboleth-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..21ca5bb --- /dev/null +++ b/apache2-shibboleth-rootless/bin/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args in the +# base image), so there is nothing to set up at start. Warn (but +# allow) if it is overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Delete any leftover pid files +rm -f /run/apache2/apache2.pid /run/shibboleth/shibd.pid + +# If we do not define the HTTPD_CSP env var, let's set an empty one so +# Apache stops complaining in the logs +if [ -z "${HTTPD_CSP:-}" ]; then + export HTTPD_CSP='' +fi + +# Note: the non-rootless image copies /config/haproxy/haproxy.crt into +# the CA bundle at start. That requires root, so in this image add the +# certificate at build time instead. + +# We substitute some environment variables +envsubst /etc/shibboleth/shibboleth2.xml +envsubst /etc/shibboleth/localLogout.html + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/apache2-shibboleth-rootless/bin/shib.sh b/apache2-shibboleth-rootless/bin/shib.sh new file mode 100755 index 0000000..7cbfdb5 --- /dev/null +++ b/apache2-shibboleth-rootless/bin/shib.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +# Start Apache2 +apachectl -D FOREGROUND & + +# Start Shibboleth +/usr/sbin/shibd -f -F -c /etc/shibboleth/shibboleth2.xml -p /run/shibboleth/shibd.pid -w 30 \ No newline at end of file diff --git a/apache2-shibboleth-rootless/conf/shibboleth/attribute-map.xml b/apache2-shibboleth-rootless/conf/shibboleth/attribute-map.xml new file mode 100644 index 0000000..cea7916 --- /dev/null +++ b/apache2-shibboleth-rootless/conf/shibboleth/attribute-map.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apache2-shibboleth-rootless/conf/shibboleth/attribute-policy.xml b/apache2-shibboleth-rootless/conf/shibboleth/attribute-policy.xml new file mode 100644 index 0000000..f6c97a4 --- /dev/null +++ b/apache2-shibboleth-rootless/conf/shibboleth/attribute-policy.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apache2-shibboleth-rootless/conf/shibboleth/localLogout.html.dist b/apache2-shibboleth-rootless/conf/shibboleth/localLogout.html.dist new file mode 100644 index 0000000..90a3394 --- /dev/null +++ b/apache2-shibboleth-rootless/conf/shibboleth/localLogout.html.dist @@ -0,0 +1,46 @@ + + + + + ${OPENCONEXT_INSTANCENAME} - Local logout + + + + + +
+ +
+ +
+ +
+
+ +

Local Logout

+ +
+
+

+ Status of Local Logout:
+ +

+

+ You must close your browser to complete the logout process. +

+ +
+
+
+
+

This service is provided by ${OPENCONEXT_INSTANCENAME}.

+
+
+
+
+
+ + + + diff --git a/apache2-shibboleth-rootless/conf/shibboleth/shibboleth2.xml.dist b/apache2-shibboleth-rootless/conf/shibboleth/shibboleth2.xml.dist new file mode 100644 index 0000000..88e5574 --- /dev/null +++ b/apache2-shibboleth-rootless/conf/shibboleth/shibboleth2.xml.dist @@ -0,0 +1,72 @@ + + + + + + SAML2 + SAML2 Local + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apache2-shibboleth-rootless/conf/shibboleth/shibd.logger b/apache2-shibboleth-rootless/conf/shibboleth/shibd.logger new file mode 100644 index 0000000..236c82e --- /dev/null +++ b/apache2-shibboleth-rootless/conf/shibboleth/shibd.logger @@ -0,0 +1,76 @@ +# set overall behavior +log4j.rootCategory=INFO, shibd_log, warn_log + +# fairly verbose for DEBUG, so generally leave at INFO +log4j.category.XMLTooling.XMLObject=INFO +log4j.category.XMLTooling.KeyInfoResolver=INFO +log4j.category.Shibboleth.IPRange=INFO +log4j.category.Shibboleth.PropertySet=INFO + +# raise for low-level tracing of SOAP client HTTP/SSL behavior +log4j.category.XMLTooling.libcurl=INFO + +# useful categories to tune independently: +# +# tracing of SAML messages and security policies +#log4j.category.OpenSAML.MessageDecoder=DEBUG +#log4j.category.OpenSAML.MessageEncoder=DEBUG +#log4j.category.OpenSAML.SecurityPolicyRule=DEBUG +#log4j.category.XMLTooling.SOAPClient=DEBUG +# interprocess message remoting +#log4j.category.Shibboleth.Listener=DEBUG +# mapping of requests to applicationId +#log4j.category.Shibboleth.RequestMapper=DEBUG +# high level session cache operations +#log4j.category.Shibboleth.SessionCache=DEBUG +# persistent storage and caching +#log4j.category.XMLTooling.StorageService=DEBUG + +# logs XML being signed or verified if set to DEBUG +log4j.category.XMLTooling.Signature.Debugger=INFO, sig_log +log4j.additivity.XMLTooling.Signature.Debugger=false +log4j.ownAppenders.XMLTooling.Signature.Debugger=true + +# the tran log blocks the "default" appender(s) at runtime +# Level should be left at INFO for this category +log4j.category.Shibboleth-TRANSACTION=INFO, tran_log +log4j.additivity.Shibboleth-TRANSACTION=false +log4j.ownAppenders.Shibboleth-TRANSACTION=true + +# uncomment to suppress particular event types +#log4j.category.Shibboleth-TRANSACTION.AuthnRequest=WARN +#log4j.category.Shibboleth-TRANSACTION.Login=WARN +#log4j.category.Shibboleth-TRANSACTION.Logout=WARN + +# define the appenders + +log4j.appender.shibd_log=org.apache.log4j.RollingFileAppender +#log4j.appender.shibd_log.fileName=/var/log/shibboleth/shibd.log +log4j.appender.shibd_log.fileName=/proc/self/fd/1 +log4j.appender.shibd_log.maxFileSize=1000000 +log4j.appender.shibd_log.maxBackupIndex=10 +log4j.appender.shibd_log.layout=org.apache.log4j.PatternLayout +log4j.appender.shibd_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n + +log4j.appender.warn_log=org.apache.log4j.RollingFileAppender +#log4j.appender.warn_log.fileName=/var/log/shibboleth/shibd_warn.log +log4j.appender.warn_log.fileName=/proc/self/fd/1 +log4j.appender.warn_log.maxFileSize=1000000 +log4j.appender.warn_log.maxBackupIndex=10 +log4j.appender.warn_log.layout=org.apache.log4j.PatternLayout +log4j.appender.warn_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S} %p %c %x: %m%n +log4j.appender.warn_log.threshold=WARN + +log4j.appender.tran_log=org.apache.log4j.RollingFileAppender +#log4j.appender.tran_log.fileName=/var/log/shibboleth/transaction.log +log4j.appender.tran_log.fileName=/proc/self/fd/1 +log4j.appender.tran_log.maxFileSize=1000000 +log4j.appender.tran_log.maxBackupIndex=20 +log4j.appender.tran_log.layout=org.apache.log4j.PatternLayout +log4j.appender.tran_log.layout.ConversionPattern=%d{%Y-%m-%d %H:%M:%S}|%c|%m%n + +log4j.appender.sig_log=org.apache.log4j.FileAppender +#log4j.appender.sig_log.fileName=/var/log/shibboleth/signature.log +log4j.appender.sig_log.fileName=/proc/self/fd/1 +log4j.appender.sig_log.layout=org.apache.log4j.PatternLayout +log4j.appender.sig_log.layout.ConversionPattern=%m diff --git a/haproxy28-rootless/Dockerfile b/haproxy28-rootless/Dockerfile new file mode 100644 index 0000000..ec1c3b3 --- /dev/null +++ b/haproxy28-rootless/Dockerfile @@ -0,0 +1,29 @@ +FROM haproxy:2.8 + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. The base image already runs +# as an unprivileged user; this image uses the same "openconext" user as +# the other rootless base images for consistency. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +# The base image runs as the unprivileged "haproxy" user; build steps +# need root +USER root + +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext + +COPY conf/haproxy.pem /usr/local/etc/haproxy + +# Warn if running as root; hand off to CMD (haproxy). The base image +# does not ship a config: downstream images must provide haproxy.cfg, +# and since this image runs without root it must only bind ports >= 1024 +COPY ./bin/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +USER openconext +ENTRYPOINT ["/entrypoint.sh"] +CMD ["haproxy", "-f", "/usr/local/etc/haproxy/haproxy.cfg"] \ No newline at end of file diff --git a/haproxy28-rootless/bin/entrypoint.sh b/haproxy28-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..cca4657 --- /dev/null +++ b/haproxy28-rootless/bin/entrypoint.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/haproxy28-rootless/conf/haproxy.pem b/haproxy28-rootless/conf/haproxy.pem new file mode 100644 index 0000000..34aa2ac --- /dev/null +++ b/haproxy28-rootless/conf/haproxy.pem @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCTXniv7fbbcelj +HDGoMoCRlpirVdbiLUQDwGbnRa+VjohagE+HcAc42n3bncC8U8s3AfB4ONpEh9Mo +MLD8beR3puEoPxhfQmtRnxHOr80ZhGbY2qqTjDUq0YkBsnq9XTCQd4AWiWXFC1YU +xbJLe4CltWvCCcigbIbnydtE5m3/xnw+CfjE+vzC4gD4XxUwgCtUATZbxPYgldkF +Z2zbGsaM1FU9nTAZVk7u9kGxvDRsjankNOXbQpD04B2KYTzZxGgdEzbQtO/JCUAn +XjSy/yriH2z/vpWZPJMM1cHENdxv+2eIgaurRMcE5R8ZSWhHE7MNGcl1U44vtCsW +fqeS6GQFAgMBAAECggEACEU/BLCYMAyein9kkgnziTgobUUuABeGP6emnsgcaIpN +QLb+v50b6qbodVDPJkKVnwqu0jBun3X39cN1y1Tng4eMD1QrK/1wRG5m83COVCZI +F1d+RzWRLkwAImY/2SzHsvPh7mHboklWlWfa772IpMQfvg4KgmZU0+FSOvWrqrUF +Ks54ETNqki5GydjrYDGLuSwAcC/owNe45Zdx5Uu/6O+DFlowVKWgxf4r6PKQZ6xI +SoFQpV9R/yecEqAzst/CIgjFp37kI9/X1g9NkK11JIrnWRApVD97IXglItA3NWTn +LLunwdY4Qvm/TBx7PBppclRNEX30Sw/doqF25Dn0AQKBgQDCxbdsK7PvTGKHTW41 +LgusOlRWpzBRILvsmnKnMMotaYaxDPOqfySrBul7+/sMYi9d2nPiDb9T0xaIm6Lw +0VjmpA4XLA6vyLeokz4fw/1ef+qouXVz3QTC8o/sIOObilcO/Z9qfP+ONduBdhhG +AEo9/7850UC5V2oRZuyKFDY5gQKBgQDBsfqmPFLmpwWaO7xPKGTpQ7wTDy37823H +TCr1gCj60BkouUYUyYbf7j+W290ukRJupUZLTo8df8dogmwA7Qi9ET+cobAos1bD +S8xOSbK95/rdVX5H/Qgbw7yspQlye0ffmQZqAOQMmOLCHos+6YsfznM+FJ6/nWTr +KAv0LBaEhQKBgCkKIhDv0HUeojAiN5OLBTif/b9YcQzXGeHL1Va3KBKThqbttLX7 +Hk3PIglW2doNIi/jZMF/5g2Sj8/vA1E8uz+116Y4SUvuvgwOImvtDwkHaPluQnpM +WsF8/KhjcbIHXzqwZBO7DNn5LSxFJu4xB3Oy3KchGUM0UQwjpLmfcqGBAoGBAJ7k +5BturQstxLjLfCTrnCyHNkfkUfK3tTyizTLr2bI2+AyiQE1ZLN2SUnPtFTrYI2jF +CC9K2Lh4VBr1sqfnyx1egvJ46UvaIri++3DVoF2Nagwb0CY5+mbcYXC3SlKxszpI +DCz1Yh67ZHmeGNiZOVn0QDGTNM83zMvj5Dp/2FEJAoGAB3rzW5LcA+LVT6hPcgbo +sn9RGhVrqmiysNYAc7tT0/QI21QSWlQ+KUHq66S07AMm85uzUFLnw+ci1kLyFts8 +tcfp4F0hyKQADbRvzqXwWqkPjAr1VqYzJOCVeHlPtrvsaGDltM+eu0tf6ZQOQ8mH +pTmwrSTeyUvvofYpFd/B8ZE= +-----END PRIVATE KEY----- +- +-----BEGIN CERTIFICATE----- +MIID1zCCAr+gAwIBAgIUDvja2rnsTgFy+cvxfHNTWDbMRLwwDQYJKoZIhvcNAQEL +BQAwezELMAkGA1UEBhMCTkwxEDAOBgNVBAgMB1V0cmVjaHQxEDAOBgNVBAcMB1V0 +cmVjaHQxEjAQBgNVBAoMCVNVUkYgQi5WLjETMBEGA1UECwwKT3BlbkNvbmV4dDEf +MB0GA1UEAwwWKi5kZXYub3BlbmNvbmV4dC5sb2NhbDAeFw0yMzAzMjgxMTQ0Mjha +Fw0yNTEyMjIxMTQ0MjhaMHsxCzAJBgNVBAYTAk5MMRAwDgYDVQQIDAdVdHJlY2h0 +MRAwDgYDVQQHDAdVdHJlY2h0MRIwEAYDVQQKDAlTVVJGIEIuVi4xEzARBgNVBAsM +Ck9wZW5Db25leHQxHzAdBgNVBAMMFiouZGV2Lm9wZW5jb25leHQubG9jYWwwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTXniv7fbbceljHDGoMoCRlpir +VdbiLUQDwGbnRa+VjohagE+HcAc42n3bncC8U8s3AfB4ONpEh9MoMLD8beR3puEo +PxhfQmtRnxHOr80ZhGbY2qqTjDUq0YkBsnq9XTCQd4AWiWXFC1YUxbJLe4CltWvC +CcigbIbnydtE5m3/xnw+CfjE+vzC4gD4XxUwgCtUATZbxPYgldkFZ2zbGsaM1FU9 +nTAZVk7u9kGxvDRsjankNOXbQpD04B2KYTzZxGgdEzbQtO/JCUAnXjSy/yriH2z/ +vpWZPJMM1cHENdxv+2eIgaurRMcE5R8ZSWhHE7MNGcl1U44vtCsWfqeS6GQFAgMB +AAGjUzBRMB0GA1UdDgQWBBRXEU6j5ncv/nhZIPHFXGNoGPWsHzAfBgNVHSMEGDAW +gBRXEU6j5ncv/nhZIPHFXGNoGPWsHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4IBAQARl/Hu+C6argh4I9UvCaJGyQaKNH8fe7JWkailaCLZBXXi/Wpi +6vTqZmTUp03Srqkxm2ng/8UjslN7mX1AYgofqv8vUK3wrNkjiW5E6fWNw8jy+s81 +8g6UNCvorEp+6TxDciR1BK5dC2yLlhvhfayAtMxdR0mMybEhk9PCMwJSaMmrzRpm +lN765qvCcBoMc9bGdkCY+CEWV0mrVx0deFtoValOHSonU8lfGVXuAOPAFQ6IWCd/ +xYc2HYhfoShF0HcVjJhJAVysFQ+1oEus3xygGt8ywsJE+2b3c+OSnpqPjZKCopLd +QVPFkX+6L74Uug8cIwB6B0SBlsd79664YNOB +-----END CERTIFICATE----- + diff --git a/java21-rootless/Dockerfile b/java21-rootless/Dockerfile new file mode 100644 index 0000000..87fb2d3 --- /dev/null +++ b/java21-rootless/Dockerfile @@ -0,0 +1,22 @@ +FROM eclipse-temurin:21-jre-alpine + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +RUN addgroup -g "${USER_GID}" openconext \ + && adduser -D -H -u "${USER_UID}" -G openconext -s /sbin/nologin openconext + +# Default workdir, owned by the unprivileged user +WORKDIR /app +RUN chown -R openconext:openconext /app + +# Warn if running as root; hand off to CMD +COPY ./bin/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +USER openconext +ENTRYPOINT ["/entrypoint.sh"] \ No newline at end of file diff --git a/java21-rootless/bin/entrypoint.sh b/java21-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..0557653 --- /dev/null +++ b/java21-rootless/bin/entrypoint.sh @@ -0,0 +1,20 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +if [ -n "${JAVA_UID:-}" ] || [ -n "${JAVA_GID:-}" ]; then + echo "WARNING: JAVA_UID / JAVA_GID are ignored by this image." >&2 + echo " The uid/gid are fixed at build time (USER_UID / USER_GID build args)." >&2 + echo " For a one-off override use: docker run --user #:#" >&2 +fi + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/php82-apache2-node20-composer2-rootless/Dockerfile b/php82-apache2-node20-composer2-rootless/Dockerfile new file mode 100644 index 0000000..6feabc6 --- /dev/null +++ b/php82-apache2-node20-composer2-rootless/Dockerfile @@ -0,0 +1,33 @@ +FROM ghcr.io/openconext/openconext-basecontainers/php82-apache2-rootless:latest + +# The base image runs as the unprivileged user; build steps need root +USER root + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +COPY --from=node:20-slim /usr/local/bin /usr/local/bin +COPY --from=node:20-slim /opt /opt +COPY --from=node:20-slim /usr/local/lib/node_modules /usr/local/lib/node_modules + +ENV DEBIAN_FRONTEND=noninteractive +RUN \ + apt-get update && \ + apt-get -y install \ + git \ + unzip \ + vim \ + zip \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log +RUN npm install -g yarn --force + +# enable xdebug +RUN docker-php-ext-enable xdebug +COPY ./conf/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +RUN mkdir -p /var/www/html/public && chown -R openconext:openconext /var/www/html/public + +RUN rm -rf /etc/apache2/sites-enabled/* +COPY ./conf/appconf.conf /etc/apache2/sites-enabled/ + +# Back to the unprivileged user +USER openconext \ No newline at end of file diff --git a/php82-apache2-node20-composer2-rootless/conf/appconf.conf b/php82-apache2-node20-composer2-rootless/conf/appconf.conf new file mode 100644 index 0000000..2267438 --- /dev/null +++ b/php82-apache2-node20-composer2-rootless/conf/appconf.conf @@ -0,0 +1,31 @@ +ServerAdmin admin@dev.openconext.local + +DocumentRoot /var/www/html/public + +SetEnv HTTPS on +SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + + + Require all granted + + Options -MultiViews + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,L] + + + Require all granted + + +Header always set X-Content-Type-Options "nosniff" + +# Set the php application handler so mod_php interpets the files + + SetHandler application/x-httpd-php + + +ExpiresActive on +ExpiresByType font/* "access plus 1 year" +ExpiresByType image/* "access plus 6 months" +ExpiresByType text/css "access plus 1 year" +ExpiresByType text/js "access plus 1 year" diff --git a/php82-apache2-node20-composer2-rootless/conf/xdebug.ini b/php82-apache2-node20-composer2-rootless/conf/xdebug.ini new file mode 100644 index 0000000..a231734 --- /dev/null +++ b/php82-apache2-node20-composer2-rootless/conf/xdebug.ini @@ -0,0 +1,6 @@ +xdebug.mode = debug +xdebug.discover_client_host = true +xdebug.idekey = PHPSTORM +xdebug.client_host=host.docker.internal +xdebug.client_port=9003 +zend_extension=xdebug diff --git a/php82-apache2-rootless/Dockerfile b/php82-apache2-rootless/Dockerfile new file mode 100644 index 0000000..ee48af3 --- /dev/null +++ b/php82-apache2-rootless/Dockerfile @@ -0,0 +1,99 @@ +FROM docker.io/library/php:8.2-apache-bookworm +#Some default environment variables. They can be overridden when starting the container +ENV PHP_MEMORY_LIMIT=128M +ENV TZ=Europe/Amsterdam +ENV DEBIAN_FRONTEND=noninteractive + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +# Copy the Composer PHAR from the Composer image into the PHP image +COPY --from=composer:1.9.3 --chmod=755 /usr/bin/composer /usr/bin/composer + +# Install the packages we need +RUN \ + apt-get update && \ + apt-get install -y \ + curl \ + libfreetype6-dev \ + libgmp3-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libxml2 \ + libxml2-dev \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# Install the PHP 8.2 extensions we need +RUN docker-php-ext-install -j$(nproc) pdo_mysql +RUN docker-php-ext-install -j$(nproc) opcache +RUN docker-php-ext-install -j$(nproc) gmp +RUN docker-php-ext-install -j$(nproc) xml + +RUN docker-php-ext-install -j$(nproc) intl +RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd + +# install xdebug but do not enable by default +RUN pecl install xdebug + +# Clean up +# don't autoremove, becasue that will remove libraries needed for the custom-built php extensions +RUN \ + apt-get -y purge \ + icu-devtools \ + libfreetype6-dev \ + libgmp-dev \ + libgmp3-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libxml2-dev \ + zlib1g-dev \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt + +# Create the unprivileged user Apache2 will run as +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext + +# Run Apache2 as the unprivileged user on an unprivileged port +RUN sed -i \ + -e 's/: \${APACHE_RUN_USER:=www-data}/: \${APACHE_RUN_USER:=openconext}/' \ + -e 's/: \${APACHE_RUN_GROUP:=www-data}/: \${APACHE_RUN_GROUP:=openconext}/' \ + /etc/apache2/envvars \ + && grep -q 'APACHE_RUN_USER:=openconext' /etc/apache2/envvars \ + && grep -q 'APACHE_RUN_GROUP:=openconext' /etc/apache2/envvars \ + && printf 'Listen 8080\n' > /etc/apache2/ports.conf \ + && mkdir -p /var/run/apache2 \ + && chown -R openconext:openconext /run /var/log/apache2 /var/www/html + +# Enable the Apache2 modules we need and disable the ones we don't need +RUN \ + a2enmod rewrite headers expires proxy_fcgi remoteip \ + && \ + a2dismod status + +# Copy the openconext specific config +COPY ./conf/openconext.conf ./conf/security.conf /etc/apache2/conf-enabled/ +COPY ./conf/php.ini ./conf/php-cli.ini /usr/local/etc/php/ + +# Copy the startup script +COPY --chmod=755 ./bin/entrypoint.sh /entrypoint.sh + +# Set the default workdir +WORKDIR /var/www/html + +# Default port to listen on +EXPOSE 8080 + +# Run as the unprivileged user +USER openconext + +# Start Apache +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apache2-foreground"] \ No newline at end of file diff --git a/php82-apache2-rootless/bin/entrypoint.sh b/php82-apache2-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..0fe62b0 --- /dev/null +++ b/php82-apache2-rootless/bin/entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Clear the PHP cache +if [ -f "/var/www/html/bin/console" ]; then + php /var/www/html/bin/console cache:clear -e "${APP_ENV:-prod}" +fi + +# Make sure the log directories exist +if [ -d "/var/www/html/var/cache" ]; then + mkdir -p /var/www/html/var/log/ +fi +if [ -d "/var/www/html/app/cache" ]; then + mkdir -p /var/www/html/app/logs/ +fi + +# If we do not define the HTTPD_CSP env var, let's set an empty one so +# Apache stops complaining in the logs +if [ -z "${HTTPD_CSP:-}" ]; then + export HTTPD_CSP='' +fi + +# Note: the non-rootless image copies /config/haproxy/haproxy.crt into +# the CA bundle at start. That requires root, so in this image add the +# certificate at build time instead. + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/php82-apache2-rootless/conf/openconext.conf b/php82-apache2-rootless/conf/openconext.conf new file mode 100644 index 0000000..b4ba5f2 --- /dev/null +++ b/php82-apache2-rootless/conf/openconext.conf @@ -0,0 +1,16 @@ +RemoteIPHeader X-Forwarded-For + +# OpenConext logformat logs the X-Real-IP, which is set by Traefik. It is the ip address of the loadbalancer in front of Traefik +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Real-IP}i\"" openconext + +# The stepup logformat logs the stepup request id +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Real-IP}i\" \"%{x-stepup-request-id}o\"" stepup + +# Set the CSP header if it was defined as an env variable +PassEnv HTTPD_CSP + + Header unset Content-Security-Policy + Header unset X-Content-Security-Policy + Header always set Content-Security-Policy "${HTTPD_CSP}" + + diff --git a/php82-apache2-rootless/conf/php-cli.ini b/php82-apache2-rootless/conf/php-cli.ini new file mode 100644 index 0000000..ce27c95 --- /dev/null +++ b/php82-apache2-rootless/conf/php-cli.ini @@ -0,0 +1,19 @@ +; General PHP settings for all OpenConext hosts +; disable onzinnige 'X-Powered-By' HTTP header +expose_php = Off +; sane default sesssion cookie settings (are usually overridden in the Symfony config) +session.save_handler = files +session.cookie_httponly = On +session.cookie_secure = On +session.cookie_lifetime = 0 +session.gc_maxlifetime = 300 +session.cookie_samesite= "None" +session.hash_function = 1 +session.hash_bits_per_character = 6 +; The defaults for timezone is Europe/Amsterdam and the PHP_MEMORY_LIMIT=128m +; Override them when starting your container if necessary +date.timezone = ${TZ} +memory_limit = ${PHP_MEMORY_LIMIT} +disable_functions = +enable_dl = Off +allow_url_fopen = On diff --git a/php82-apache2-rootless/conf/php.ini b/php82-apache2-rootless/conf/php.ini new file mode 100644 index 0000000..c862f2b --- /dev/null +++ b/php82-apache2-rootless/conf/php.ini @@ -0,0 +1,19 @@ +; General PHP settings for all OpenConext hosts +; disable onzinnige 'X-Powered-By' HTTP header +expose_php = Off +; sane default sesssion cookie settings (are usually overridden in the Symfony config) +session.save_handler = files +session.cookie_httponly = On +session.cookie_secure = On +session.cookie_lifetime = 0 +session.gc_maxlifetime = 300 +session.cookie_samesite= "None" +session.hash_function = 1 +session.hash_bits_per_character = 6 +; The defaults for timezone is Europe/Amsterdam and the PHP_MEMORY_LIMIT=128m +; Override them when starting your container if necessary +date.timezone = ${TZ} +memory_limit = ${PHP_MEMORY_LIMIT} +disable_functions = exec,passthru,shell_exec,system,popen,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, +enable_dl = Off +allow_url_fopen = Off diff --git a/php82-apache2-rootless/conf/security.conf b/php82-apache2-rootless/conf/security.conf new file mode 100644 index 0000000..4bd1f5f --- /dev/null +++ b/php82-apache2-rootless/conf/security.conf @@ -0,0 +1,3 @@ +ServerTokens Prod +ServerSignature On +TraceEnable Off diff --git a/php85-apache2-node24-rootless/Dockerfile b/php85-apache2-node24-rootless/Dockerfile new file mode 100644 index 0000000..9390a70 --- /dev/null +++ b/php85-apache2-node24-rootless/Dockerfile @@ -0,0 +1,33 @@ +FROM ghcr.io/openconext/openconext-basecontainers/php85-apache2-rootless:latest + +# The base image runs as the unprivileged user; build steps need root +USER root + +COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +COPY --from=node:24-slim /usr/local/bin /usr/local/bin +COPY --from=node:24-slim /opt /opt +COPY --from=node:24-slim /usr/local/lib/node_modules /usr/local/lib/node_modules + +ENV DEBIAN_FRONTEND=noninteractive +RUN \ + apt-get update && \ + apt-get -y install \ + git \ + unzip \ + vim \ + zip \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log +RUN npm install -g yarn --force + +# enable xdebug +RUN docker-php-ext-enable xdebug +COPY ./conf/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini + +RUN mkdir -p /var/www/html/public && chown -R openconext:openconext /var/www/html/public + +RUN rm -rf /etc/apache2/sites-enabled/* +COPY ./conf/appconf.conf /etc/apache2/sites-enabled/ + +# Back to the unprivileged user +USER openconext \ No newline at end of file diff --git a/php85-apache2-node24-rootless/conf/appconf.conf b/php85-apache2-node24-rootless/conf/appconf.conf new file mode 100644 index 0000000..2267438 --- /dev/null +++ b/php85-apache2-node24-rootless/conf/appconf.conf @@ -0,0 +1,31 @@ +ServerAdmin admin@dev.openconext.local + +DocumentRoot /var/www/html/public + +SetEnv HTTPS on +SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 + + + Require all granted + + Options -MultiViews + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^(.*)$ index.php [QSA,L] + + + Require all granted + + +Header always set X-Content-Type-Options "nosniff" + +# Set the php application handler so mod_php interpets the files + + SetHandler application/x-httpd-php + + +ExpiresActive on +ExpiresByType font/* "access plus 1 year" +ExpiresByType image/* "access plus 6 months" +ExpiresByType text/css "access plus 1 year" +ExpiresByType text/js "access plus 1 year" diff --git a/php85-apache2-node24-rootless/conf/xdebug.ini b/php85-apache2-node24-rootless/conf/xdebug.ini new file mode 100644 index 0000000..a231734 --- /dev/null +++ b/php85-apache2-node24-rootless/conf/xdebug.ini @@ -0,0 +1,6 @@ +xdebug.mode = debug +xdebug.discover_client_host = true +xdebug.idekey = PHPSTORM +xdebug.client_host=host.docker.internal +xdebug.client_port=9003 +zend_extension=xdebug diff --git a/php85-apache2-rootless/Dockerfile b/php85-apache2-rootless/Dockerfile new file mode 100644 index 0000000..ea46530 --- /dev/null +++ b/php85-apache2-rootless/Dockerfile @@ -0,0 +1,103 @@ +FROM docker.io/library/php:8.5-apache-trixie +#Some default environment variables. They can be overridden when starting the container +ENV PHP_MEMORY_LIMIT=128M +ENV TZ=Europe/Amsterdam +ENV DEBIAN_FRONTEND=noninteractive + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +# Copy the Composer PHAR from the Composer image into the PHP image +COPY --from=docker.io/library/composer:2 --chmod=755 /usr/bin/composer /usr/bin/composer + +# Install the packages we need +RUN \ + apt-get update && \ + apt-get install -y \ + curl \ + libfreetype6-dev \ + libgmp3-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libxml2 \ + libxml2-dev \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# Install the PHP 8.5 extensions we need +RUN docker-php-ext-install -j$(nproc) pdo_mysql +RUN docker-php-ext-install -j$(nproc) gmp +RUN docker-php-ext-install -j$(nproc) xml +RUN docker-php-ext-install -j$(nproc) bcmath + +RUN docker-php-ext-install -j$(nproc) intl +RUN docker-php-ext-configure gd --with-freetype --with-jpeg && docker-php-ext-install -j$(nproc) gd + +# install xdebug but do not enable by default +RUN \ + pecl install xdebug \ + && \ + rm -rf /tmp/pear + + +# Clean up +# don't autoremove, becasue that will remove libraries needed for the custom-built php extensions +RUN \ + apt-get -y purge \ + icu-devtools \ + libfreetype6-dev \ + libgmp-dev \ + libgmp3-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libxml2-dev \ + zlib1g-dev \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# Create the unprivileged user Apache2 will run as +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext + +# Run Apache2 as the unprivileged user on an unprivileged port +RUN sed -i \ + -e 's/: \${APACHE_RUN_USER:=www-data}/: \${APACHE_RUN_USER:=openconext}/' \ + -e 's/: \${APACHE_RUN_GROUP:=www-data}/: \${APACHE_RUN_GROUP:=openconext}/' \ + /etc/apache2/envvars \ + && grep -q 'APACHE_RUN_USER:=openconext' /etc/apache2/envvars \ + && grep -q 'APACHE_RUN_GROUP:=openconext' /etc/apache2/envvars \ + && printf 'Listen 8080\n' > /etc/apache2/ports.conf \ + && mkdir -p /var/run/apache2 \ + && chown -R openconext:openconext /run /var/log/apache2 /var/www/html + +# Enable the Apache2 modules we need and disable the ones we don't need +RUN \ + a2enmod rewrite headers expires proxy_fcgi remoteip \ + && \ + a2dismod status + +# Copy the openconext specific config +COPY ./conf/openconext.conf ./conf/security.conf /etc/apache2/conf-enabled/ +COPY ./conf/php.ini ./conf/php-cli.ini /usr/local/etc/php/ + +# Copy the startup script +COPY --chmod=755 ./bin/entrypoint.sh /entrypoint.sh + +# Set the default workdir +WORKDIR /var/www/html + +# Default port to listen on +EXPOSE 8080 + +# Run as the unprivileged user +USER openconext + +# Start Apache +ENTRYPOINT ["/entrypoint.sh"] +CMD ["apache2-foreground"] \ No newline at end of file diff --git a/php85-apache2-rootless/bin/entrypoint.sh b/php85-apache2-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..0fe62b0 --- /dev/null +++ b/php85-apache2-rootless/bin/entrypoint.sh @@ -0,0 +1,37 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Clear the PHP cache +if [ -f "/var/www/html/bin/console" ]; then + php /var/www/html/bin/console cache:clear -e "${APP_ENV:-prod}" +fi + +# Make sure the log directories exist +if [ -d "/var/www/html/var/cache" ]; then + mkdir -p /var/www/html/var/log/ +fi +if [ -d "/var/www/html/app/cache" ]; then + mkdir -p /var/www/html/app/logs/ +fi + +# If we do not define the HTTPD_CSP env var, let's set an empty one so +# Apache stops complaining in the logs +if [ -z "${HTTPD_CSP:-}" ]; then + export HTTPD_CSP='' +fi + +# Note: the non-rootless image copies /config/haproxy/haproxy.crt into +# the CA bundle at start. That requires root, so in this image add the +# certificate at build time instead. + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/php85-apache2-rootless/conf/openconext.conf b/php85-apache2-rootless/conf/openconext.conf new file mode 100644 index 0000000..b4ba5f2 --- /dev/null +++ b/php85-apache2-rootless/conf/openconext.conf @@ -0,0 +1,16 @@ +RemoteIPHeader X-Forwarded-For + +# OpenConext logformat logs the X-Real-IP, which is set by Traefik. It is the ip address of the loadbalancer in front of Traefik +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Real-IP}i\"" openconext + +# The stepup logformat logs the stepup request id +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Real-IP}i\" \"%{x-stepup-request-id}o\"" stepup + +# Set the CSP header if it was defined as an env variable +PassEnv HTTPD_CSP + + Header unset Content-Security-Policy + Header unset X-Content-Security-Policy + Header always set Content-Security-Policy "${HTTPD_CSP}" + + diff --git a/php85-apache2-rootless/conf/php-cli.ini b/php85-apache2-rootless/conf/php-cli.ini new file mode 100644 index 0000000..ce27c95 --- /dev/null +++ b/php85-apache2-rootless/conf/php-cli.ini @@ -0,0 +1,19 @@ +; General PHP settings for all OpenConext hosts +; disable onzinnige 'X-Powered-By' HTTP header +expose_php = Off +; sane default sesssion cookie settings (are usually overridden in the Symfony config) +session.save_handler = files +session.cookie_httponly = On +session.cookie_secure = On +session.cookie_lifetime = 0 +session.gc_maxlifetime = 300 +session.cookie_samesite= "None" +session.hash_function = 1 +session.hash_bits_per_character = 6 +; The defaults for timezone is Europe/Amsterdam and the PHP_MEMORY_LIMIT=128m +; Override them when starting your container if necessary +date.timezone = ${TZ} +memory_limit = ${PHP_MEMORY_LIMIT} +disable_functions = +enable_dl = Off +allow_url_fopen = On diff --git a/php85-apache2-rootless/conf/php.ini b/php85-apache2-rootless/conf/php.ini new file mode 100644 index 0000000..c862f2b --- /dev/null +++ b/php85-apache2-rootless/conf/php.ini @@ -0,0 +1,19 @@ +; General PHP settings for all OpenConext hosts +; disable onzinnige 'X-Powered-By' HTTP header +expose_php = Off +; sane default sesssion cookie settings (are usually overridden in the Symfony config) +session.save_handler = files +session.cookie_httponly = On +session.cookie_secure = On +session.cookie_lifetime = 0 +session.gc_maxlifetime = 300 +session.cookie_samesite= "None" +session.hash_function = 1 +session.hash_bits_per_character = 6 +; The defaults for timezone is Europe/Amsterdam and the PHP_MEMORY_LIMIT=128m +; Override them when starting your container if necessary +date.timezone = ${TZ} +memory_limit = ${PHP_MEMORY_LIMIT} +disable_functions = exec,passthru,shell_exec,system,popen,show_source,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, +enable_dl = Off +allow_url_fopen = Off diff --git a/php85-apache2-rootless/conf/security.conf b/php85-apache2-rootless/conf/security.conf new file mode 100644 index 0000000..4bd1f5f --- /dev/null +++ b/php85-apache2-rootless/conf/security.conf @@ -0,0 +1,3 @@ +ServerTokens Prod +ServerSignature On +TraceEnable Off diff --git a/python3-rootless/Dockerfile b/python3-rootless/Dockerfile new file mode 100644 index 0000000..a0df49e --- /dev/null +++ b/python3-rootless/Dockerfile @@ -0,0 +1,48 @@ +FROM docker.io/library/python:3.11-slim-bookworm + +ENV PYTHON_VERSION_MAJOR=3 +ENV PYTHON_VERSION_MINOR=3.11 + +# Do an initial clean up and general upgrade of the distribution +ENV DEBIAN_FRONTEND=noninteractive +RUN \ + apt-get update && \ + apt-get -y dist-upgrade && \ + apt-get -y install \ + build-essential \ + bzip2 \ + curl \ + default-libmysqlclient-dev \ + git \ + libxmlsec1-dev \ + pkgconf \ + python3-dev \ + util-linux \ + xz-utils \ + && \ + apt-get -y autoremove \ + && \ + rm -rf /var/lib/apt/lists/* /var/cache/apt /var/log/alternatives.log /var/log/apt /var/log/dpkg.log + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext + +# Copy the startup script +RUN mkdir /container-init /container-init-post +COPY --chmod=0755 ./bin/entrypoint.sh /entrypoint.sh + +# Set the default workdir +WORKDIR /opt + +# Run as the unprivileged user +USER openconext + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["python3"] \ No newline at end of file diff --git a/python3-rootless/bin/entrypoint.sh b/python3-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..2b3462a --- /dev/null +++ b/python3-rootless/bin/entrypoint.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -e + +if [[ -v DEBUG ]]; then + set -x +fi + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# run custom scripts in /container-init and /container-init-post as +# the current (unprivileged) user +if [ -d "/container-init" ]; then + run-parts --verbose --regex '.*' "/container-init" +fi +if [ -d "/container-init-post" ]; then + run-parts --verbose --regex '.*' "/container-init-post" +fi + +# Hand off to CMD +exec "$@" \ No newline at end of file diff --git a/satosa-rootless/Dockerfile b/satosa-rootless/Dockerfile new file mode 100644 index 0000000..3012bbd --- /dev/null +++ b/satosa-rootless/Dockerfile @@ -0,0 +1,27 @@ +FROM docker.io/satosa:8 + +# The unprivileged user is created at build time, so the container's +# first process never needs to run as root. +# Override at build time to match the uid/gid of your mounted files: +# docker build --build-arg USER_UID=1337 --build-arg USER_GID=1337 . +ARG USER_UID=1001 +ARG USER_GID=1001 + +# The base image runs as the unprivileged "satosa" user; build steps +# need root +USER root + +# The base image entrypoint generates the SATOSA config in /etc/satosa +# at start, so it needs to be writable by the unprivileged user +RUN groupadd -g "${USER_GID}" openconext \ + && useradd -M -u "${USER_UID}" -g openconext -s /usr/sbin/nologin openconext \ + && chown -R openconext:openconext /etc/satosa + +# Warn if running as root; hand off to the base image entrypoint, which +# generates the config and starts gunicorn +COPY ./bin/entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +USER openconext +ENTRYPOINT ["/entrypoint.sh"] +CMD ["gunicorn","-b0.0.0.0:8080","satosa.wsgi:app"] \ No newline at end of file diff --git a/satosa-rootless/bin/entrypoint.sh b/satosa-rootless/bin/entrypoint.sh new file mode 100755 index 0000000..87e300a --- /dev/null +++ b/satosa-rootless/bin/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh +set -e + +# This image is intended to run without root. The unprivileged user +# was created at build time (see USER_UID / USER_GID build args), so +# there is nothing to set up at start. Warn (but allow) if it is +# overridden to run as root anyway, e.g. for debugging. +if [ "$(id -u)" = "0" ]; then + echo "WARNING: this image is intended to run without root." >&2 + echo " You are running as root (e.g. via --user root)." >&2 +fi + +# Hand off to the base image entrypoint, which generates the SATOSA +# config and starts gunicorn +exec docker-entrypoint.sh "$@" \ No newline at end of file