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 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-07-26 18:48:56 +02:00
committed by alexbelgium
parent c203703ead
commit c10801ae75
4 changed files with 76 additions and 17 deletions

View File

@@ -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`

View File

@@ -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
################

View File

@@ -1,6 +1,6 @@
{
"build_from": {
"aarch64": "collabora/code:latest",
"amd64": "collabora/code:latest"
"aarch64": "collabora/code:latest-arm64",
"amd64": "collabora/code:latest-amd64"
}
}

View File

@@ -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