an officerdev docker network, and the reasoning next to the port binding
One network for everything Officer provisions, created before anything joins it and declared external in the compose file. Postgres needs nothing from it today — the platform is a host process reaching it over loopback — but a reverse proxy in front of the web UI does, and so does any app-store service that talks to another. Creating it now means the later ones do not have to be migrated onto it. Two things written next to the line they explain, rather than assumed: Why loopback. Publishing a port makes Docker write its own DNAT and ACCEPT rules into iptables, and those are evaluated BEFORE ufw sees the packet — so `ports: "5432:5432"` is reachable from the internet while `ufw status` reports everything denied. That is the same mechanism the machine-setup firewall section hooks DOCKER-USER to close. Binding to 127.0.0.1 sidesteps it: the DNAT rule only matches traffic arriving on loopback. Why the password is not decoration. Loopback means nothing off this machine, but every account ON it can open 127.0.0.1:5432 — including the per-user Linux accounts Officer gives its members. What stops them is that they cannot authenticate. The password is the boundary between the platform and anyone with a login here, which is why it stays random and why both files holding it are 0600. A unix socket would remove even that, and was ruled out for a specific reason: postgres.js only treats a host as a socket path when the host FIELD contains a slash (src/index.js:468), and officer_db/src/db.ts passes a bare URL string. It would take a change to db.ts, which is not a setup-script change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -342,6 +342,15 @@ if ! skip; then
|
||||
echo " accounts and the job queue. Nothing else in the platform is a"
|
||||
echo " database."
|
||||
echo ""
|
||||
# One network for everything Officer provisions. Created before the compose
|
||||
# file references it, since it is declared external there.
|
||||
if ensure_docker_network; then
|
||||
ok "docker network '${OFFICER_NETWORK}' created"
|
||||
SUMMARY+=("Docker network: ${OFFICER_NETWORK} created")
|
||||
elif docker_network_exists; then
|
||||
echo " network: ${OFFICER_NETWORK} (already there)"
|
||||
fi
|
||||
|
||||
echo " compose file: $(pg_compose_exists && echo "$(pg_compose_file)" || echo 'not written yet')"
|
||||
echo " container: $(pg_container_running && echo "${PG_CONTAINER} running" || echo 'not running')"
|
||||
echo " port ${PG_PORT}: $(pg_port_in_use && echo 'something is listening' || echo 'free')"
|
||||
|
||||
@@ -27,11 +27,24 @@
|
||||
[[ -n "${OFFICER_SETUP_POSTGRES_LOADED:-}" ]] && return 0
|
||||
OFFICER_SETUP_POSTGRES_LOADED=1
|
||||
|
||||
# One network for everything Officer provisions, so containers can reach each
|
||||
# other by name. Postgres needs nothing from it today — the platform is a host
|
||||
# process and reaches it over loopback — but a reverse proxy in front of the web
|
||||
# UI, or any app-store service that talks to another, does. Creating it now means
|
||||
# the later ones do not have to migrate onto it.
|
||||
OFFICER_NETWORK="${OFFICER_NETWORK:-officerdev}"
|
||||
|
||||
PG_IMAGE="${PG_IMAGE:-postgres:18-alpine}"
|
||||
PG_DATABASE="${PG_DATABASE:-officer}"
|
||||
PG_CONTAINER="${PG_CONTAINER:-officer-postgres}"
|
||||
PG_PORT="${PG_PORT:-5432}"
|
||||
|
||||
docker_network_exists() { docker network inspect "$OFFICER_NETWORK" &>/dev/null; }
|
||||
ensure_docker_network() {
|
||||
docker_network_exists && return 1
|
||||
docker network create "$OFFICER_NETWORK" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
pg_service_dir() { echo "${OFFICER_ROOT}/dockers/postgres"; }
|
||||
pg_compose_file() { echo "$(pg_service_dir)/docker-compose.yaml"; }
|
||||
pg_env_file() { echo "$(pg_service_dir)/.env"; }
|
||||
@@ -43,12 +56,21 @@ pg_container_running() { docker ps --filter "name=^${PG_CONTAINER}$" --format '{
|
||||
# way is a perfectly good answer, and finding out by failing to bind is not.
|
||||
pg_port_in_use() { ss -ltn 2>/dev/null | grep -qE "127\.0\.0\.1:${PG_PORT}\b|\*:${PG_PORT}\b|0\.0\.0\.0:${PG_PORT}\b"; }
|
||||
|
||||
# Bound to loopback, deliberately.
|
||||
# Bound to loopback, deliberately, and the reason is worth keeping next to the
|
||||
# line it explains.
|
||||
#
|
||||
# `ports: "5432:5432"` publishes to every interface, and Docker writes its own
|
||||
# iptables rules underneath ufw — so a database published that way is reachable
|
||||
# from the internet whatever the firewall says. 127.0.0.1 is the whole fix, and
|
||||
# it is enough: the platform runs on the same machine.
|
||||
# Publishing a port makes Docker write its own DNAT and ACCEPT rules into
|
||||
# iptables, and those are evaluated BEFORE ufw sees the packet. So `ports:
|
||||
# "5432:5432"` is reachable from the internet while `ufw status` reports
|
||||
# everything denied. Binding to 127.0.0.1 sidesteps it entirely: the DNAT rule
|
||||
# only matches traffic arriving on loopback.
|
||||
#
|
||||
# Loopback is not the whole story, though, and the password is not decoration.
|
||||
# Every account ON this machine can open 127.0.0.1:5432 — including the per-user
|
||||
# Linux accounts Officer gives its members. What stops them is that they cannot
|
||||
# authenticate. The password is the boundary between the platform and anyone
|
||||
# with a login here, which is why it is random and why both files holding it are
|
||||
# 0600.
|
||||
write_pg_compose() {
|
||||
local password="$1" dir
|
||||
dir="$(pg_service_dir)"
|
||||
@@ -75,11 +97,17 @@ services:
|
||||
volumes:
|
||||
- ./data:/var/lib/postgresql/data
|
||||
- ./dumps:/dumps
|
||||
networks:
|
||||
- ${OFFICER_NETWORK}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
networks:
|
||||
${OFFICER_NETWORK}:
|
||||
external: true
|
||||
COMPOSE
|
||||
|
||||
# The password lives beside the compose file rather than inside it, so the
|
||||
|
||||
Reference in New Issue
Block a user