Installing rocketchat failed with
invalid IPv4 address: ParseAddr("IP_DATA_2"): unable to parse IP
ipUpdateComposeTags allocates one IP per SERVICE_TAG_N annotation and fills
IP_TAG_i only where SERVICE_TAG_i exists. The four new apps tagged only their
primary service, so every sidecar — matrix's postgres, mattermost's postgres,
rocketchat's mongo, and fifteen of stoat's sixteen — kept a literal IP_DATA_n
in the deployed compose and docker refused to create the container.
Tag every service that carries an ipv4_address, index-aligned with its IP_TAG.
For stoat that also meant moving caddy from SERVICE_TAG_1 to _6 so the indices
line up with the IPs rather than the reading order.
mastodon had the same latent break (IP_TAG_2 and _3 untagged) and is fixed the
same way — it would have failed on first install for the same reason.
SERVICE_TAG carries the compose *key*, not container_name: 'libreportal app
restart <app> <service>' passes it to 'docker compose restart', which only
understands keys.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
3.5 KiB
Bash
72 lines
3.5 KiB
Bash
#!/bin/bash
|
|
|
|
# Mastodon install hooks.
|
|
|
|
# Web Push identity.
|
|
# ---------------------------------------------------------------------------
|
|
# The two VAPID values are not independent secrets — they are the two halves of
|
|
# one P-256 keypair, and the browser verifies that the push request is signed by
|
|
# the private key matching the public key it subscribed with. The framework's
|
|
# RANDOMIZED* generators mint each placeholder on its own, so they can produce
|
|
# two well-formed strings but never a *pair*: what shipped before was two
|
|
# unrelated random values, and web push could not work with them. Stoat hits the
|
|
# same wall and solves it the same way, in its own install hook.
|
|
#
|
|
# Mastodon's webpush gem expects unpadded URL-safe base64 of the raw key
|
|
# material: the 32-byte private scalar (43 chars) and the 65-byte uncompressed
|
|
# public point (87 chars). Both are sliced out of the SEC1 DER, whose layout for
|
|
# prime256v1 is a fixed 121 bytes — a 2-byte SEQUENCE header, INTEGER 1, then
|
|
# `04 20` introducing the private scalar at offset 7, and the public point as
|
|
# the trailing field. Both slices were cross-checked against `openssl ec -text`.
|
|
#
|
|
# Runs at install_post_setup: the deployed config exists by then (so there is
|
|
# something to write to) and the compose has not been templated yet (so the pair
|
|
# reaches the compose on this same install).
|
|
#
|
|
# Generated once, then left alone. Rotating the pair invalidates every push
|
|
# subscription clients are holding, so an existing well-formed pair is never
|
|
# replaced — including across reinstalls.
|
|
mastodon_install_post_setup()
|
|
{
|
|
local app_name="${1:-mastodon}"
|
|
local config_file="${containers_dir}${app_name}/${app_name}.config"
|
|
[[ -f "$config_file" ]] || return 0
|
|
|
|
# Keep a pair that already has the right shape. The length test is also what
|
|
# retires the old values: two independent RANDOMIZEDVAPID strings are the
|
|
# wrong length for the public half, so an install carrying them regenerates
|
|
# once and is correct from then on.
|
|
if [[ "$CFG_MASTODON_VAPID_PRIVATE_KEY_1" =~ ^[A-Za-z0-9_-]{43}$ \
|
|
&& "$CFG_MASTODON_VAPID_PUBLIC_KEY_1" =~ ^[A-Za-z0-9_-]{87}$ ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local pem der
|
|
pem=$(mktemp) || return 0
|
|
der=$(mktemp) || { rm -f "$pem"; return 0; }
|
|
|
|
if ! openssl ecparam -name prime256v1 -genkey -noout -out "$pem" 2>/dev/null \
|
|
|| ! openssl ec -in "$pem" -outform DER -out "$der" 2>/dev/null; then
|
|
rm -f "$pem" "$der"
|
|
isError "Could not generate a VAPID keypair for $app_name — web push will not work."
|
|
return 0
|
|
fi
|
|
|
|
local vapid_private vapid_public
|
|
vapid_private=$(head -c 39 "$der" | tail -c 32 | base64 | tr -d '\n' | tr '+/' '-_' | tr -d '=')
|
|
vapid_public=$(tail -c 65 "$der" | base64 | tr -d '\n' | tr '+/' '-_' | tr -d '=')
|
|
rm -f "$pem" "$der"
|
|
|
|
# Refuse to write a malformed pair over a working one — a short read or an
|
|
# openssl build with a different DER layout would otherwise quietly break
|
|
# push instead of leaving it as it was.
|
|
if [[ ! "$vapid_private" =~ ^[A-Za-z0-9_-]{43}$ || ! "$vapid_public" =~ ^[A-Za-z0-9_-]{87}$ ]]; then
|
|
isError "Generated VAPID keypair had an unexpected shape — leaving $app_name's push keys alone."
|
|
return 0
|
|
fi
|
|
|
|
updateConfigOption "CFG_MASTODON_VAPID_PRIVATE_KEY_1" "$vapid_private" "$config_file"
|
|
updateConfigOption "CFG_MASTODON_VAPID_PUBLIC_KEY_1" "$vapid_public" "$config_file"
|
|
isSuccessful "Generated a VAPID keypair for $app_name web push."
|
|
}
|