From 6a27361cefdd9527d5ac003b95826b71df850ab7 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 17:26:24 +0200 Subject: [PATCH 01/11] fix(collabora): pass server_name to Collabora, fix aliasgroup escaping and version numbering Reported in #2768: several users could not get Collabora to talk to Nextcloud, and the two options meant to configure it had no effect. - 99-run.sh read a `domain` option that does not exist in the schema (the option is `domain1`), and recent Collabora releases dropped the `domain` environment variable entirely, so `domain1` was inert. It now maps to `server_name` with a deprecation warning. - `server_name` and `cert_domain` were in the schema but never passed to Collabora. `server_name` is what fixes "Your browser has been unable to connect to the Collabora server" behind a reverse proxy. - `aliasgroup*` entries are matched by Collabora as regular expressions, so a dot needs a single backslash. The README asked for two, which can never match a real hostname. Values are now normalised (unescaped, escaped and double-escaped all give the same correct pattern) and logged at startup. Values containing other regex metacharacters are left untouched. - Added `ssl_termination`, needed when `ssl` is false but Collabora is reached over https through a reverse proxy, and `aliasgroup2`/`aliasgroup3`. - `cert_domain` is a certificate common name, so it is a string, not a bool. - Releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates from the Supervisor. Version tracking moves back to the collabora/code Docker Hub tags. Co-Authored-By: Claude --- collabora/CHANGELOG.md | 10 +++ collabora/README.md | 65 ++++++++++++++++-- collabora/config.yaml | 9 ++- collabora/rootfs/etc/cont-init.d/99-run.sh | 78 +++++++++++++++++++--- collabora/updater.json | 9 +-- 5 files changed, 148 insertions(+), 23 deletions(-) diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index 116a541546..d5851e8d77 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -1,4 +1,14 @@ +## 26.04.2.4.1 (2026-07-26) +- Fix version numbering: releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates. The version is tracked from the collabora/code Docker Hub tags again +- `server_name` is now passed to Collabora, fixing `Your browser has been unable to connect to the Collabora server` behind a reverse proxy +- `domain1` was never passed to Collabora at all (the script read a `domain` option that does not exist, and recent Collabora releases dropped that variable). It is now deprecated and applied as `server_name` +- `aliasgroup*` values are normalised: unescaped, escaped and double-escaped dots all produce the correct regex, and the value handed to Collabora is printed in the log +- Added `ssl_termination`, needed when `ssl` is false but Collabora is reached over https through a reverse proxy +- Added `aliasgroup2` and `aliasgroup3` for additional Nextcloud servers +- `cert_domain` is now a string (it is a certificate common name) and is passed to Collabora +- Documented the above, and corrected the README which asked for two backslashes where Collabora expects one + ## 1.3.0 (2026-07-16) - Update to latest version from CollaboraOnline/online (changelog : https://github.com/CollaboraOnline/online/releases) diff --git a/collabora/README.md b/collabora/README.md index 51c96cf83f..c6f04f87eb 100644 --- a/collabora/README.md +++ b/collabora/README.md @@ -52,22 +52,63 @@ Webui can be found at `https://homeassistant:9980/browser/dist/admin/admin.html` | Option | Type | Default | Description | |--------|------|---------|-------------| -| `aliasgroup1` | str | | Nextcloud external domain with escaped dots using two \ (e.g. `nextcloud_domain\\.com`) | -| `domain1` | str | | Collabora external domain with escaped dots using two \ (e.g. `code_domain\\.com`) | +| `aliasgroup1` | str | | External address of the **Nextcloud** server allowed to use this Collabora (e.g. `https://nextcloud_domain\.com:443`) | +| `aliasgroup2` | str | | A second Nextcloud server, same format as `aliasgroup1` | +| `aliasgroup3` | str | | A third Nextcloud server, same format as `aliasgroup1` | +| `server_name` | str | | External hostname (and port) of **this Collabora** server, as the browser reaches it (e.g. `code_domain.com:9980`). Set it when Collabora sits behind a reverse proxy | +| `ssl_termination` | bool | `false` | Set to `true` when `ssl` is `false` but the browser reaches Collabora over `https` through a reverse proxy | | `extra_params` | str | | Extra parameters passed to the Collabora start script | | `ssl` | bool | `false` | Enable SSL using certificates from /ssl | | `certfile` | str | `fullchain.pem` | Certificate file name located in /ssl | | `keyfile` | str | `privkey.pem` | Private key file name located in /ssl | +| `cert_domain` | str | | Common name of the self-signed certificate generated when `ssl` is `false` | | `username` | str | | Username for the Collabora admin console | | `password` | str | | Password for the Collabora admin console | | `dictionaries` | str | | Space-separated list of dictionary languages to install | +| `domain1` | str | | **Deprecated**, use `server_name` instead | + +#### About the escaped dots in `aliasgroup*` + +Collabora matches the `aliasgroup*` addresses as **regular expressions**, so a dot +has to be escaped with a **single** backslash: `next\.duckdns\.org`, not +`next\\.duckdns\\.org`. A doubled backslash means "a literal backslash followed by +any character", which never matches a real hostname, and Collabora then rejects the +Nextcloud server. + +Earlier versions of this page asked for two backslashes, which was wrong. The add-on +now normalises whatever you type, so `next.duckdns.org`, `next\.duckdns\.org` and +`next\\.duckdns\\.org` all end up as the same correct pattern. The value that is +really handed to Collabora is printed in the add-on log at startup: + +```text +Allowed Nextcloud host aliasgroup1: https://next\.duckdns\.org:443 +``` + +Values containing other regex characters (`*`, `|`, `(`, `[`, …) are left untouched, +so hand-written patterns keep working. + +`server_name` is **not** a regular expression: write it as a plain hostname, without +backslashes. ### Example configuration +Nextcloud on `https://next.duckdns.org` and Collabora reachable on +`https://code.duckdns.org:9980`, with a reverse proxy handling the certificates: + ```yaml -aliasgroup1: nextcloud_domain\\.com -domain1: code_domain\\.com -extra_params: "" +aliasgroup1: https://next\.duckdns\.org:443 +server_name: code.duckdns.org:9980 +ssl_termination: true +ssl: false +username: admin +password: changeme +``` + +Same setup, but letting the add-on serve the certificates itself from `/ssl`: + +```yaml +aliasgroup1: https://next\.duckdns\.org:443 +server_name: code.duckdns.org:9980 ssl: true certfile: fullchain.pem keyfile: privkey.pem @@ -81,7 +122,19 @@ password: changeme 1. Start the add-on and expose the Collabora server to an external domain. 1. Install and configure the Nextcloud add-on. 1. Inside Nextcloud, install the **Nextcloud Office** app. -1. In Nextcloud **Administration Settings → Office**, set the Collabora server URL to `https://yourdomain:9980` and enable **Disable certificate validation**. +1. In Nextcloud **Administration Settings → Office**, set the Collabora server URL to + the **Collabora** address, not the Nextcloud one — with the example above that is + `https://code.duckdns.org:9980` — and enable **Disable certificate validation** if + the add-on serves a self-signed certificate. +1. Add both hostnames to the Nextcloud `trusted_domains`. + +The two hostnames have different roles, and swapping them is the most common cause of +`Could not establish connection to the Collabora Online server`: + +- `aliasgroup1` is the **Nextcloud** address, it tells Collabora which server is + allowed to ask it to open documents. +- `server_name` is the **Collabora** address, it tells Collabora which URL to hand + back to the browser. ### Custom Scripts and Environment Variables diff --git a/collabora/config.yaml b/collabora/config.yaml index a152277ecb..22ac07fc4d 100644 --- a/collabora/config.yaml +++ b/collabora/config.yaml @@ -15,7 +15,7 @@ options: env_vars: [] aliasgroup1: "" certfile: fullchain.pem - domain1: "" + server_name: "" extra_params: --o:ssl.enable=false --o:user_interface.use_integration_theme=false --o:net.proto=IPv4 @@ -33,7 +33,9 @@ schema: value: str? TZ: str? aliasgroup1: str - cert_domain: bool? + aliasgroup2: str? + aliasgroup3: str? + cert_domain: str? certfile: str dictionaries: str? domain1: str? @@ -42,8 +44,9 @@ schema: password: password server_name: str? ssl: bool + ssl_termination: bool? username: str slug: collabora url: https://github.com/alexbelgium/hassio-addons -version: "1.3.0" +version: "26.04.2.4.1" webui: "[PROTO:ssl]://[HOST]:[PORT:9980]/browser/dist/admin/admin.html" diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index c09ba1f182..952e6fceb1 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -2,9 +2,58 @@ # shellcheck shell=bash set -e -if bashio::config.has_value 'domain'; then - domain="$(bashio::config 'domain')" - export domain +# coolwsd matches storage.wopi.alias_groups host/alias entries as regular +# expressions, so every dot has to be escaped with a single backslash. The value +# is typed by hand in the add-on options, where it is easy to end up with no +# escaping at all or with doubled backslashes, and a wrong pattern silently +# never matches: Collabora then refuses the Nextcloud host. Accept all three +# spellings and always hand coolwsd the canonical single-escaped form. +REGEX_METACHARACTERS='][(){}|*+?^$' +normalise_wopi_host() { + local value="$1" + + # A value containing regex metacharacters was written by someone who knows + # what they are doing, leave it exactly as-is. + if [[ "$value" == *["$REGEX_METACHARACTERS"]* ]]; then + printf '%s' "$value" + return + fi + + value="${value//\\/}" # drop whatever escaping was typed, at any depth + value="${value//./\\.}" # re-escape every dot exactly once + printf '%s' "$value" +} + +# server_name is a literal "hostname[:port]", not a regex and not a URL +normalise_server_name() { + local value="$1" + value="${value//\\/}" # never escaped, drop backslashes if any were copied over + value="${value#*://}" # strip the scheme + value="${value%%/*}" # strip any path + printf '%s' "$value" +} + +for index in 1 2 3; do + if bashio::config.has_value "aliasgroup${index}"; then + aliasgroup="$(normalise_wopi_host "$(bashio::config "aliasgroup${index}")")" + export "aliasgroup${index}=${aliasgroup}" + bashio::log.info "Allowed Nextcloud host aliasgroup${index}: ${aliasgroup}" + fi +done + +if bashio::config.has_value 'server_name'; then + server_name="$(normalise_server_name "$(bashio::config 'server_name')")" + export server_name +elif bashio::config.has_value 'domain1'; then + # domain1 predates server_name and was documented as "the Collabora external + # domain", which is what server_name means to coolwsd. It was never actually + # passed to Collabora, so honour it here rather than keep ignoring it. + server_name="$(normalise_server_name "$(bashio::config 'domain1')")" + export server_name + bashio::log.warning "domain1 is deprecated, please use server_name instead" +fi +if bashio::config.has_value 'server_name' || bashio::config.has_value 'domain1'; then + bashio::log.info "Collabora public hostname (server_name): ${server_name}" fi if bashio::config.has_value 'username'; then @@ -17,9 +66,9 @@ if bashio::config.has_value 'password'; then export password fi -if bashio::config.has_value 'aliasgroup1'; then - aliasgroup1="$(bashio::config 'aliasgroup1')" - export aliasgroup1 +if bashio::config.has_value 'cert_domain'; then + cert_domain="$(bashio::config 'cert_domain')" + export cert_domain fi if bashio::config.has_value 'dictionaries'; then @@ -45,16 +94,25 @@ if bashio::config.true 'ssl'; then bashio::log.error "Key file /ssl/${keyfile} not found" exit 1 fi - cp -f /ssl/${keyfile} /etc/coolwsd/key.pem - cp -f /ssl/${certfile} /etc/coolwsd/cert.pem - cp -f /ssl/${certfile} /etc/coolwsd/ca-chain.cert.pem + cp -f "/ssl/${keyfile}" /etc/coolwsd/key.pem + cp -f "/ssl/${certfile}" /etc/coolwsd/cert.pem + cp -f "/ssl/${certfile}" /etc/coolwsd/ca-chain.cert.pem extra_params="${extra_params/--o:ssl.enable=false/}" extra_params="${extra_params} \ - --o:ssl.enable=true + --o:ssl.enable=true \ --o:ssl.termination=false \ --o:ssl.cert_file_path=/ssl/${certfile} \ --o:ssl.key_file_path=/ssl/${keyfile} \ --o:ssl.ca_file_path=/ssl/${certfile}" +elif [[ "$extra_params" != *ssl.termination* ]]; then + # coolwsd defaults ssl.termination to false, so with ssl disabled it builds + # http:// and ws:// URLs even when the browser reached it over https through + # a reverse proxy, and the browser then refuses the connection. + if bashio::config.true 'ssl_termination'; then + extra_params="${extra_params} --o:ssl.termination=true" + elif ! bashio::config.has_value 'ssl_termination'; then + bashio::log.notice "If Collabora is reached over https through a reverse proxy, set ssl_termination to true" + fi fi export extra_params diff --git a/collabora/updater.json b/collabora/updater.json index 35f757568b..c8f8b13ede 100644 --- a/collabora/updater.json +++ b/collabora/updater.json @@ -1,8 +1,9 @@ { - "last_update": "2026-07-16", + "github_exclude": "sha256", + "last_update": "2026-07-26", "repository": "alexbelgium/hassio-addons", "slug": "collabora", - "source": "github", - "upstream_repo": "CollaboraOnline/online", - "upstream_version": "1.3.0" + "source": "dockerhub", + "upstream_repo": "collabora/code", + "upstream_version": "26.04.2.4.1" } From dd650156f3032f4a9bdb7223d36348ffbbf61245 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 17:28:12 +0200 Subject: [PATCH 02/11] fix(collabora): simplify server_name log condition Co-Authored-By: Claude --- collabora/rootfs/etc/cont-init.d/99-run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index 952e6fceb1..2ccdf8594e 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -52,7 +52,7 @@ elif bashio::config.has_value 'domain1'; then export server_name bashio::log.warning "domain1 is deprecated, please use server_name instead" fi -if bashio::config.has_value 'server_name' || bashio::config.has_value 'domain1'; then +if [ -n "${server_name:-}" ]; then bashio::log.info "Collabora public hostname (server_name): ${server_name}" fi From 789f8813d384cb5e242ee38109c4f1fb94074a38 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 18:23:14 +0200 Subject: [PATCH 03/11] fix(collabora): rebuild on a Debian base, upstream image is now distroless Upstream rebuilt collabora/code as a Nix distroless image between 26.04.2.1.1 (2026-07-01) and 26.04.2.2.1 (2026-07-18): /bin and /sbin are empty and the entrypoint is coolwsd itself. It can no longer serve as BUILD_FROM, since every RUN, s6-overlay and bashio need a shell. The addon build has been failing since, which is independent of the option fixes in this branch. Collabora is now taken from the official image as a build stage and copied onto ghcr.io/hassio-addons/debian-base: - Only the payload is copied: /usr/bin/cool*, /usr/share/coolwsd, /etc/coolwsd, /opt/collaboraoffice and /opt/cool. /etc and /nix are deliberately left out: in the distroless image /etc/resolv.conf, /etc/hosts, /etc/passwd, /etc/group and /etc/nsswitch.conf are symlinks into /nix/store, so importing them would break DNS and wipe the base image users. - coolwsd links only against glibc, libstdc++, libgcc and libm, and needs at most GLIBCXX_3.4.22, so the Debian base satisfies it; the office engine bundles its own cairo, fontconfig, curl, icu and fonts. Only openssl, fontconfig, libcap2-bin, cpio, findutils and ca-certificates are installed. - The uid/gid 1001 cool user is recreated, matching the official image. - /start-collabora-online.sh is gone, so the addon ships an equivalent launcher which also regenerates the self-signed certificate when ssl is off. - The build now runs "coolwsd --version" so a payload that cannot link fails the build instead of shipping an image that will not start. Co-Authored-By: Claude --- collabora/CHANGELOG.md | 1 + collabora/Dockerfile | 53 +++++++++++++++++- collabora/build.json | 4 +- collabora/rootfs/etc/cont-init.d/99-run.sh | 7 ++- .../rootfs/usr/local/bin/collabora-run.sh | 55 +++++++++++++++++++ 5 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 collabora/rootfs/usr/local/bin/collabora-run.sh diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index d5851e8d77..e98698830f 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -1,5 +1,6 @@ ## 26.04.2.4.1 (2026-07-26) +- Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. Collabora is now copied from the official image onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh - Fix version numbering: releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates. The version is tracked from the collabora/code Docker Hub tags again - `server_name` is now passed to Collabora, fixing `Your browser has been unable to connect to the Collabora server` behind a reverse proxy - `domain1` was never passed to Collabora at all (the script read a `domain` option that does not exist, and recent Collabora releases dropped that variable). It is now deprecated and applied as `server_name` diff --git a/collabora/Dockerfile b/collabora/Dockerfile index e6a80949e9..4f7517ff61 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -16,6 +16,23 @@ ARG BUILD_FROM ARG BUILD_VERSION + +############################################################################### +# Get Collabora Online from the official image +# +# Upstream rebuilt collabora/code as a Nix-based distroless image: /bin and +# /sbin are empty, so it can no longer be used as BUILD_FROM (s6, bashio and +# every RUN need a shell). Only the Collabora payload is taken from it, on top +# of a normal Debian base. +# +# Copy only what belongs to Collabora. Do NOT copy /etc or /nix: in that image +# /etc/resolv.conf, /etc/hosts, /etc/passwd, /etc/group and /etc/nsswitch.conf +# are symlinks into /nix/store, and importing them breaks DNS resolution and +# wipes the base image users. +############################################################################### +FROM collabora/code:latest AS collabora + +# hadolint ignore=DL3006 FROM ${BUILD_FROM} ################## @@ -55,12 +72,46 @@ COPY ha_automodules.sh /ha_automodules.sh RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh # Manual apps -ENV PACKAGES="" +# coolwsd itself only needs glibc/libstdc++ (max GLIBCXX_3.4.22); the office +# engine bundles its own cairo, fontconfig, curl, icu and fonts under +# /opt/collaboraoffice/program. openssl is used to generate the self-signed +# certificate when the ssl option is off, cpio and findutils by the jail setup. +ENV PACKAGES="ca-certificates cpio findutils fontconfig libcap2-bin libstdc++6 openssl tzdata" # Automatic apps & bashio COPY ha_autoapps.sh /ha_autoapps.sh RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" || true && rm /ha_autoapps.sh +# Collabora Online payload, taken from the official image. COPY --from keeps the +# numeric ownership, so /opt/cool and /etc/coolwsd arrive already owned by 1001. +COPY --from=collabora /usr/bin/coolwsd /usr/bin/coolforkit-caps /usr/bin/coolforkit-ns /usr/bin/coolmount /usr/bin/ +COPY --from=collabora /usr/share/coolwsd /usr/share/coolwsd +COPY --from=collabora /etc/coolwsd /etc/coolwsd +COPY --from=collabora /opt/collaboraoffice /opt/collaboraoffice +COPY --from=collabora /opt/cool /opt/cool + +# Recreate the runtime user the official image declares (uid/gid 1001), and the +# per-container state upstream sets up in its own final build stage. +RUN \ + groupadd --system --gid 1001 cool && \ + useradd --system --uid 1001 --gid 1001 --home-dir /opt/cool --shell /usr/sbin/nologin cool && \ + mkdir -p /opt/cool/child-roots /opt/cool/cache && \ + chown -R 1001:1001 /opt/cool /etc/coolwsd && \ + chmod 640 /etc/coolwsd/coolwsd.xml && \ + touch /var/log/coolwsd.log && \ + chown 1001:1001 /var/log/coolwsd.log && \ + # the WOPI proof key must be unique per container, not baked into the image + rm -rf /etc/coolwsd/proof_key* && \ + fc-cache /opt/collaboraoffice/share/fonts/truetype > /dev/null 2>&1 || true + +# Fail the build rather than ship an image that cannot start: ha_autoapps.sh is +# invoked with "|| true", and coolwsd comes from an image built against a +# different libc, so both are worth proving here. +RUN \ + command -v openssl > /dev/null && \ + command -v su > /dev/null && \ + /usr/bin/coolwsd --version + ################ # 4 Entrypoint # ################ diff --git a/collabora/build.json b/collabora/build.json index f70552aa00..794de9e1ff 100644 --- a/collabora/build.json +++ b/collabora/build.json @@ -1,6 +1,6 @@ { "build_from": { - "aarch64": "collabora/code:latest-arm64", - "amd64": "collabora/code:latest-amd64" + "aarch64": "ghcr.io/hassio-addons/debian-base:9.3.0", + "amd64": "ghcr.io/hassio-addons/debian-base:9.3.0" } } diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index 2ccdf8594e..5b1522dd38 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -141,4 +141,9 @@ chown -R 1001 /etc/coolwsd chmod -R 755 /opt/cool/systemplate bashio::log.info "Starting Collabora Online..." -su -p -s /bin/bash "$(getent passwd 1001 | cut -d: -f1)" -c "/start-collabora-online.sh" +# coolwsd refuses to run as root. The official image used to ship +# /start-collabora-online.sh, which is gone since it became distroless, so the +# add-on provides its own launcher. It reads everything from the environment, +# which su -p preserves. +export HOME=/opt/cool +su -p -s /bin/bash cool -c /usr/local/bin/collabora-run.sh diff --git a/collabora/rootfs/usr/local/bin/collabora-run.sh b/collabora/rootfs/usr/local/bin/collabora-run.sh new file mode 100644 index 0000000000..a3264ebe11 --- /dev/null +++ b/collabora/rootfs/usr/local/bin/collabora-run.sh @@ -0,0 +1,55 @@ +#!/bin/bash +# shellcheck shell=bash +# +# Launch coolwsd. +# +# The official image used to ship /start-collabora-online.sh and set it as its +# entrypoint. Since the move to a distroless image that script is gone, so the +# add-on provides its own equivalent. It is invoked as uid 1001 by +# /etc/cont-init.d/99-run.sh and takes everything from the environment, which +# avoids re-quoting extra_params through su. +set -e + +# Collabora serves https itself unless the add-on already installed real +# certificates, in which case 99-run.sh exports DONT_GEN_SSL_CERT. +cert_params="" +if [ -z "${DONT_GEN_SSL_CERT:-}" ]; then + SSL_DIR="/tmp/ssl" + mkdir -p "${SSL_DIR}/certs/ca" "${SSL_DIR}/certs/servers/localhost" "${SSL_DIR}/certs/tmp" + + openssl genrsa -out "${SSL_DIR}/certs/ca/root.key.pem" 2048 + openssl req -x509 -new -nodes \ + -key "${SSL_DIR}/certs/ca/root.key.pem" -days 9131 \ + -out "${SSL_DIR}/certs/ca/root.crt.pem" \ + -subj "/C=DE/ST=BW/L=Stuttgart/O=Dummy Authority/CN=Dummy Authority" + + openssl genrsa -out "${SSL_DIR}/certs/servers/localhost/privkey.pem" 2048 + openssl req -new -sha256 \ + -key "${SSL_DIR}/certs/servers/localhost/privkey.pem" \ + -out "${SSL_DIR}/certs/tmp/localhost.csr.pem" \ + -subj "/C=DE/ST=BW/L=Stuttgart/O=Dummy Authority/CN=${cert_domain:-localhost}" + openssl x509 -req -days 9131 \ + -in "${SSL_DIR}/certs/tmp/localhost.csr.pem" \ + -CA "${SSL_DIR}/certs/ca/root.crt.pem" \ + -CAkey "${SSL_DIR}/certs/ca/root.key.pem" -CAcreateserial \ + -out "${SSL_DIR}/certs/servers/localhost/cert.pem" + + cert_params="--o:ssl.cert_file_path=${SSL_DIR}/certs/servers/localhost/cert.pem \ + --o:ssl.key_file_path=${SSL_DIR}/certs/servers/localhost/privkey.pem \ + --o:ssl.ca_file_path=${SSL_DIR}/certs/ca/root.crt.pem" +fi + +# Flags mirror the entrypoint of the official image. extra_params is expanded +# last so that add-on options and user overrides win. +# shellcheck disable=SC2086 +exec /usr/bin/coolwsd \ + --version \ + --use-env-vars \ + ${cert_params} \ + --o:sys_template_path=/opt/cool/systemplate \ + --o:child_root_path=/opt/cool/child-roots \ + --o:file_server_root_path=/usr/share/coolwsd \ + --o:cache_files.path=/opt/cool/cache \ + --o:logging.color=false \ + --o:stop_on_config_change=true \ + ${extra_params:-} From 10d32afa69dd54d573a79d5e1aea9e2f7776d1c8 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 18:30:45 +0200 Subject: [PATCH 04/11] fix(collabora): keep collabora/code:latest as BUILD_FROM in build.json build.json is where every add-on in this repo records the upstream image it tracks, and it is what the updater bot rewrites. Hardcoding the Collabora tag in the Dockerfile and putting the Debian base in build.json inverted that. BUILD_FROM is now collabora/code:latest again and feeds the build stage the payload is copied from; the Debian runtime base is named in the Dockerfile, where it is an implementation detail of the add-on rather than the upstream being tracked. Co-Authored-By: Claude --- collabora/CHANGELOG.md | 2 +- collabora/Dockerfile | 17 ++++++++++------- collabora/build.json | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index e98698830f..ced06271ec 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -1,6 +1,6 @@ ## 26.04.2.4.1 (2026-07-26) -- Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. Collabora is now copied from the official image onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh +- Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. collabora/code:latest stays the tracked upstream image in build.json, but is now a build stage whose payload is copied onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh - Fix version numbering: releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates. The version is tracked from the collabora/code Docker Hub tags again - `server_name` is now passed to Collabora, fixing `Your browser has been unable to connect to the Collabora server` behind a reverse proxy - `domain1` was never passed to Collabora at all (the script read a `domain` option that does not exist, and recent Collabora releases dropped that variable). It is now deprecated and applied as `server_name` diff --git a/collabora/Dockerfile b/collabora/Dockerfile index 4f7517ff61..41b3540060 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -18,22 +18,25 @@ ARG BUILD_FROM ARG BUILD_VERSION ############################################################################### -# Get Collabora Online from the official image +# Get Collabora Online from the official image (BUILD_FROM, see build.json) # # Upstream rebuilt collabora/code as a Nix-based distroless image: /bin and -# /sbin are empty, so it can no longer be used as BUILD_FROM (s6, bashio and -# every RUN need a shell). Only the Collabora payload is taken from it, on top -# of a normal Debian base. +# /sbin are empty, so it can no longer be the base of the add-on itself, as s6, +# bashio and every RUN need a shell. It stays the tracked upstream image, and +# only the Collabora payload is copied out of it onto a Debian runtime. # # Copy only what belongs to Collabora. Do NOT copy /etc or /nix: in that image # /etc/resolv.conf, /etc/hosts, /etc/passwd, /etc/group and /etc/nsswitch.conf # are symlinks into /nix/store, and importing them breaks DNS resolution and # wipes the base image users. ############################################################################### -FROM collabora/code:latest AS collabora - # hadolint ignore=DL3006 -FROM ${BUILD_FROM} +FROM ${BUILD_FROM} AS collabora + +############################################################################### +# Build the actual add-on on a base that has a shell +############################################################################### +FROM ghcr.io/hassio-addons/debian-base:9.3.0 ################## # 2 Modify Image # diff --git a/collabora/build.json b/collabora/build.json index 794de9e1ff..76a11401e7 100644 --- a/collabora/build.json +++ b/collabora/build.json @@ -1,6 +1,6 @@ { "build_from": { - "aarch64": "ghcr.io/hassio-addons/debian-base:9.3.0", - "amd64": "ghcr.io/hassio-addons/debian-base:9.3.0" + "aarch64": "collabora/code:latest", + "amd64": "collabora/code:latest" } } From 0e431c8281529edcc0b95d7c83bf54cb2f0681fa Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 18:54:27 +0200 Subject: [PATCH 05/11] fix(collabora): run the build smoke test as the cool user coolwsd refuses to start as root, so "coolwsd --version" failed the build even though it proved what it was there to prove: the payload copied out of the distroless image links and executes on the Debian base. Run it through su as uid 1001, which is also exactly how 99-run.sh launches it. Also drop --system from the useradd/groupadd, which only produced a "uid 1001 is greater than SYS_UID_MAX 999" warning. Co-Authored-By: Claude --- collabora/Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/collabora/Dockerfile b/collabora/Dockerfile index 41b3540060..a692f925be 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -96,8 +96,8 @@ COPY --from=collabora /opt/cool /opt/cool # Recreate the runtime user the official image declares (uid/gid 1001), and the # per-container state upstream sets up in its own final build stage. RUN \ - groupadd --system --gid 1001 cool && \ - useradd --system --uid 1001 --gid 1001 --home-dir /opt/cool --shell /usr/sbin/nologin cool && \ + groupadd --gid 1001 cool && \ + useradd --uid 1001 --gid 1001 --no-create-home --home-dir /opt/cool --shell /usr/sbin/nologin cool && \ mkdir -p /opt/cool/child-roots /opt/cool/cache && \ chown -R 1001:1001 /opt/cool /etc/coolwsd && \ chmod 640 /etc/coolwsd/coolwsd.xml && \ @@ -109,11 +109,11 @@ RUN \ # Fail the build rather than ship an image that cannot start: ha_autoapps.sh is # invoked with "|| true", and coolwsd comes from an image built against a -# different libc, so both are worth proving here. +# different libc, so both are worth proving here. coolwsd refuses to run as +# root, so this runs it exactly the way 99-run.sh does. RUN \ command -v openssl > /dev/null && \ - command -v su > /dev/null && \ - /usr/bin/coolwsd --version + su -s /bin/bash cool -c "/usr/bin/coolwsd --version" ################ # 4 Entrypoint # From eef927d485e5a94af8cd8581bb9c7179ae64ddd1 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 19:26:01 +0200 Subject: [PATCH 06/11] fix(collabora): check linkage with ldd instead of booting coolwsd "coolwsd --version" does not short-circuit: it runs a full initialisation and tries to create a jail, which fails in a build layer because the --o: paths the launcher passes are absent, so it looked for /usr/bin/jails. It did prove the binaries link against the Debian base, but booting Collabora is the wrong check for a build step. ldd asserts the same thing directly: every NEEDED library of coolwsd, coolforkit-ns and coolmount resolves on this base. The loop uses an if rather than "grep && exit 1" so that a clean result does not leave the loop with grep's non-zero status and fail the good case. Co-Authored-By: Claude --- collabora/Dockerfile | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/collabora/Dockerfile b/collabora/Dockerfile index a692f925be..f092727ce7 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -107,13 +107,19 @@ RUN \ rm -rf /etc/coolwsd/proof_key* && \ fc-cache /opt/collaboraoffice/share/fonts/truetype > /dev/null 2>&1 || true -# Fail the build rather than ship an image that cannot start: ha_autoapps.sh is -# invoked with "|| true", and coolwsd comes from an image built against a -# different libc, so both are worth proving here. coolwsd refuses to run as -# root, so this runs it exactly the way 99-run.sh does. +# Fail the build rather than ship an image that cannot start. ha_autoapps.sh is +# invoked with "|| true", so a missing package would go unnoticed, and the +# binaries were built inside the upstream Nix image against a different libc. +# ldd is the right check here: it resolves every NEEDED library against this +# base without booting Collabora, which would try to build a jail. RUN \ command -v openssl > /dev/null && \ - su -s /bin/bash cool -c "/usr/bin/coolwsd --version" + command -v su > /dev/null && \ + for binary in /usr/bin/coolwsd /usr/bin/coolforkit-ns /usr/bin/coolmount; do \ + if ldd "$binary" | grep "not found"; then \ + echo "unresolved shared libraries in $binary" && exit 1; \ + fi; \ + done ################ # 4 Entrypoint # From c203703ead65e8ead8bcb27e3c5f837c685d4f74 Mon Sep 17 00:00:00 2001 From: alexbelgium Date: Sun, 26 Jul 2026 19:26:22 +0200 Subject: [PATCH 07/11] fix(collabora): declare SHELL with pipefail explicitly The base image already sets it, but hadolint cannot see an inherited SHELL (DL4006), and the ldd linkage check relies on pipefail to notice a failing ldd. Co-Authored-By: Claude --- collabora/Dockerfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/collabora/Dockerfile b/collabora/Dockerfile index f092727ce7..387ca84341 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -38,6 +38,10 @@ FROM ${BUILD_FROM} AS collabora ############################################################################### FROM ghcr.io/hassio-addons/debian-base:9.3.0 +# Inherited from the base, but declared explicitly: the linkage check below +# relies on pipefail to notice a failing ldd. +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + ################## # 2 Modify Image # ################## From c10801ae751b45ce72788245625d8570700e9311 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 18:48:56 +0200 Subject: [PATCH 08/11] fix(collabora): pin BUILD_FROM per arch and restore the payload capabilities build.json named the multi-arch collabora/code:latest for both architectures. The builder never passes --platform -- it runs each architecture on its own native runner -- so BUILD_FROM is the only thing selecting which binaries end up in the add-on. That resolves correctly today only because the runner architecture happens to match the target. Name collabora/code:latest-amd64 and collabora/code:latest-arm64, which are published in lockstep with latest. The official image sets file capabilities on two binaries, and COPY --from does not carry extended attributes, so they arrived stripped: coolforkit-caps cap_chown,cap_fowner,cap_sys_chroot=ep coolmount cap_sys_admin=ep Without them coolwsd starts and serves the admin console, but cannot chroot a kit process, so no document ever opens. Reapply and verify them. Replace the smoke test, which is why the build is currently red: coolwsd refuses to run as root (exit 78), and --version does not exit anyway, since the official entrypoint passes it to the long-running server. Check instead that every binary resolves its libraries against the Debian base. For ssl: true, hand Collabora the certificate copies in /etc/coolwsd rather than /ssl. coolwsd runs as uid 1001 and a private key in /ssl is commonly root-only, so it could not be read; the copies were already being made and chowned, but nothing pointed at them. Co-Authored-By: Claude --- collabora/CHANGELOG.md | 5 +- collabora/Dockerfile | 73 ++++++++++++++++++---- collabora/build.json | 4 +- collabora/rootfs/etc/cont-init.d/99-run.sh | 11 +++- 4 files changed, 76 insertions(+), 17 deletions(-) diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index ced06271ec..db45763362 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -1,6 +1,9 @@ ## 26.04.2.4.1 (2026-07-26) -- Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. collabora/code:latest stays the tracked upstream image in build.json, but is now a build stage whose payload is copied onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh +- Rebuild on a Debian base: upstream turned collabora/code into a distroless image with no shell, which broke the addon build entirely. collabora/code stays the tracked upstream image in build.json, but is now a build stage whose payload is copied onto ghcr.io/hassio-addons/debian-base, and the addon ships its own launcher in place of the removed /start-collabora-online.sh +- build.json names the architecture explicitly again (`collabora/code:latest-amd64` and `collabora/code:latest-arm64`). The builder never passes `--platform`, so the tag is the only thing that decides which binaries land in the addon +- Restore the file capabilities on `coolforkit-caps` and `coolmount`. The official image carries them as extended attributes, which `COPY --from` does not transfer, and without them Collabora starts but cannot open any document +- The certificates for `ssl: true` are read from the copies in /etc/coolwsd rather than from /ssl directly, which Collabora could not read as uid 1001 when the private key is root-only - Fix version numbering: releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates. The version is tracked from the collabora/code Docker Hub tags again - `server_name` is now passed to Collabora, fixing `Your browser has been unable to connect to the Collabora server` behind a reverse proxy - `domain1` was never passed to Collabora at all (the script read a `domain` option that does not exist, and recent Collabora releases dropped that variable). It is now deprecated and applied as `server_name` diff --git a/collabora/Dockerfile b/collabora/Dockerfile index 387ca84341..c13571a3df 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -20,6 +20,16 @@ ARG BUILD_VERSION ############################################################################### # Get Collabora Online from the official image (BUILD_FROM, see build.json) # +# build.json pins the architecture explicitly, collabora/code:latest-amd64 and +# collabora/code:latest-arm64, rather than the multi-arch collabora/code:latest. +# The builder never passes --platform: it runs the amd64 build on a native amd64 +# runner and the aarch64 build on a native arm runner, so the only thing that +# decides which Collabora binaries end up in the add-on is this tag. With the +# multi-arch tag that happens to resolve correctly, but only for as long as the +# runner architecture keeps matching the target, and a mismatch would silently +# produce an image full of foreign-architecture binaries. The per-arch tags are +# published in lockstep with latest, so nothing is lost by naming them. +# # Upstream rebuilt collabora/code as a Nix-based distroless image: /bin and # /sbin are empty, so it can no longer be the base of the add-on itself, as s6, # bashio and every RUN need a shell. It stays the tracked upstream image, and @@ -38,8 +48,12 @@ FROM ${BUILD_FROM} AS collabora ############################################################################### FROM ghcr.io/hassio-addons/debian-base:9.3.0 -# Inherited from the base, but declared explicitly: the linkage check below -# relies on pipefail to notice a failing ldd. +# Inherited from the base, declared here so it is visible to hadolint and to +# anyone adding a pipe below. Note that the linkage check does NOT pipe into +# grep: with pipefail an ldd that exits non-zero (a binary it cannot handle at +# all) would make the pipeline fail even though grep matched, and "if" would +# then read that as "no unresolved libraries" -- the one case worth catching. +# Capturing the output and matching it with case avoids the question entirely. SHELL ["/bin/bash", "-o", "pipefail", "-c"] ################## @@ -111,18 +125,55 @@ RUN \ rm -rf /etc/coolwsd/proof_key* && \ fc-cache /opt/collaboraoffice/share/fonts/truetype > /dev/null 2>&1 || true -# Fail the build rather than ship an image that cannot start. ha_autoapps.sh is -# invoked with "|| true", so a missing package would go unnoticed, and the -# binaries were built inside the upstream Nix image against a different libc. -# ldd is the right check here: it resolves every NEEDED library against this -# base without booting Collabora, which would try to build a jail. +# Restore the file capabilities. The official image carries them as extended +# attributes on two binaries: +# coolforkit-caps cap_chown,cap_fowner,cap_sys_chroot=ep +# coolmount cap_sys_admin=ep +# COPY --from does not transfer extended attributes, so both arrive stripped. +# Nothing about the build notices: coolwsd starts and serves the admin console, +# but every document fails to open because it cannot chroot a kit process. Set +# them again and check they stuck, so a builder without xattr support fails here +# instead of shipping an add-on that only looks like it works. +# +# cap_sys_admin on coolmount only takes effect if the container is given +# SYS_ADMIN, which the add-on does not request; without it Collabora copies its +# child roots instead of bind-mounting them, which is slower but works. +RUN \ + setcap "cap_chown,cap_fowner,cap_sys_chroot=ep" /usr/bin/coolforkit-caps && \ + setcap "cap_sys_admin=ep" /usr/bin/coolmount && \ + caps="$(getcap /usr/bin/coolforkit-caps /usr/bin/coolmount)" && \ + case "$caps" in \ + *cap_sys_chroot*) ;; \ + *) echo "coolforkit-caps lost its capabilities: ${caps}"; exit 1 ;; \ + esac && \ + case "$caps" in \ + *cap_sys_admin*) ;; \ + *) echo "coolmount lost its capabilities: ${caps}"; exit 1 ;; \ + esac + +# Fail the build rather than ship an image that cannot start: ha_autoapps.sh is +# invoked with "|| true", and the payload was linked against the libraries of +# the distroless image, so both are worth proving here. +# +# coolwsd itself cannot be executed as a smoke test. It refuses to run as root +# ("Do not run as root. Please run as cool user.", exit 78), and --version does +# not exit either -- the official entrypoint passes it to the long-running +# server to get the version into the log. Checking that every binary resolves +# its libraries proves the same thing and terminates. RUN \ command -v openssl > /dev/null && \ command -v su > /dev/null && \ - for binary in /usr/bin/coolwsd /usr/bin/coolforkit-ns /usr/bin/coolmount; do \ - if ldd "$binary" | grep "not found"; then \ - echo "unresolved shared libraries in $binary" && exit 1; \ - fi; \ + for binary in \ + /usr/bin/coolwsd \ + /usr/bin/coolforkit-caps \ + /usr/bin/coolforkit-ns \ + /usr/bin/coolmount \ + /opt/collaboraoffice/program/soffice.bin \ + /opt/collaboraoffice/program/libmergedlo.so; do \ + libs="$(ldd "$binary" 2>&1)"; \ + case "$libs" in \ + *"not found"*) echo "Unresolved libraries in ${binary}:"; echo "$libs"; exit 1 ;; \ + esac; \ done ################ diff --git a/collabora/build.json b/collabora/build.json index 76a11401e7..f70552aa00 100644 --- a/collabora/build.json +++ b/collabora/build.json @@ -1,6 +1,6 @@ { "build_from": { - "aarch64": "collabora/code:latest", - "amd64": "collabora/code:latest" + "aarch64": "collabora/code:latest-arm64", + "amd64": "collabora/code:latest-amd64" } } diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index 5b1522dd38..23b13dc252 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -94,16 +94,21 @@ if bashio::config.true 'ssl'; then bashio::log.error "Key file /ssl/${keyfile} not found" exit 1 fi + # Point Collabora at the copies rather than at /ssl. coolwsd runs as uid + # 1001 and /ssl is mounted read-only with whatever ownership the certificate + # tooling left behind, which for a private key is commonly root-only. These + # copies are picked up by the chown below, so they are readable regardless. cp -f "/ssl/${keyfile}" /etc/coolwsd/key.pem cp -f "/ssl/${certfile}" /etc/coolwsd/cert.pem cp -f "/ssl/${certfile}" /etc/coolwsd/ca-chain.cert.pem + chmod 600 /etc/coolwsd/key.pem extra_params="${extra_params/--o:ssl.enable=false/}" extra_params="${extra_params} \ --o:ssl.enable=true \ --o:ssl.termination=false \ - --o:ssl.cert_file_path=/ssl/${certfile} \ - --o:ssl.key_file_path=/ssl/${keyfile} \ - --o:ssl.ca_file_path=/ssl/${certfile}" + --o:ssl.cert_file_path=/etc/coolwsd/cert.pem \ + --o:ssl.key_file_path=/etc/coolwsd/key.pem \ + --o:ssl.ca_file_path=/etc/coolwsd/ca-chain.cert.pem" elif [[ "$extra_params" != *ssl.termination* ]]; then # coolwsd defaults ssl.termination to false, so with ssl disabled it builds # http:// and ws:// URLs even when the browser reached it over https through From ae2c19074aa3b44abdaea627881a0e4c8e1063dc Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:41:22 +0200 Subject: [PATCH 09/11] fix(collabora): stop masking required setup failures --- collabora/Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/collabora/Dockerfile b/collabora/Dockerfile index c13571a3df..a14f258332 100644 --- a/collabora/Dockerfile +++ b/collabora/Dockerfile @@ -101,7 +101,7 @@ ENV PACKAGES="ca-certificates cpio findutils fontconfig libcap2-bin libstdc++6 o # Automatic apps & bashio COPY ha_autoapps.sh /ha_autoapps.sh -RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" || true && rm /ha_autoapps.sh +RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh # Collabora Online payload, taken from the official image. COPY --from keeps the # numeric ownership, so /opt/cool and /etc/coolwsd arrive already owned by 1001. @@ -123,7 +123,7 @@ RUN \ chown 1001:1001 /var/log/coolwsd.log && \ # the WOPI proof key must be unique per container, not baked into the image rm -rf /etc/coolwsd/proof_key* && \ - fc-cache /opt/collaboraoffice/share/fonts/truetype > /dev/null 2>&1 || true + (fc-cache /opt/collaboraoffice/share/fonts/truetype > /dev/null 2>&1 || true) # Restore the file capabilities. The official image carries them as extended # attributes on two binaries: @@ -151,9 +151,9 @@ RUN \ *) echo "coolmount lost its capabilities: ${caps}"; exit 1 ;; \ esac -# Fail the build rather than ship an image that cannot start: ha_autoapps.sh is -# invoked with "|| true", and the payload was linked against the libraries of -# the distroless image, so both are worth proving here. +# Fail the build rather than ship an image with unresolved runtime dependencies. +# The payload was linked against the libraries of the distroless image, so each +# executable and shared library is checked against the Debian runtime. # # coolwsd itself cannot be executed as a smoke test. It refuses to run as root # ("Do not run as root. Please run as cool user.", exit 78), and --version does From ff4df36fbac24aa0c469bcefbd4e9a3065cbc6c8 Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:47:15 +0200 Subject: [PATCH 10/11] fix(collabora): enforce the ssl option independently of extra_params --- collabora/rootfs/etc/cont-init.d/99-run.sh | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/collabora/rootfs/etc/cont-init.d/99-run.sh b/collabora/rootfs/etc/cont-init.d/99-run.sh index 23b13dc252..95b0d33240 100755 --- a/collabora/rootfs/etc/cont-init.d/99-run.sh +++ b/collabora/rootfs/etc/cont-init.d/99-run.sh @@ -81,6 +81,12 @@ if bashio::config.has_value 'extra_params'; then extra_params="$(bashio::config 'extra_params')" fi +# The add-on ssl option is authoritative. coolwsd defaults ssl.enable to true, +# so merely clearing extra_params used to re-enable its self-signed HTTPS even +# when ssl was false, which breaks reverse proxies expecting plain HTTP. +extra_params="${extra_params//--o:ssl.enable=false/}" +extra_params="${extra_params//--o:ssl.enable=true/}" + if bashio::config.true 'ssl'; then export DONT_GEN_SSL_CERT=true bashio::config.require.ssl @@ -102,21 +108,22 @@ if bashio::config.true 'ssl'; then cp -f "/ssl/${certfile}" /etc/coolwsd/cert.pem cp -f "/ssl/${certfile}" /etc/coolwsd/ca-chain.cert.pem chmod 600 /etc/coolwsd/key.pem - extra_params="${extra_params/--o:ssl.enable=false/}" extra_params="${extra_params} \ --o:ssl.enable=true \ --o:ssl.termination=false \ --o:ssl.cert_file_path=/etc/coolwsd/cert.pem \ --o:ssl.key_file_path=/etc/coolwsd/key.pem \ --o:ssl.ca_file_path=/etc/coolwsd/ca-chain.cert.pem" -elif [[ "$extra_params" != *ssl.termination* ]]; then - # coolwsd defaults ssl.termination to false, so with ssl disabled it builds - # http:// and ws:// URLs even when the browser reached it over https through - # a reverse proxy, and the browser then refuses the connection. - if bashio::config.true 'ssl_termination'; then - extra_params="${extra_params} --o:ssl.termination=true" - elif ! bashio::config.has_value 'ssl_termination'; then - bashio::log.notice "If Collabora is reached over https through a reverse proxy, set ssl_termination to true" +else + extra_params="${extra_params} --o:ssl.enable=false" + if [[ "$extra_params" != *ssl.termination* ]]; then + # With SSL disabled, termination must be enabled when a reverse proxy + # exposes Collabora over https, otherwise it advertises http/ws URLs. + if bashio::config.true 'ssl_termination'; then + extra_params="${extra_params} --o:ssl.termination=true" + elif ! bashio::config.has_value 'ssl_termination'; then + bashio::log.notice "If Collabora is reached over https through a reverse proxy, set ssl_termination to true" + fi fi fi From f79ae66d73238747ab92aab05a8b03775f379a6a Mon Sep 17 00:00:00 2001 From: Alexandre <44178713+alexbelgium@users.noreply.github.com> Date: Sun, 26 Jul 2026 19:48:57 +0200 Subject: [PATCH 11/11] docs(collabora): document authoritative ssl handling --- collabora/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/collabora/CHANGELOG.md b/collabora/CHANGELOG.md index db45763362..6792738832 100644 --- a/collabora/CHANGELOG.md +++ b/collabora/CHANGELOG.md @@ -4,6 +4,7 @@ - build.json names the architecture explicitly again (`collabora/code:latest-amd64` and `collabora/code:latest-arm64`). The builder never passes `--platform`, so the tag is the only thing that decides which binaries land in the addon - Restore the file capabilities on `coolforkit-caps` and `coolmount`. The official image carries them as extended attributes, which `COPY --from` does not transfer, and without them Collabora starts but cannot open any document - The certificates for `ssl: true` are read from the copies in /etc/coolwsd rather than from /ssl directly, which Collabora could not read as uid 1001 when the private key is root-only +- Make the `ssl` option authoritative even when `extra_params` is empty or customized. `ssl: false` now always disables Collabora's internal HTTPS instead of silently falling back to its default self-signed TLS - Fix version numbering: releases on CollaboraOnline/online are now Helm charts only, which had renumbered the addon from 25.4.9.2 down to 1.3.0 and hid updates. The version is tracked from the collabora/code Docker Hub tags again - `server_name` is now passed to Collabora, fixing `Your browser has been unable to connect to the Collabora server` behind a reverse proxy - `domain1` was never passed to Collabora at all (the script read a `domain` option that does not exist, and recent Collabora releases dropped that variable). It is now deprecated and applied as `server_name`