feat(secrets): real VAPID keypair for mastodon, slot-numbered DB passwords
VAPID: the two values are the halves of one P-256 keypair, not independent secrets — the browser verifies that a push is signed by the private key matching the public key it subscribed with. The RANDOMIZED* generators mint each placeholder on its own, so they produced two unrelated strings and web push could never have worked. Generate the pair in mastodon_install_post_setup the way stoat already does, encoded as Mastodon's webpush gem expects: unpadded URL-safe base64 of the 32-byte private scalar and the 65-byte uncompressed public point, sliced out of the SEC1 DER. Verified by rebuilding the key from the emitted private half and re-deriving the public point — openssl accepts it and the point matches. Generated once and never rotated (rotation would invalidate every subscription), but a pair of the wrong shape is replaced, so an install carrying the old unrelated strings heals itself on next install — their public half is 42 chars where a real point is 87. Slots: CFG_<APP>_DB_PASSWORD -> CFG_<APP>_DB_PASSWORD_1 and likewise for DB_ROOT_PASSWORD, across mastodon, owncloud, mattermost, matrix, nextcloud and bookstack, so a database credential is always a numbered slot and a second one is just _2. Renaming a key means reconciliation drops the old and adds the new holding its placeholder, so an existing install regenerates unless the value is carried over first — documented, including that the old file survives as .<app>.config.bak. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
be7d6813ef
commit
4685320353
@ -31,8 +31,8 @@ CFG_BOOKSTACK_ADMIN_PASSWORD=RANDOMIZEDPASSWORD3
|
||||
# auto-generated, and (unlike a RANDOMIZED* placeholder in the compose)
|
||||
# preserved across reinstalls. DB_PASSWORD is shared by the app + db services.
|
||||
CFG_BOOKSTACK_APP_KEY=RANDOMIZEDAPPKEY1
|
||||
CFG_BOOKSTACK_DB_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_BOOKSTACK_DB_ROOT_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
CFG_BOOKSTACK_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
CFG_BOOKSTACK_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
|
||||
@ -15,7 +15,7 @@ services:
|
||||
- DB_HOST=bookstack_db
|
||||
- DB_PORT=3306
|
||||
- DB_USERNAME=bookstack
|
||||
- DB_PASSWORD=BOOKSTACK_DB_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_TAG|BOOKSTACK_DB_PASSWORD_DATA
|
||||
- DB_PASSWORD=BOOKSTACK_DB_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_1_TAG|BOOKSTACK_DB_PASSWORD_1_DATA
|
||||
- DB_DATABASE=bookstackapp
|
||||
volumes:
|
||||
- SOCKET_DATA #LIBREPORTAL|SOCKET_TAG|SOCKET_DATA
|
||||
@ -60,10 +60,10 @@ services:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- MYSQL_ROOT_PASSWORD=BOOKSTACK_DB_ROOT_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_ROOT_PASSWORD_TAG|BOOKSTACK_DB_ROOT_PASSWORD_DATA
|
||||
- MYSQL_ROOT_PASSWORD=BOOKSTACK_DB_ROOT_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_ROOT_PASSWORD_1_TAG|BOOKSTACK_DB_ROOT_PASSWORD_1_DATA
|
||||
- MYSQL_DATABASE=bookstackapp
|
||||
- MYSQL_USER=bookstack
|
||||
- MYSQL_PASSWORD=BOOKSTACK_DB_PASSWORD_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_TAG|BOOKSTACK_DB_PASSWORD_DATA
|
||||
- MYSQL_PASSWORD=BOOKSTACK_DB_PASSWORD_1_DATA #LIBREPORTAL|BOOKSTACK_DB_PASSWORD_1_TAG|BOOKSTACK_DB_PASSWORD_1_DATA
|
||||
volumes:
|
||||
- ./db:/config
|
||||
restart: unless-stopped
|
||||
|
||||
@ -3,7 +3,7 @@ networks:
|
||||
external: true
|
||||
|
||||
services:
|
||||
mastodon-service: #LIBREPORTAL|SERVICE_TAG_1|mastodon-service
|
||||
mastodon-service: #LIBREPORTAL|SERVICE_TAG_3|mastodon-service
|
||||
container_name: mastodon-service
|
||||
image: tootsuite/mastodon:v4.6 #LIBREPORTAL|MASTODON_VERSION_TAG|v4.6
|
||||
ports:
|
||||
@ -16,7 +16,7 @@ services:
|
||||
- LOCAL_DOMAIN=DOMAINSUBNAME_DATA #LIBREPORTAL|DOMAINSUBNAME_TAG|DOMAINSUBNAME_DATA
|
||||
- DB_HOST=mastodon-postgres
|
||||
- DB_USER=MASTODON_DB_USER_DATA #LIBREPORTAL|MASTODON_DB_USER_TAG|MASTODON_DB_USER_DATA
|
||||
- DB_PASS=MASTODON_DB_PASSWORD_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_TAG|MASTODON_DB_PASSWORD_DATA
|
||||
- DB_PASS=MASTODON_DB_PASSWORD_1_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_1_TAG|MASTODON_DB_PASSWORD_1_DATA
|
||||
- DB_NAME=MASTODON_DB_NAME_DATA #LIBREPORTAL|MASTODON_DB_NAME_TAG|MASTODON_DB_NAME_DATA
|
||||
- REDIS_HOST=mastodon-redis
|
||||
- SECRET_KEY_BASE=MASTODON_SECRET_KEY_BASE_DATA #LIBREPORTAL|MASTODON_SECRET_KEY_BASE_TAG|MASTODON_SECRET_KEY_BASE_DATA
|
||||
@ -55,20 +55,20 @@ services:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
ipv4_address: IP_DATA_3 #LIBREPORTAL|IP_TAG_3|IP_DATA_3
|
||||
|
||||
mastodon-postgres:
|
||||
mastodon-postgres: #LIBREPORTAL|SERVICE_TAG_1|mastodon-postgres
|
||||
image: postgres:15
|
||||
container_name: mastodon-postgres
|
||||
environment:
|
||||
- POSTGRES_DB=MASTODON_DB_NAME_DATA #LIBREPORTAL|MASTODON_DB_NAME_TAG|MASTODON_DB_NAME_DATA
|
||||
- POSTGRES_USER=MASTODON_DB_USER_DATA #LIBREPORTAL|MASTODON_DB_USER_TAG|MASTODON_DB_USER_DATA
|
||||
- POSTGRES_PASSWORD=MASTODON_DB_PASSWORD_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_TAG|MASTODON_DB_PASSWORD_DATA
|
||||
- POSTGRES_PASSWORD=MASTODON_DB_PASSWORD_1_DATA #LIBREPORTAL|MASTODON_DB_PASSWORD_1_TAG|MASTODON_DB_PASSWORD_1_DATA
|
||||
volumes:
|
||||
- ./postgres:/var/lib/postgresql/data
|
||||
networks:
|
||||
DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA
|
||||
ipv4_address: IP_DATA_1 #LIBREPORTAL|IP_TAG_1|IP_DATA_1
|
||||
|
||||
mastodon-redis:
|
||||
mastodon-redis: #LIBREPORTAL|SERVICE_TAG_2|mastodon-redis
|
||||
image: redis:7
|
||||
container_name: mastodon-redis
|
||||
command: redis-server --save 60 1 --loglevel warning
|
||||
|
||||
@ -34,11 +34,17 @@ CFG_MASTODON_HEADSCALE=false
|
||||
# every user out.
|
||||
# OTP_SECRET = protects stored two-factor enrolments. Rotating it invalidates
|
||||
# them, and anyone with 2FA on needs it reset before they can log in.
|
||||
# VAPID_PRIVATE_KEY / VAPID_PUBLIC_KEY = Web Push identity.
|
||||
# VAPID_PRIVATE_KEY / VAPID_PUBLIC_KEY = Web Push identity. These two are the
|
||||
# halves of one P-256 keypair, so they cannot be generated independently the
|
||||
# way the other secrets are — mastodon_install_post_setup replaces the pair
|
||||
# below with a real one on first install, and never touches it again.
|
||||
# RANDOMIZEDVAPID<n> is kept here only so the tag always has something
|
||||
# substitutable: if key generation ever fails, Mastodon starts with push
|
||||
# broken rather than with a literal placeholder as its key.
|
||||
#
|
||||
CFG_MASTODON_DB_NAME=RANDOMIZEDUSERNAME1
|
||||
CFG_MASTODON_DB_USER=RANDOMIZEDUSERNAME2
|
||||
CFG_MASTODON_DB_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_MASTODON_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
CFG_MASTODON_SECRET_KEY_BASE=RANDOMIZEDHEX1
|
||||
CFG_MASTODON_OTP_SECRET=RANDOMIZEDHEX2
|
||||
CFG_MASTODON_VAPID_PRIVATE_KEY=RANDOMIZEDVAPID1
|
||||
|
||||
71
containers/mastodon/scripts/mastodon_install_hooks.sh
Normal file
71
containers/mastodon/scripts/mastodon_install_hooks.sh
Normal file
@ -0,0 +1,71 @@
|
||||
#!/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" =~ ^[A-Za-z0-9_-]{43}$ \
|
||||
&& "$CFG_MASTODON_VAPID_PUBLIC_KEY" =~ ^[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" "$vapid_private" "$config_file"
|
||||
updateConfigOption "CFG_MASTODON_VAPID_PUBLIC_KEY" "$vapid_public" "$config_file"
|
||||
isSuccessful "Generated a VAPID keypair for $app_name web push."
|
||||
}
|
||||
@ -96,14 +96,14 @@ services:
|
||||
# No `user:` override — the postgres entrypoint starts as root, chowns
|
||||
# PGDATA and drops privileges, which works under rootless because
|
||||
# container-root is the install user that owns the mount.
|
||||
matrix-postgres:
|
||||
matrix-postgres: #LIBREPORTAL|SERVICE_TAG_3|matrix-postgres
|
||||
image: postgres:15-alpine
|
||||
container_name: matrix-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- POSTGRES_USER=synapse
|
||||
- POSTGRES_PASSWORD=MATRIX_DB_PASSWORD_DATA #LIBREPORTAL|MATRIX_DB_PASSWORD_TAG|MATRIX_DB_PASSWORD_DATA
|
||||
- POSTGRES_PASSWORD=MATRIX_DB_PASSWORD_1_DATA #LIBREPORTAL|MATRIX_DB_PASSWORD_1_TAG|MATRIX_DB_PASSWORD_1_DATA
|
||||
- POSTGRES_DB=synapse
|
||||
# Not optional. Synapse refuses to start against a database with any
|
||||
# other collation or ctype — it needs deterministic byte ordering for
|
||||
|
||||
@ -39,11 +39,11 @@ CFG_MATRIX_ADMIN_USERNAME=admin
|
||||
CFG_MATRIX_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_MATRIX_MONITORING=false
|
||||
# Postgres password for the `synapse` role, fed to the compose via
|
||||
# #LIBREPORTAL|MATRIX_DB_PASSWORD_TAG| and written into homeserver.yaml by the
|
||||
# #LIBREPORTAL|MATRIX_DB_PASSWORD_1_TAG| and written into homeserver.yaml by the
|
||||
# install hook. Generated on first install and preserved across reinstalls —
|
||||
# initdb sets it once when the volume is created, so a regenerated value would
|
||||
# leave Synapse unable to open its own database.
|
||||
CFG_MATRIX_DB_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
CFG_MATRIX_DB_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
|
||||
@ -47,12 +47,12 @@ matrix_install_post_compose()
|
||||
fi
|
||||
|
||||
# Must match the password the compose handed to Postgres. Read it back from
|
||||
# the deployed compose rather than from CFG_MATRIX_DB_PASSWORD: this hook
|
||||
# the deployed compose rather than from CFG_MATRIX_DB_PASSWORD_1: this hook
|
||||
# runs after templating, so the compose is the settled value, and it stays
|
||||
# correct even on an install whose config still holds the placeholder.
|
||||
local db_password
|
||||
db_password=$(tagsManagerGetTagContent "$app_dir/docker-compose.yml" "MATRIX_DB_PASSWORD_TAG")
|
||||
if [[ -z "$db_password" || "$db_password" == "MATRIX_DB_PASSWORD_DATA" ]]; then
|
||||
db_password=$(tagsManagerGetTagContent "$app_dir/docker-compose.yml" "MATRIX_DB_PASSWORD_1_TAG")
|
||||
if [[ -z "$db_password" || "$db_password" == "MATRIX_DB_PASSWORD_1_DATA" ]]; then
|
||||
isError "Database password was not generated in the compose file — aborting Synapse configuration."
|
||||
return 1
|
||||
fi
|
||||
|
||||
@ -34,7 +34,7 @@ services:
|
||||
# Fixed role and database name, random password. The database is
|
||||
# only reachable on the internal docker network, and a generated
|
||||
# username buys nothing while making manual psql recovery painful.
|
||||
- MM_SQLSETTINGS_DATASOURCE=postgres://mattermost:MATTERMOST_DB_PASSWORD_DATA@mattermost-postgres:5432/mattermost?sslmode=disable&connect_timeout=10 #LIBREPORTAL|MATTERMOST_DB_PASSWORD_TAG|MATTERMOST_DB_PASSWORD_DATA
|
||||
- MM_SQLSETTINGS_DATASOURCE=postgres://mattermost:MATTERMOST_DB_PASSWORD_1_DATA@mattermost-postgres:5432/mattermost?sslmode=disable&connect_timeout=10 #LIBREPORTAL|MATTERMOST_DB_PASSWORD_1_TAG|MATTERMOST_DB_PASSWORD_1_DATA
|
||||
# Mattermost builds every absolute link (invites, password resets,
|
||||
# CORS and websocket origin checks) from this. Wrong value and the
|
||||
# web client connects but the websocket is rejected, which shows up
|
||||
@ -76,14 +76,14 @@ services:
|
||||
# root, chowns PGDATA to the postgres user and then drops privileges. Under
|
||||
# rootless Docker container-root *is* the install user on the host, so it
|
||||
# owns ./postgres and the chown succeeds. Pinning a uid would break that.
|
||||
mattermost-postgres:
|
||||
mattermost-postgres: #LIBREPORTAL|SERVICE_TAG_2|mattermost-postgres
|
||||
image: postgres:15-alpine
|
||||
container_name: mattermost-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- TZ=TIMEZONE_DATA #LIBREPORTAL|TIMEZONE_TAG|TIMEZONE_DATA
|
||||
- POSTGRES_USER=mattermost
|
||||
- POSTGRES_PASSWORD=MATTERMOST_DB_PASSWORD_DATA #LIBREPORTAL|MATTERMOST_DB_PASSWORD_TAG|MATTERMOST_DB_PASSWORD_DATA
|
||||
- POSTGRES_PASSWORD=MATTERMOST_DB_PASSWORD_1_DATA #LIBREPORTAL|MATTERMOST_DB_PASSWORD_1_TAG|MATTERMOST_DB_PASSWORD_1_DATA
|
||||
- POSTGRES_DB=mattermost
|
||||
volumes:
|
||||
- ./postgres:/var/lib/postgresql/data
|
||||
|
||||
@ -27,10 +27,10 @@ CFG_MATTERMOST_AUTHELIA=false
|
||||
CFG_MATTERMOST_HEADSCALE=false
|
||||
CFG_MATTERMOST_MONITORING=false
|
||||
# Postgres password for the `mattermost` role, fed to the compose via
|
||||
# #LIBREPORTAL|MATTERMOST_DB_PASSWORD_TAG| (both the server's datasource URL and
|
||||
# #LIBREPORTAL|MATTERMOST_DB_PASSWORD_1_TAG| (both the server's datasource URL and
|
||||
# the database's own env). Generated on first install and preserved across
|
||||
# reinstalls — initdb sets it once when the volume is created.
|
||||
CFG_MATTERMOST_DB_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_MATTERMOST_DB_PASSWORD_1=RANDOMIZEDPASSWORD1
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
|
||||
@ -16,7 +16,7 @@ services:
|
||||
- MYSQL_HOST=nextcloud-db
|
||||
- MYSQL_DATABASE=NEXTCLOUD_DB_NAME_DATA #LIBREPORTAL|NEXTCLOUD_DB_NAME_TAG|NEXTCLOUD_DB_NAME_DATA
|
||||
- MYSQL_USER=NEXTCLOUD_DB_USER_DATA #LIBREPORTAL|NEXTCLOUD_DB_USER_TAG|NEXTCLOUD_DB_USER_DATA
|
||||
- MYSQL_PASSWORD=NEXTCLOUD_DB_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_DB_PASSWORD_TAG|NEXTCLOUD_DB_PASSWORD_DATA
|
||||
- MYSQL_PASSWORD=NEXTCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_DB_PASSWORD_1_TAG|NEXTCLOUD_DB_PASSWORD_1_DATA
|
||||
- NEXTCLOUD_ADMIN_USER=NEXTCLOUD_ADMIN_USER_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_USER_TAG|NEXTCLOUD_ADMIN_USER_DATA
|
||||
- NEXTCLOUD_ADMIN_PASSWORD=NEXTCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_ADMIN_PASSWORD_TAG|NEXTCLOUD_ADMIN_PASSWORD_DATA
|
||||
- NEXTCLOUD_TRUSTED_DOMAINS=NEXTCLOUD_TRUSTED_DOMAINS_DATA #LIBREPORTAL|NEXTCLOUD_TRUSTED_DOMAINS_TAG|NEXTCLOUD_TRUSTED_DOMAINS_DATA
|
||||
@ -49,10 +49,10 @@ services:
|
||||
restart: unless-stopped
|
||||
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=NEXTCLOUD_DB_ROOT_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_DB_ROOT_PASSWORD_TAG|NEXTCLOUD_DB_ROOT_PASSWORD_DATA
|
||||
- MARIADB_ROOT_PASSWORD=NEXTCLOUD_DB_ROOT_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_DB_ROOT_PASSWORD_1_TAG|NEXTCLOUD_DB_ROOT_PASSWORD_1_DATA
|
||||
- MARIADB_DATABASE=NEXTCLOUD_DB_NAME_DATA #LIBREPORTAL|NEXTCLOUD_DB_NAME_TAG|NEXTCLOUD_DB_NAME_DATA
|
||||
- MARIADB_USER=NEXTCLOUD_DB_USER_DATA #LIBREPORTAL|NEXTCLOUD_DB_USER_TAG|NEXTCLOUD_DB_USER_DATA
|
||||
- MARIADB_PASSWORD=NEXTCLOUD_DB_PASSWORD_DATA #LIBREPORTAL|NEXTCLOUD_DB_PASSWORD_TAG|NEXTCLOUD_DB_PASSWORD_DATA
|
||||
- MARIADB_PASSWORD=NEXTCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|NEXTCLOUD_DB_PASSWORD_1_TAG|NEXTCLOUD_DB_PASSWORD_1_DATA
|
||||
volumes:
|
||||
- ./db_data:/var/lib/mysql
|
||||
networks:
|
||||
|
||||
@ -27,8 +27,8 @@ CFG_NEXTCLOUD_ADMIN_USER=admin
|
||||
CFG_NEXTCLOUD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_NEXTCLOUD_DB_NAME=nextcloud
|
||||
CFG_NEXTCLOUD_DB_USER=nextcloud
|
||||
CFG_NEXTCLOUD_DB_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
CFG_NEXTCLOUD_DB_ROOT_PASSWORD=RANDOMIZEDPASSWORD3
|
||||
CFG_NEXTCLOUD_DB_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_NEXTCLOUD_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD3
|
||||
CFG_NEXTCLOUD_AUTH_PROFILE=multi_user
|
||||
#
|
||||
# =============================================================================
|
||||
|
||||
@ -22,7 +22,7 @@ services:
|
||||
- OWNCLOUD_DB_TYPE=mysql
|
||||
- OWNCLOUD_DB_NAME=OWNCLOUD_DB_NAME_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_TAG|OWNCLOUD_DB_NAME_DATA
|
||||
- OWNCLOUD_DB_USERNAME=OWNCLOUD_DB_USER_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_TAG|OWNCLOUD_DB_USER_DATA
|
||||
- OWNCLOUD_DB_PASSWORD=OWNCLOUD_DB_PASSWORD_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_TAG|OWNCLOUD_DB_PASSWORD_DATA
|
||||
- OWNCLOUD_DB_PASSWORD=OWNCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_1_TAG|OWNCLOUD_DB_PASSWORD_1_DATA
|
||||
- OWNCLOUD_DB_HOST=owncloud-mariadb
|
||||
- OWNCLOUD_ADMIN_USERNAME=OWNCLOUD_ADMIN_USERNAME_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_USERNAME_TAG|OWNCLOUD_ADMIN_USERNAME_DATA
|
||||
- OWNCLOUD_ADMIN_PASSWORD=OWNCLOUD_ADMIN_PASSWORD_DATA #LIBREPORTAL|OWNCLOUD_ADMIN_PASSWORD_TAG|OWNCLOUD_ADMIN_PASSWORD_DATA
|
||||
@ -73,13 +73,13 @@ services:
|
||||
restart: unless-stopped
|
||||
hostname: mariadb
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=OWNCLOUD_DB_ROOT_PASSWORD_DATA #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_TAG|OWNCLOUD_DB_ROOT_PASSWORD_DATA
|
||||
- MYSQL_ROOT_PASSWORD=OWNCLOUD_DB_ROOT_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_1_TAG|OWNCLOUD_DB_ROOT_PASSWORD_1_DATA
|
||||
- MYSQL_USER=OWNCLOUD_DB_USER_DATA #LIBREPORTAL|OWNCLOUD_DB_USER_TAG|OWNCLOUD_DB_USER_DATA
|
||||
- MYSQL_PASSWORD=OWNCLOUD_DB_PASSWORD_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_TAG|OWNCLOUD_DB_PASSWORD_DATA
|
||||
- MYSQL_PASSWORD=OWNCLOUD_DB_PASSWORD_1_DATA #LIBREPORTAL|OWNCLOUD_DB_PASSWORD_1_TAG|OWNCLOUD_DB_PASSWORD_1_DATA
|
||||
- MYSQL_DATABASE=OWNCLOUD_DB_NAME_DATA #LIBREPORTAL|OWNCLOUD_DB_NAME_TAG|OWNCLOUD_DB_NAME_DATA
|
||||
command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=OWNCLOUD_DB_ROOT_PASSWORD_DATA"] #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_TAG|OWNCLOUD_DB_ROOT_PASSWORD_DATA
|
||||
test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=OWNCLOUD_DB_ROOT_PASSWORD_1_DATA"] #LIBREPORTAL|OWNCLOUD_DB_ROOT_PASSWORD_1_TAG|OWNCLOUD_DB_ROOT_PASSWORD_1_DATA
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
@ -37,8 +37,8 @@ CFG_OWNCLOUD_ADMIN_USERNAME=RANDOMIZEDUSERNAME1
|
||||
CFG_OWNCLOUD_ADMIN_PASSWORD=RANDOMIZEDPASSWORD1
|
||||
CFG_OWNCLOUD_DB_NAME=RANDOMIZEDUSERNAME2
|
||||
CFG_OWNCLOUD_DB_USER=RANDOMIZEDUSERNAME3
|
||||
CFG_OWNCLOUD_DB_PASSWORD=RANDOMIZEDPASSWORD2
|
||||
CFG_OWNCLOUD_DB_ROOT_PASSWORD=RANDOMIZEDPASSWORD3
|
||||
CFG_OWNCLOUD_DB_PASSWORD_1=RANDOMIZEDPASSWORD2
|
||||
CFG_OWNCLOUD_DB_ROOT_PASSWORD_1=RANDOMIZEDPASSWORD3
|
||||
#
|
||||
# =============================================================================
|
||||
# METADATA
|
||||
|
||||
@ -78,7 +78,7 @@ services:
|
||||
# No `user:` override: the entrypoint starts as root, chowns /data/db and
|
||||
# then drops to the mongodb user. Under rootless Docker container-root is
|
||||
# the install user on the host, which owns the bind mount, so that works.
|
||||
rocketchat-db:
|
||||
rocketchat-db: #LIBREPORTAL|SERVICE_TAG_2|rocketchat-db
|
||||
image: mongo:8.0 #LIBREPORTAL|ROCKETCHAT_MONGO_VERSION_TAG|8.0
|
||||
container_name: rocketchat-db
|
||||
restart: unless-stopped
|
||||
|
||||
@ -10,6 +10,14 @@
|
||||
# service names in Revolt.toml — keep resolving, and LibrePortal still gets the
|
||||
# prefixed container names its port, firewall and backup layers key on.
|
||||
#
|
||||
# SERVICE_TAG_n carries the compose *key*, not the container name, for two
|
||||
# reasons: ipUpdateComposeTags allocates exactly one IP per SERVICE_TAG and fills
|
||||
# IP_TAG_i only where SERVICE_TAG_i exists (so every service with an
|
||||
# ipv4_address needs one, index-aligned, or it deploys with a literal
|
||||
# IP_DATA_n and compose refuses to start), and `libreportal app restart <app>
|
||||
# <service>` passes the value straight to `docker compose restart`, which only
|
||||
# understands keys.
|
||||
#
|
||||
# Upstream fronts the whole stack with Caddy doing path routing (/api, /ws,
|
||||
# /autumn, ...). That is kept as-is and Traefik simply proxies to it, which is
|
||||
# upstream's own supported "behind another reverse proxy" mode — reimplementing
|
||||
@ -27,7 +35,7 @@ services:
|
||||
# Upstream's healthcheck is kept rather than the LibrePortal HEALTHCHECK_TAG:
|
||||
# half the stack uses `depends_on: condition: service_healthy` against it, so
|
||||
# disabling the healthcheck would deadlock the boot order. Same for rabbit.
|
||||
database:
|
||||
database: #LIBREPORTAL|SERVICE_TAG_1|database
|
||||
container_name: stoat-database
|
||||
image: mongo:8.0 #LIBREPORTAL|STOAT_MONGO_VERSION_TAG|8.0
|
||||
restart: unless-stopped
|
||||
@ -46,7 +54,7 @@ services:
|
||||
ipv4_address: IP_DATA_1 #LIBREPORTAL|IP_TAG_1|IP_DATA_1
|
||||
|
||||
# Valkey — event message broker and KV store.
|
||||
redis:
|
||||
redis: #LIBREPORTAL|SERVICE_TAG_2|redis
|
||||
container_name: stoat-redis
|
||||
image: valkey/valkey:9-alpine #LIBREPORTAL|STOAT_VALKEY_VERSION_TAG|9-alpine
|
||||
restart: unless-stopped
|
||||
@ -57,7 +65,7 @@ services:
|
||||
ipv4_address: IP_DATA_2 #LIBREPORTAL|IP_TAG_2|IP_DATA_2
|
||||
|
||||
# RabbitMQ — internal message broker (push notifications, voice events).
|
||||
rabbit:
|
||||
rabbit: #LIBREPORTAL|SERVICE_TAG_3|rabbit
|
||||
container_name: stoat-rabbit
|
||||
image: rabbitmq:4-alpine #LIBREPORTAL|STOAT_RABBITMQ_VERSION_TAG|4-alpine
|
||||
restart: unless-stopped
|
||||
@ -82,7 +90,7 @@ services:
|
||||
# The bucket-name aliases are load-bearing: the file server addresses
|
||||
# buckets virtual-host style (<bucket>.minio), so without these the DNS
|
||||
# lookup fails and every upload errors.
|
||||
minio:
|
||||
minio: #LIBREPORTAL|SERVICE_TAG_4|minio
|
||||
container_name: stoat-minio
|
||||
image: minio/minio:latest #LIBREPORTAL|STOAT_MINIO_VERSION_TAG|latest
|
||||
restart: unless-stopped
|
||||
@ -111,7 +119,7 @@ services:
|
||||
|
||||
# One-shot: creates the uploads bucket, then exits. Not a failure when you
|
||||
# see it stopped.
|
||||
createbuckets:
|
||||
createbuckets: #LIBREPORTAL|SERVICE_TAG_5|createbuckets
|
||||
container_name: stoat-createbuckets
|
||||
image: minio/mc:latest #LIBREPORTAL|STOAT_MINIO_MC_VERSION_TAG|latest
|
||||
depends_on:
|
||||
@ -139,7 +147,7 @@ services:
|
||||
|
||||
# Caddy — internal path router for the whole stack. This is the only service
|
||||
# Traefik talks to, and the only one carrying a Traefik router.
|
||||
caddy: #LIBREPORTAL|SERVICE_TAG_1|caddy
|
||||
caddy: #LIBREPORTAL|SERVICE_TAG_6|caddy
|
||||
container_name: stoat-caddy
|
||||
image: caddy:2-alpine #LIBREPORTAL|STOAT_CADDY_VERSION_TAG|2-alpine
|
||||
restart: unless-stopped
|
||||
@ -184,7 +192,7 @@ services:
|
||||
# GLUETUN_ON_END
|
||||
|
||||
# API server.
|
||||
api:
|
||||
api: #LIBREPORTAL|SERVICE_TAG_7|api
|
||||
container_name: stoat-api
|
||||
image: ghcr.io/stoatchat/api:v0.15.1 #LIBREPORTAL|STOAT_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -205,7 +213,7 @@ services:
|
||||
- api
|
||||
|
||||
# Websocket / events service.
|
||||
events:
|
||||
events: #LIBREPORTAL|SERVICE_TAG_8|events
|
||||
container_name: stoat-events
|
||||
image: ghcr.io/stoatchat/events:v0.15.1 #LIBREPORTAL|STOAT_EVENTS_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -224,7 +232,7 @@ services:
|
||||
- events
|
||||
|
||||
# Autumn — file server.
|
||||
autumn:
|
||||
autumn: #LIBREPORTAL|SERVICE_TAG_9|autumn
|
||||
container_name: stoat-autumn
|
||||
image: ghcr.io/stoatchat/file-server:v0.15.1 #LIBREPORTAL|STOAT_AUTUMN_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -243,7 +251,7 @@ services:
|
||||
- autumn
|
||||
|
||||
# January — link metadata and image proxy.
|
||||
january:
|
||||
january: #LIBREPORTAL|SERVICE_TAG_10|january
|
||||
container_name: stoat-january
|
||||
image: ghcr.io/stoatchat/proxy:v0.15.1 #LIBREPORTAL|STOAT_JANUARY_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -258,7 +266,7 @@ services:
|
||||
|
||||
# Gifbox — Tenor proxy for the GIF picker. Inert until a Tenor API key is
|
||||
# added to secrets.env; see the upstream Guides.md.
|
||||
gifbox:
|
||||
gifbox: #LIBREPORTAL|SERVICE_TAG_11|gifbox
|
||||
container_name: stoat-gifbox
|
||||
image: ghcr.io/stoatchat/gifbox:v0.15.1 #LIBREPORTAL|STOAT_GIFBOX_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -272,7 +280,7 @@ services:
|
||||
- gifbox
|
||||
|
||||
# Scheduled task daemon.
|
||||
crond:
|
||||
crond: #LIBREPORTAL|SERVICE_TAG_12|crond
|
||||
container_name: stoat-crond
|
||||
image: ghcr.io/stoatchat/crond:v0.15.1 #LIBREPORTAL|STOAT_CROND_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -289,7 +297,7 @@ services:
|
||||
ipv4_address: IP_DATA_12 #LIBREPORTAL|IP_TAG_12|IP_DATA_12
|
||||
|
||||
# Push notification daemon.
|
||||
pushd:
|
||||
pushd: #LIBREPORTAL|SERVICE_TAG_13|pushd
|
||||
container_name: stoat-pushd
|
||||
image: ghcr.io/stoatchat/pushd:v0.15.1 #LIBREPORTAL|STOAT_PUSHD_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -308,7 +316,7 @@ services:
|
||||
ipv4_address: IP_DATA_13 #LIBREPORTAL|IP_TAG_13|IP_DATA_13
|
||||
|
||||
# Voice ingress daemon — receives LiveKit's webhooks.
|
||||
voice-ingress:
|
||||
voice-ingress: #LIBREPORTAL|SERVICE_TAG_14|voice-ingress
|
||||
container_name: stoat-voice-ingress
|
||||
image: ghcr.io/stoatchat/voice-ingress:v0.15.1 #LIBREPORTAL|STOAT_VOICE_INGRESS_VERSION_TAG|v0.15.1
|
||||
restart: unless-stopped
|
||||
@ -337,7 +345,7 @@ services:
|
||||
# to work from outside the LAN:
|
||||
# sudo ufw allow 50000:50100/udp
|
||||
# Voice still falls back to TCP 7881 without it, at the cost of latency.
|
||||
livekit:
|
||||
livekit: #LIBREPORTAL|SERVICE_TAG_15|livekit
|
||||
container_name: stoat-livekit
|
||||
image: ghcr.io/stoatchat/livekit-server:v1.9.13 #LIBREPORTAL|STOAT_LIVEKIT_VERSION_TAG|v1.9.13
|
||||
restart: unless-stopped
|
||||
@ -357,7 +365,7 @@ services:
|
||||
- livekit
|
||||
|
||||
# The web client itself. Served by Caddy at /.
|
||||
web:
|
||||
web: #LIBREPORTAL|SERVICE_TAG_16|web
|
||||
container_name: stoat-web
|
||||
image: ghcr.io/stoatchat/for-web:0c31cf0 #LIBREPORTAL|STOAT_WEB_VERSION_TAG|0c31cf0
|
||||
restart: unless-stopped
|
||||
|
||||
@ -156,8 +156,42 @@ initialised with. That is the same desync the old mechanism caused on every
|
||||
reinstall; the difference is that it now happens at most once, because the values
|
||||
are preserved from then on.
|
||||
|
||||
If you have one of these installed and want to avoid it, capture the credentials
|
||||
**before** reinstalling. With the app stopped:
|
||||
### Database password keys are now numbered slots
|
||||
|
||||
`CFG_<APP>_DB_PASSWORD` became `CFG_<APP>_DB_PASSWORD_1`, and
|
||||
`CFG_<APP>_DB_ROOT_PASSWORD` became `CFG_<APP>_DB_ROOT_PASSWORD_1`, so a database
|
||||
credential is always a numbered slot and an app that needs a second one just adds
|
||||
`_2`. This also touches **nextcloud** and **bookstack**, which were otherwise
|
||||
unaffected by the change above.
|
||||
|
||||
A rename is a delete plus an add as far as config reconciliation is concerned: the
|
||||
old key is dropped and the new one arrives holding its `RANDOMIZED*` placeholder,
|
||||
so the next install generates a fresh password rather than keeping the current
|
||||
one. To carry the existing password over, copy it into the new key before
|
||||
reinstalling — reconciliation leaves the previous file as a hidden
|
||||
`.<app>.config.bak` sibling, so the old value is still there:
|
||||
|
||||
```bash
|
||||
grep DB_PASSWORD <containers-dir>/<app>/.<app>.config.bak
|
||||
```
|
||||
|
||||
Paste each value into the matching `_1` key in `<containers-dir>/<app>/<app>.config`.
|
||||
|
||||
### Mastodon web push
|
||||
|
||||
`VAPID_PRIVATE_KEY` and `VAPID_PUBLIC_KEY` were generated as two independent
|
||||
random strings, which is not a keypair — web push could never have worked. They
|
||||
are now generated together as a P-256 keypair by the install hook, in the format
|
||||
Mastodon's webpush gem expects. Any install still carrying the old values
|
||||
regenerates the pair once on its next install (the old public half is the wrong
|
||||
length to be a valid point, which is what triggers it) and is correct from then
|
||||
on. Existing push subscriptions were not working anyway, so nothing is lost;
|
||||
clients re-subscribe on next login.
|
||||
|
||||
### Carrying credentials across, generally
|
||||
|
||||
If you have one of these installed and want to avoid regeneration, capture the
|
||||
credentials **before** reinstalling. With the app stopped:
|
||||
|
||||
```bash
|
||||
grep -E 'POSTGRES_|MYSQL_|SECRET_KEY_BASE|OTP_SECRET|VAPID_|RABBITMQ_DEFAULT_PASS' <containers-dir>/<app>/docker-compose.yml
|
||||
|
||||
@ -645,6 +645,7 @@ declare -gA LP_FN_MAP=(
|
||||
[manifestReadFromSnapshot]="backup/manifest/manifest_read.sh"
|
||||
[manifestRemove]="backup/manifest/manifest_write.sh"
|
||||
[manifestWrite]="backup/manifest/manifest_write.sh"
|
||||
[mastodon_install_post_setup]="mastodon/scripts/mastodon_install_hooks.sh"
|
||||
[mastodon_upgrade_verify]="mastodon/scripts/mastodon_upgrade_hooks.sh"
|
||||
[matrix_install_post]="matrix/scripts/matrix_install_hooks.sh"
|
||||
[matrix_install_post_compose]="matrix/scripts/matrix_install_hooks.sh"
|
||||
@ -1700,6 +1701,7 @@ declare -gA LP_FN_ROOT=(
|
||||
[manifestReadFromSnapshot]="scripts"
|
||||
[manifestRemove]="scripts"
|
||||
[manifestWrite]="scripts"
|
||||
[mastodon_install_post_setup]="containers"
|
||||
[mastodon_upgrade_verify]="containers"
|
||||
[matrix_install_post]="containers"
|
||||
[matrix_install_post_compose]="containers"
|
||||
@ -2789,6 +2791,7 @@ manifestReadField() { unset -f manifestReadField; __lpAutoload "${install_script
|
||||
manifestReadFromSnapshot() { unset -f manifestReadFromSnapshot; __lpAutoload "${install_scripts_dir}backup/manifest/manifest_read.sh"; manifestReadFromSnapshot "$@"; }
|
||||
manifestRemove() { unset -f manifestRemove; __lpAutoload "${install_scripts_dir}backup/manifest/manifest_write.sh"; manifestRemove "$@"; }
|
||||
manifestWrite() { unset -f manifestWrite; __lpAutoload "${install_scripts_dir}backup/manifest/manifest_write.sh"; manifestWrite "$@"; }
|
||||
mastodon_install_post_setup() { unset -f mastodon_install_post_setup; __lpAutoload "${install_containers_dir}mastodon/scripts/mastodon_install_hooks.sh"; mastodon_install_post_setup "$@"; }
|
||||
mastodon_upgrade_verify() { unset -f mastodon_upgrade_verify; __lpAutoload "${install_containers_dir}mastodon/scripts/mastodon_upgrade_hooks.sh"; mastodon_upgrade_verify "$@"; }
|
||||
matrix_install_post() { unset -f matrix_install_post; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post "$@"; }
|
||||
matrix_install_post_compose() { unset -f matrix_install_post_compose; __lpAutoload "${install_containers_dir}matrix/scripts/matrix_install_hooks.sh"; matrix_install_post_compose "$@"; }
|
||||
|
||||
@ -343,14 +343,14 @@ PORTEOF
|
||||
"advanced": true,
|
||||
"default": "nextcloud"
|
||||
},
|
||||
"NEXTCLOUD_DB_PASSWORD": {
|
||||
"NEXTCLOUD_DB_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Password",
|
||||
"type": "password",
|
||||
"tooltip": "MariaDB password for the Nextcloud user (auto-generated; persists across reinstalls)",
|
||||
"advanced": true
|
||||
},
|
||||
"NEXTCLOUD_DB_ROOT_PASSWORD": {
|
||||
"NEXTCLOUD_DB_ROOT_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Root Password",
|
||||
"type": "password",
|
||||
@ -371,7 +371,7 @@ PORTEOF
|
||||
"tooltip": "Postgres role Mastodon connects with (internal to the docker network). Set by initdb when the volume is created.",
|
||||
"advanced": true
|
||||
},
|
||||
"MASTODON_DB_PASSWORD": {
|
||||
"MASTODON_DB_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Password",
|
||||
"type": "password",
|
||||
@ -420,28 +420,28 @@ PORTEOF
|
||||
"tooltip": "MariaDB account ownCloud connects with (internal to the docker network)",
|
||||
"advanced": true
|
||||
},
|
||||
"OWNCLOUD_DB_PASSWORD": {
|
||||
"OWNCLOUD_DB_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Password",
|
||||
"type": "password",
|
||||
"tooltip": "MariaDB password for the ownCloud user (auto-generated; persists across reinstalls)",
|
||||
"advanced": true
|
||||
},
|
||||
"OWNCLOUD_DB_ROOT_PASSWORD": {
|
||||
"OWNCLOUD_DB_ROOT_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Root Password",
|
||||
"type": "password",
|
||||
"tooltip": "MariaDB root password (auto-generated; kept separate from the app user password so root can be left alone if you rotate the app account)",
|
||||
"advanced": true
|
||||
},
|
||||
"MATTERMOST_DB_PASSWORD": {
|
||||
"MATTERMOST_DB_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Password",
|
||||
"type": "password",
|
||||
"tooltip": "Postgres password for the mattermost role (auto-generated; persists across reinstalls)",
|
||||
"advanced": true
|
||||
},
|
||||
"MATRIX_DB_PASSWORD": {
|
||||
"MATRIX_DB_PASSWORD_1": {
|
||||
"category": "advanced",
|
||||
"label": "Database Password",
|
||||
"type": "password",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user