diff --git a/Containers/windmill/Dockerfile b/Containers/windmill/Dockerfile index f1432e6c..e1619308 100644 --- a/Containers/windmill/Dockerfile +++ b/Containers/windmill/Dockerfile @@ -27,33 +27,37 @@ RUN set -ex; \ apt-get update; \ apt-get upgrade -y; \ apt-get install -y --no-install-recommends \ + ca-certificates \ supervisor \ tzdata \ netcat-openbsd; \ rm -rf /var/lib/apt/lists/*; \ + update-ca-certificates; \ + # Copy the CA bundle to a world-readable path so uid=1000 can access it. + # /etc/ssl/certs/ is persistently mode 700 in the base image's layer and + # cannot be chmod'd in a derived image, so we copy the regenerated bundle. + cp /etc/ssl/certs/ca-certificates.crt /etc/ssl/ca-bundle.crt; \ + chmod 644 /etc/ssl/ca-bundle.crt; \ \ - # Create a single non-root windmill user (uid=10001) that owns both PostgreSQL - # and Windmill processes — no root or privilege-switching needed at runtime - groupadd -r windmill --gid=10001; \ - useradd -r -g windmill --uid=10001 --home-dir=/var/lib/windmill --shell=/sbin/nologin windmill; \ - \ - # Create required directories and give windmill user full ownership + # The base image already ships a 'windmill' user/group at uid/gid 1000. + # Create the directories our services need and hand them to that user. + # /tmp/windmill/cache is intentionally excluded: the base image pre-creates + # it as 777 so any UID can write to it; a recursive chown on ~340 MB of + # pre-installed runtimes would create a huge and unnecessary image layer. mkdir -p \ /var/lib/postgresql/data \ /var/run/postgresql \ /var/log/supervisord \ - /var/run/supervisord \ - /tmp/windmill/cache \ - /var/lib/windmill; \ + /var/run/supervisord; \ chown -R windmill:windmill \ /var/lib/postgresql \ /var/run/postgresql \ /var/log/supervisord \ - /var/run/supervisord \ - /tmp/windmill/cache \ - /var/lib/windmill; \ - chmod 750 /var/run/postgresql; \ - chmod 750 /var/log/supervisord /var/run/supervisord; \ + /var/run/supervisord; \ + chmod 1777 \ + /var/run/postgresql \ + /var/log/supervisord \ + /var/run/supervisord; \ \ # Create symlinks so postgres tools are on PATH ln -sf /usr/lib/postgresql/17/bin/postgres /usr/local/bin/postgres; \ @@ -65,9 +69,22 @@ COPY --chmod=775 healthcheck.sh /healthcheck.sh COPY --chmod=775 windmill-start.sh /windmill-start.sh COPY --chmod=664 supervisord.conf /supervisord.conf +# Redirect writable tool dirs to the persistent cache volume so the +# container can run with a read-only root filesystem. +# HOME is already correct (/home/windmill) for the pre-existing uid=1000 user. +# WINDMILL_DIR is set to the cache volume so Windmill's logs/worker dirs +# are inside the volume and writable (Docker creates the volume mount-point +# parent /tmp/windmill as root:root, making /tmp/windmill/logs inaccessible +# for uid=1000 when /tmp is a tmpfs). +ENV UV_TOOL_BIN_DIR=/tmp/windmill/cache/uv/bin \ + UV_TOOL_DIR=/tmp/windmill/cache/uv/tools \ + WINDMILL_DIR=/tmp/windmill/cache \ + SSL_CERT_FILE=/etc/ssl/ca-bundle.crt + VOLUME ["/var/lib/postgresql/data", "/tmp/windmill/cache"] -USER 10001 +# Use the pre-existing windmill user (uid=1000) from the base image +USER 1000 EXPOSE 8000 diff --git a/Containers/windmill/start.sh b/Containers/windmill/start.sh index a53d743c..014b1458 100644 --- a/Containers/windmill/start.sh +++ b/Containers/windmill/start.sh @@ -8,10 +8,29 @@ if [ -z "$BASE_URL" ]; then fi export TZ="${TZ:-Etc/UTC}" + +# The Docker daemon injects SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt and +# SSL_CERT_DIR=/etc/ssl/certs into every container, but /etc/ssl/certs/ is mode 700 +# (root only) in the base Windmill image, so uid=1000 cannot traverse it. +# Build a combined, world-readable CA bundle in the writable /tmp tmpfs and +# override all SSL cert env vars so Windmill and its sub-processes use it. +_COMBINED_BUNDLE="/tmp/ca-bundle.crt" +cat /etc/ssl/ca-bundle.crt /etc/ssl/cert.pem > "$_COMBINED_BUNDLE" 2>/dev/null || \ + cat /etc/ssl/ca-bundle.crt > "$_COMBINED_BUNDLE" 2>/dev/null || true +if [ -s "$_COMBINED_BUNDLE" ]; then + export SSL_CERT_FILE="$_COMBINED_BUNDLE" + export CURL_CA_BUNDLE="$_COMBINED_BUNDLE" + export REQUESTS_CA_BUNDLE="$_COMBINED_BUNDLE" + export NODE_EXTRA_CA_CERTS="$_COMBINED_BUNDLE" + # Unset SSL_CERT_DIR so rustls-native-certs does not also try to traverse + # the inaccessible /etc/ssl/certs/ directory. + unset SSL_CERT_DIR +fi + PGDATA="/var/lib/postgresql/data" # Initialize PostgreSQL data directory on first run. -# No su/chown needed — we already own PGDATA (uid=10001 owns the volume). +# No su/chown needed — we already own PGDATA (the windmill user owns the volume). if [ -z "$(ls -A "$PGDATA" 2>/dev/null)" ]; then echo "Initializing PostgreSQL database for Windmill..." diff --git a/php/containers.json b/php/containers.json index d8a60d66..15286b65 100644 --- a/php/containers.json +++ b/php/containers.json @@ -966,6 +966,7 @@ "image_tag": "%AIO_CHANNEL%", "display_name": "Windmill", "image": "ghcr.io/nextcloud-releases/aio-windmill", + "user": "1000", "init": true, "internal_port": "8000", "expose": [ @@ -1003,6 +1004,13 @@ "nextcloud_aio_windmill_db" ], "restart": "unless-stopped", + "read_only": true, + "tmpfs": [ + "/tmp", + "/var/run/postgresql", + "/var/log/supervisord", + "/var/run/supervisord" + ], "profiles": [ "windmill" ],