- Add WELIST_PING_SIG_VERSION (1 legacy, 2 canonical JSON); default 1
- Sign WELIST-PING-V2\n + compact sorted JSON covering url, chain, height,
median_time, last_block_hash, info, base_fee, version and sig_version
- Fetch info/base_fee/version from the local /{chain}/info endpoint and sign them
- Fall back to null metadata on fetch failure; skip the fetch for onion URLs
- Normalize the advertised URL and bound metadata to match the WeTime service
- Activate v2 in bal-pusher.sh and document the TXT record setup
- Add unit tests for canonical payloads, URL normalization, metadata and onion detection
Includes previously uncommitted local deployment hardening:
- pg-server.sh configurable loopback bind address, volume and recreate handling
- .env loading in bal-server.sh and bal-pusher.sh
- second bal-server/bal-pusher development scripts
126 lines
5.0 KiB
Bash
Executable File
126 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
. "$SCRIPT_DIR/lib.sh"
|
|
|
|
PG_CONTAINER="bal-pg"
|
|
PG_IMAGE="${BAL_PG_IMAGE:-postgres:16-alpine}"
|
|
PG_VOLUME="${BAL_PG_VOLUME:-bal-pg-data}"
|
|
PG_USER="${BAL_PG_USER:-bal}"
|
|
PG_DB="${BAL_PG_DB:-baldb}"
|
|
# The password MUST come from the environment; never hardcode it.
|
|
PG_PASSWORD="${BAL_PG_PASSWORD:?BAL_PG_PASSWORD must be set (see .env.example)}"
|
|
|
|
# Publish the port on the loopback interface only by default. Set
|
|
# BAL_PG_BIND_ADDR=0.0.0.0 to expose PostgreSQL on every interface (not
|
|
# recommended: the database would be reachable from the network).
|
|
PG_BIND_ADDR="${BAL_PG_BIND_ADDR:-127.0.0.1}"
|
|
PG_PORT=5432
|
|
|
|
# Address the local clients use to reach the container. When the port is
|
|
# published on all interfaces, loopback is still the safest connect address.
|
|
case "$PG_BIND_ADDR" in
|
|
0.0.0.0|::|"") PG_CONNECT_ADDR="127.0.0.1" ;;
|
|
*) PG_CONNECT_ADDR="$PG_BIND_ADDR" ;;
|
|
esac
|
|
PG_DSN="${BAL_SERVER_PG_DSN:-postgres://${PG_USER}:${PG_PASSWORD}@${PG_CONNECT_ADDR}:${PG_PORT}/${PG_DB}}"
|
|
|
|
# Print the host IP the container port is currently published on.
|
|
# An empty value means Docker publishes it on all interfaces.
|
|
pg_container_bind_addr() {
|
|
docker inspect --format '{{range $port, $bindings := .HostConfig.PortBindings}}{{range $bindings}}{{.HostIp}}{{end}}{{end}}' "$PG_CONTAINER" 2>/dev/null
|
|
}
|
|
|
|
warn_if_public() {
|
|
case "$PG_BIND_ADDR" in
|
|
127.0.0.1|::1|localhost) ;;
|
|
*)
|
|
echo_w "WARNING: PostgreSQL is published on ${PG_BIND_ADDR}:${PG_PORT} and is reachable from the network."
|
|
echo_w " Set BAL_PG_BIND_ADDR=127.0.0.1 unless remote access is really required."
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Fail closed when an existing container publishes the port elsewhere.
|
|
# Docker cannot change port bindings of a running/stopped container.
|
|
require_expected_bind_addr() {
|
|
local current
|
|
current=$(pg_container_bind_addr)
|
|
[ "$current" = "$PG_BIND_ADDR" ] && return 0
|
|
|
|
echo_e "Container '$PG_CONTAINER' publishes port ${PG_PORT} on '${current:-0.0.0.0}', expected '$PG_BIND_ADDR'."
|
|
echo_e "Docker cannot change port bindings of an existing container."
|
|
echo_e "Recreate it while keeping its data with:"
|
|
echo_e " $0 --recreate"
|
|
exit 1
|
|
}
|
|
|
|
# Recreate the container so that the new port binding takes effect.
|
|
# The data volume is reused (or copied into BAL_PG_VOLUME when it changes).
|
|
recreate_container() {
|
|
local mount_type old_volume
|
|
mount_type=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/var/lib/postgresql/data"}}{{.Type}}{{end}}{{end}}' "$PG_CONTAINER" 2>/dev/null || true)
|
|
old_volume=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/var/lib/postgresql/data"}}{{.Name}}{{end}}{{end}}' "$PG_CONTAINER" 2>/dev/null || true)
|
|
|
|
if [ "$mount_type" = "bind" ]; then
|
|
echo_e "Container '$PG_CONTAINER' uses a bind mount for its data; refusing to recreate it automatically."
|
|
exit 1
|
|
fi
|
|
|
|
echo_i "Removing container '$PG_CONTAINER' (its data volume is preserved)..."
|
|
docker rm -f "$PG_CONTAINER" >/dev/null
|
|
|
|
if [ -n "$old_volume" ] && [ "$old_volume" != "$PG_VOLUME" ]; then
|
|
echo_i "Migrating data from volume '$old_volume' to '$PG_VOLUME'..."
|
|
docker volume create "$PG_VOLUME" >/dev/null
|
|
docker run --rm --entrypoint sh \
|
|
-v "$old_volume:/from:ro" \
|
|
-v "$PG_VOLUME:/to" \
|
|
"$PG_IMAGE" -c 'cp -a /from/. /to/'
|
|
fi
|
|
}
|
|
|
|
if [ "${1:-}" = "--recreate" ]; then
|
|
if docker ps -a --format '{{.Names}}' | grep -q "^${PG_CONTAINER}$"; then
|
|
recreate_container
|
|
fi
|
|
fi
|
|
|
|
warn_if_public
|
|
|
|
if docker ps --format '{{.Names}}' | grep -q "^${PG_CONTAINER}$"; then
|
|
echo_i "PostgreSQL container '$PG_CONTAINER' is already running"
|
|
require_expected_bind_addr
|
|
elif docker ps -a --format '{{.Names}}' | grep -q "^${PG_CONTAINER}$"; then
|
|
require_expected_bind_addr
|
|
echo_i "Starting existing PostgreSQL container '$PG_CONTAINER'..."
|
|
docker start "$PG_CONTAINER" >/dev/null 2>&1
|
|
else
|
|
echo_i "Creating PostgreSQL container '$PG_CONTAINER'..."
|
|
docker run -d --name "$PG_CONTAINER" \
|
|
-e POSTGRES_USER="$PG_USER" \
|
|
-e POSTGRES_PASSWORD="$PG_PASSWORD" \
|
|
-e POSTGRES_DB="$PG_DB" \
|
|
-p "${PG_BIND_ADDR}:${PG_PORT}:5432" \
|
|
-v "${PG_VOLUME}:/var/lib/postgresql/data" \
|
|
"$PG_IMAGE" >/dev/null
|
|
fi
|
|
|
|
echo_i "Waiting for PostgreSQL..."
|
|
until docker exec "$PG_CONTAINER" pg_isready -U "$PG_USER" >/dev/null 2>&1; do
|
|
sleep 1
|
|
done
|
|
|
|
# The container is created with a single database, so create the requested one
|
|
# if it does not exist yet (e.g. a second instance with its own database).
|
|
if ! docker exec "$PG_CONTAINER" psql -U "$PG_USER" -d postgres -tAc \
|
|
"SELECT 1 FROM pg_database WHERE datname = '$PG_DB'" | grep -q 1; then
|
|
echo_i "Creating database '$PG_DB'..."
|
|
docker exec "$PG_CONTAINER" createdb -U "$PG_USER" "$PG_DB"
|
|
fi
|
|
|
|
# Do not echo the DSN: it embeds the password.
|
|
echo_s "PostgreSQL ready (user=$PG_USER, db=$PG_DB, host=${PG_CONNECT_ADDR}:${PG_PORT})"
|
|
export BAL_SERVER_PG_DSN="$PG_DSN"
|