feat/lxc-ferretdb #9
@@ -142,7 +142,7 @@ PostgreSQL role, and the data are left untouched.
|
|||||||
- **OS**: latest Debian LXC template (auto-detected), unprivileged, `nesting=1`, `/dev/net/tun` passthrough for Tailscale
|
- **OS**: latest Debian LXC template (auto-detected), unprivileged, `nesting=1`, `/dev/net/tun` passthrough for Tailscale
|
||||||
- **Storage engine**: PostgreSQL `17` (PGDG) + the DocumentDB extension (`pg_documentdb`, `pg_cron`), loopback-only on `127.0.0.1:5432`
|
- **Storage engine**: PostgreSQL `17` (PGDG) + the DocumentDB extension (`pg_documentdb`, `pg_cron`), loopback-only on `127.0.0.1:5432`
|
||||||
- **Proxy**: official `ferretdb` deb from `github.com/FerretDB/FerretDB`, systemd unit, listening on `0.0.0.0:27017`
|
- **Proxy**: official `ferretdb` deb from `github.com/FerretDB/FerretDB`, systemd unit, listening on `0.0.0.0:27017`
|
||||||
- **Auth**: MongoDB client credentials map to PostgreSQL roles; the app role is a superuser so the single homelab user can manage collections and Mongo users (`documentdb.enableUserCrud`)
|
- **Auth**: the app user is provisioned through `documentdb_api.create_user` (roles `clusterAdmin` + `readWriteAnyDatabase`), **not** a plain `CREATE ROLE`. DocumentDB builds the SCRAM-SHA-256 verifier with its own 28-byte salt (`documentdb.scramDefaultSaltLen`); a native PostgreSQL role would store a 16-byte salt that MongoDB clients reject (`invalid salt length of 16 in sasl step2`). FerretDB connects to PostgreSQL as the same user.
|
||||||
- **Network**: FerretDB exposed on `0.0.0.0:27017`; Tailscale runs in the LXC for tailnet reachability (no `serve`)
|
- **Network**: FerretDB exposed on `0.0.0.0:27017`; Tailscale runs in the LXC for tailnet reachability (no `serve`)
|
||||||
- **Config**: `/etc/postgresql/17/main/conf.d/documentdb.conf` (extension settings) and `/etc/systemd/system/ferretdb.service.d/override.conf` (`FERRETDB_POSTGRESQL_URL`, `FERRETDB_LISTEN_ADDR`)
|
- **Config**: `/etc/postgresql/17/main/conf.d/documentdb.conf` (extension settings) and `/etc/systemd/system/ferretdb.service.d/override.conf` (`FERRETDB_POSTGRESQL_URL`, `FERRETDB_LISTEN_ADDR`)
|
||||||
- **Logs**: PostgreSQL via its stock `logrotate`; FerretDB via journald, capped at `SystemMaxUse=200M`
|
- **Logs**: PostgreSQL via its stock `logrotate`; FerretDB via journald, capped at `SystemMaxUse=200M`
|
||||||
|
|||||||
@@ -467,6 +467,10 @@ documentdb.enableBypassDocumentValidation = true
|
|||||||
documentdb.enableUserCrud = true
|
documentdb.enableUserCrud = true
|
||||||
documentdb.maxUserLimit = 100
|
documentdb.maxUserLimit = 100
|
||||||
|
|
||||||
|
# Ensure any password we set is hashed with SCRAM-SHA-256 (PG default, set
|
||||||
|
# explicitly so it is active before roles are provisioned).
|
||||||
|
password_encryption = 'scram-sha-256'
|
||||||
|
|
||||||
# Postgres stays loopback-only; FerretDB (same LXC) is the network front door.
|
# Postgres stays loopback-only; FerretDB (same LXC) is the network front door.
|
||||||
listen_addresses = '127.0.0.1'
|
listen_addresses = '127.0.0.1'
|
||||||
EOF
|
EOF
|
||||||
@@ -474,22 +478,36 @@ EOF
|
|||||||
log_info "Restarting PostgreSQL..."
|
log_info "Restarting PostgreSQL..."
|
||||||
systemctl restart postgresql
|
systemctl restart postgresql
|
||||||
|
|
||||||
log_info "Bootstrapping role '${FERRETDB_USER}' and documentdb extension..."
|
log_info "Creating the documentdb extension..."
|
||||||
# The single homelab app role is a superuser so it can manage collections and
|
su -s /bin/sh postgres -c "psql -v ON_ERROR_STOP=1 -d postgres" >/dev/null <<'SQL'
|
||||||
# MongoDB users (documentdb.enableUserCrud). FerretDB connects as this role
|
|
||||||
# and LibreChat authenticates as the same user/password.
|
|
||||||
local pw_escaped="${FERRETDB_PASSWORD//\'/\'\'}"
|
|
||||||
su -s /bin/sh postgres -c "psql -v ON_ERROR_STOP=1 -d postgres" >/dev/null <<SQL
|
|
||||||
DO \$\$
|
|
||||||
BEGIN
|
|
||||||
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '${FERRETDB_USER}') THEN
|
|
||||||
CREATE ROLE "${FERRETDB_USER}" WITH LOGIN SUPERUSER PASSWORD '${pw_escaped}';
|
|
||||||
ELSE
|
|
||||||
ALTER ROLE "${FERRETDB_USER}" WITH LOGIN SUPERUSER PASSWORD '${pw_escaped}';
|
|
||||||
END IF;
|
|
||||||
END
|
|
||||||
\$\$;
|
|
||||||
CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;
|
CREATE EXTENSION IF NOT EXISTS documentdb CASCADE;
|
||||||
|
SQL
|
||||||
|
|
||||||
|
# Provision the user THROUGH DocumentDB — never a plain CREATE ROLE ... PASSWORD.
|
||||||
|
# DocumentDB builds the SCRAM-SHA-256 verifier with its own salt length
|
||||||
|
# (documentdb.scramDefaultSaltLen = 28 bytes) via documentdb_api.create_user /
|
||||||
|
# update_user. A native PostgreSQL role instead stores a 16-byte salt, which
|
||||||
|
# MongoDB clients reject at SASL step2 with "invalid salt length of 16".
|
||||||
|
# create_user only accepts a read-only role, or the clusterAdmin +
|
||||||
|
# readWriteAnyDatabase pair we use here for full read/write (FerretDB/DocumentDB
|
||||||
|
# commands/users.c::ValidateAndObtainUserRole). The spec is built with jq so the
|
||||||
|
# password is JSON-escaped, then embedded in a $DDB$-dollar-quoted SQL literal.
|
||||||
|
log_info "Provisioning MongoDB user '${FERRETDB_USER}' via DocumentDB (28-byte SCRAM salt)..."
|
||||||
|
local role_exists cmd spec
|
||||||
|
role_exists=$(su -s /bin/sh postgres -c \
|
||||||
|
"psql -tAX -d postgres -c \"SELECT 1 FROM pg_roles WHERE rolname = '${FERRETDB_USER}'\"" 2>/dev/null || true)
|
||||||
|
if [[ "$role_exists" == "1" ]]; then
|
||||||
|
log_info "Role '${FERRETDB_USER}' already exists — resetting its password via DocumentDB."
|
||||||
|
cmd="update_user"
|
||||||
|
spec=$(jq -nc --arg u "$FERRETDB_USER" --arg p "$FERRETDB_PASSWORD" \
|
||||||
|
'{updateUser:$u, pwd:$p}')
|
||||||
|
else
|
||||||
|
cmd="create_user"
|
||||||
|
spec=$(jq -nc --arg u "$FERRETDB_USER" --arg p "$FERRETDB_PASSWORD" \
|
||||||
|
'{createUser:$u, pwd:$p, roles:[{role:"clusterAdmin",db:"admin"},{role:"readWriteAnyDatabase",db:"admin"}]}')
|
||||||
|
fi
|
||||||
|
su -s /bin/sh postgres -c "psql -v ON_ERROR_STOP=1 -d postgres" >/dev/null <<SQL
|
||||||
|
SELECT documentdb_api.${cmd}(\$DDB\$${spec}\$DDB\$);
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
log_info "Writing FerretDB systemd override..."
|
log_info "Writing FerretDB systemd override..."
|
||||||
|
|||||||
Reference in New Issue
Block a user