Stage TLS cert for coturn so turns: works.

coturn drops to nobody, but Let's Encrypt keeps privkey.pem root:root
0600, so it could not read the key and started without TLS listeners --
turns: on 5349 was silently unavailable while STUN/TURN on 3478 worked.

Copy the cert at container start into a directory owned by the runtime
user, the same approach the Prosody entrypoint already uses here, rather
than relaxing permissions on the shared Let's Encrypt tree and exposing
the key to every other container on the host.

The container now starts as root for the copy and coturn drops privileges
itself via proc-user/proc-group. Since the copy happens at start, a
renewal requires a restart, matching the existing Prosody deploy hook.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mguschin
2026-08-18 22:21:57 +03:00
parent c11f05481c
commit bf8f50ea72
3 changed files with 52 additions and 5 deletions

37
coturn/entrypoint.sh Executable file
View File

@@ -0,0 +1,37 @@
#!/bin/sh
# coturn entrypoint: stage the TLS certificate for the unprivileged user.
#
# Let's Encrypt keeps privkey.pem as root:root 0600, but coturn drops to
# nobody, so it cannot read the key from the mounted store and silently
# starts without TLS listeners (no turns: on 5349). Rather than loosening
# permissions on the shared Let's Encrypt tree -- which would expose the key
# to every other container and user on the host -- copy it to a private
# directory owned by the runtime user.
#
# This mirrors what the Prosody entrypoint does. Because the copy happens at
# start, a certificate renewal needs a container restart to be picked up;
# that is handled by the certbot deploy hook.
set -e
SRC="/etc/letsencrypt/live/guschin.info"
DST="/var/lib/coturn/certs"
RUN_AS_UID="${COTURN_UID:-65534}"
RUN_AS_GID="${COTURN_GID:-65533}"
if [ -r "$SRC/fullchain.pem" ] && [ -r "$SRC/privkey.pem" ]; then
mkdir -p "$DST"
# -L to dereference the live/ symlinks into archive/.
cp -L "$SRC/fullchain.pem" "$DST/fullchain.pem"
cp -L "$SRC/privkey.pem" "$DST/privkey.pem"
chown "$RUN_AS_UID:$RUN_AS_GID" "$DST/fullchain.pem" "$DST/privkey.pem"
chmod 644 "$DST/fullchain.pem"
chmod 600 "$DST/privkey.pem"
chown "$RUN_AS_UID:$RUN_AS_GID" "$DST"
echo "coturn: staged TLS certificate for uid $RUN_AS_UID"
else
# Not fatal: coturn still serves STUN and plain TURN on 3478. Make the
# degraded state loud rather than letting it hide in the noise.
echo "coturn: WARNING - cannot read $SRC; turns:// on 5349 will be DISABLED" >&2
fi
exec "$@"