From f5d4852ad8ee53745cd1989709bae28f50d680bf Mon Sep 17 00:00:00 2001 From: Weboria Migration Date: Sat, 25 Jul 2026 17:43:08 +0000 Subject: [PATCH] fix: hex password generation, safe DATABASE_URL, consistent /opt/argus default Root cause of a real production failure on the first live install (wap-proxy, 2026-07-25): DB_PASSWORD was generated with `openssl rand -base64 24`, which can produce '/', '+', or '=' -- docker-compose.yml then naively interpolated the raw password into postgres://postgres:${DB_PASSWORD}@db:5432/..., and a generated password containing '/' broke the connection string outright. The API never became healthy; log ingestion failed completely. - install.sh now generates with `openssl rand -hex 32` (always [0-9a-f], can't produce this class of character). Same fix applied everywhere else openssl-rand-base64-24 was referenced. - docker-compose.yml no longer builds DATABASE_URL by string interpolation -- DB_PASSWORD is passed as its own var and the API assembles the connection string safely internally using net/url.UserPassword (proper percent-encoding), a second, independent layer so the installer doesn't rely on the password generator alone. See the matching argus-appliance commit for the Go-side change and its regression test. - backup.sh/healthcheck.sh/restore.sh/uninstall.sh still defaulted ARGUS_INSTALL_DIR to $HOME/argus, inconsistent with install.sh/ update.sh's own /opt/argus default (changed in an earlier commit this session) -- confirmed live on the same install: healthcheck.sh and uninstall.sh reported "no installation found" when run from the real, correct directory. All five scripts now agree on /opt/argus. Corresponding argus-appliance fix (config.go's buildDatabaseURL, v3.73.1) already built and pushed to git-cloud.weboria.eu/weboria/argus-api. --- README.md | 2 +- backup.sh | 4 ++-- docker-compose.yml | 9 ++++++++- healthcheck.sh | 4 ++-- install.sh | 10 +++++++++- restore.sh | 4 ++-- uninstall.sh | 4 ++-- 7 files changed, 26 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bf3b387..fbf62f3 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ curl -fsSL https://git-cloud.weboria.eu/weboria/argus-installer/raw/branch/main/ curl -fsSL https://git-cloud.weboria.eu/weboria/argus-installer/raw/branch/main/docker-socket-proxy/haproxy.cfg.template -o docker-socket-proxy/haproxy.cfg.template # Generate a secure database password -sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$(openssl rand -base64 24)/" .env +sed -i "s/DB_PASSWORD=.*/DB_PASSWORD=$(openssl rand -hex 32)/" .env # ARGUS expects this network to already exist docker network create argus-network diff --git a/backup.sh b/backup.sh index 5ccc6a1..ca4ae29 100755 --- a/backup.sh +++ b/backup.sh @@ -3,11 +3,11 @@ # ARGUS backup — dumps the database and application data (certificates, # uploaded config) into a single, timestamped archive. # -# cd ~/argus && ./backup.sh [output-directory] +# cd /opt/argus && ./backup.sh [output-directory] # set -euo pipefail -INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}" +INSTALL_DIR="${ARGUS_INSTALL_DIR:-/opt/argus}" OUT_DIR="${1:-$PWD}" STAMP="$(date +%Y%m%d-%H%M%S)" ARCHIVE="${OUT_DIR}/argus-backup-${STAMP}.tar.gz" diff --git a/docker-compose.yml b/docker-compose.yml index 8376c5d..24a383f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -108,7 +108,14 @@ services: environment: TZ: ${TZ:-UTC} PORT: "8080" - DATABASE_URL: postgres://postgres:${DB_PASSWORD:-postgres}@db:5432/argus?sslmode=disable + # DATABASE_URL is not set here — the API builds it internally from + # DB_PASSWORD using net/url (proper percent-encoding), instead of this + # file naively interpolating the password into a raw connection + # string. A raw '${DB_PASSWORD}@...' interpolation breaks outright if + # the password ever contains '@', ':', '/', or similar (confirmed + # live: an openssl-rand-base64-generated password containing '/' + # produced an unparseable URL and the API never became healthy). + DB_PASSWORD: ${DB_PASSWORD:-postgres} REDIS_URL: redis://valkey:6379/0 ENVIRONMENT: ${ENVIRONMENT:-production} NGINX_CONTAINER: argus-proxy diff --git a/healthcheck.sh b/healthcheck.sh index 086efa0..c4c415a 100755 --- a/healthcheck.sh +++ b/healthcheck.sh @@ -2,11 +2,11 @@ # # ARGUS health check — reports the status of every component. # -# cd ~/argus && ./healthcheck.sh +# cd /opt/argus && ./healthcheck.sh # set -euo pipefail -INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}" +INSTALL_DIR="${ARGUS_INSTALL_DIR:-/opt/argus}" bold() { printf '\033[1m%s\033[0m\n' "$1"; } ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; } diff --git a/install.sh b/install.sh index 7dfdf63..63b9a67 100755 --- a/install.sh +++ b/install.sh @@ -67,8 +67,16 @@ else fi # ── 4. Generate secrets (first install only — never overwrite an existing .env) ── +# hex, not base64: base64 can produce '/', '+', '=', any of which breaks the +# raw postgres://user:PASSWORD@host connection string if it ever ends up +# interpolated unescaped (confirmed live on the first real production +# install — see config.go's buildDatabaseURL for the second, independent +# defensive layer this pairs with). Hex output is always [0-9a-f], which +# can never produce that class of bug, at 4 bits/char vs base64's ~6 — +# 32 hex chars (128 bits) is used in place of 24 base64 chars (~144 bits) +# to keep entropy comparable rather than matching digit-count cosmetically. if grep -q '^DB_PASSWORD=change-me-in-production' .env 2>/dev/null; then - DB_PASSWORD="$(openssl rand -base64 24 | tr -d '\n')" + DB_PASSWORD="$(openssl rand -hex 32)" sed -i.bak "s#^DB_PASSWORD=.*#DB_PASSWORD=${DB_PASSWORD}#" .env && rm -f .env.bak ok "Generated a secure database password" fi diff --git a/restore.sh b/restore.sh index 98e7bcc..2853edf 100755 --- a/restore.sh +++ b/restore.sh @@ -5,11 +5,11 @@ # that already has data on it, uninstall.sh --remove-data && install.sh # first, then run this. # -# cd ~/argus && ./restore.sh argus-backup-20260719-120000.tar.gz +# cd /opt/argus && ./restore.sh argus-backup-20260719-120000.tar.gz # set -euo pipefail -INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}" +INSTALL_DIR="${ARGUS_INSTALL_DIR:-/opt/argus}" ARCHIVE="${1:-}" bold() { printf '\033[1m%s\033[0m\n' "$1"; } diff --git a/uninstall.sh b/uninstall.sh index 689faba..bfcb099 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -6,13 +6,13 @@ # it only stops and removes containers, so a plain re-install (install.sh) # picks up right where you left off. # -# cd ~/argus && ./uninstall.sh # containers only, data kept +# cd /opt/argus && ./uninstall.sh # containers only, data kept # ./uninstall.sh --remove-data # also destroys all data (asks to confirm) # ./uninstall.sh --remove-data --yes # non-interactive, for scripted use # set -euo pipefail -INSTALL_DIR="${ARGUS_INSTALL_DIR:-$HOME/argus}" +INSTALL_DIR="${ARGUS_INSTALL_DIR:-/opt/argus}" REMOVE_DATA=false ASSUME_YES=false