MongoDB User Isolation — Playbook

Created: 2026-07-05 (feat/#2728) Scope: MongoDB rs0 (bms-2 PRIMARY / bms-3 SECONDARY / bms-4 arbiter)

Current user map

UserRolesAuth DBCredential DBSOPS fileConsumer
adminrootadminadminbms-servers.env.sops + mongodb-bms.env.sopsmaintenance, backups, p24-status.py
et_operreadWriteadminw4_dbet-operational-platform.env.sopset-operational-platform Vercel
w3_appreadWritew3_dbw3_dbpinbox24-w3.env.sopsv32-prod (bms-1)
w4_appreadWritew4_dbw4_dbpinbox24-w4.env.sopsv42-prod (bms-1)
pinbox_productionreadWritew3_dbw3_db (legacy)n/a — not referenced by any tracked SOPS key (verified 2026-07-11, #3754)unknown / dormant — do not reuse without confirming live usage first
w3_read_v42readw3_dbw3_dbpinbox24-w4.env.sops (V42_v3MongoUrl)v42-prod legacy mongojs compat path (reads W3 data only — .aggregate(), never a write; see #3754)
prometheusclusterMonitoradminadminmongodb-bms.env.sops + n8n-bms4.env.sops (dup)mongodb-exporter on bms-4

Note: prometheus credentials are temporarily in both mongodb-bms.env.sops and n8n-bms4.env.sops. Cleanup tracked in issue #2732.

Adding a new MongoDB user

Step 1 — Identify PRIMARY

ssh -i ~/.ssh/id_ed25519 root@145.239.133.104 "mongosh admin -u admin -p '\$ADMIN_PW' \
  --authenticationDatabase admin --quiet --eval \"rs.isMaster().primary\""

Step 2 — Create user on PRIMARY

Write a shell script (avoids SSH quoting issues):

#!/bin/bash
mongosh admin \
  -u admin -p "$MONGO_ADMIN_PW" \
  --authenticationDatabase admin \
  --quiet \
  --eval "db.createUser({user:'NEW_USER',pwd:'NEW_PW',roles:[{role:'readWrite',db:'TARGET_DB'}]});"

SCP the script to PRIMARY, execute, delete:

scp -i ~/.ssh/id_ed25519 script.sh root@145.239.133.104:/tmp/create_user.sh
ssh -i ~/.ssh/id_ed25519 root@145.239.133.104 "bash /tmp/create_user.sh && rm /tmp/create_user.sh"

Step 3 — Test connection

ssh -i ~/.ssh/id_ed25519 root@145.239.133.104 \
  "mongosh 'mongodb://NEW_USER:NEW_PW@145.239.133.104:27017/TARGET_DB?authSource=admin&replicaSet=rs0' \
  --eval 'db.collectionName.countDocuments({})'"

Step 4 — Add to SOPS

Add NEW_USER_MONGODB_URI=mongodb://NEW_USER:PW@... to the correct SOPS file. Use explicit dotenv flags:

$env:SOPS_AGE_KEY_FILE = "C:\Users\konar\.age\p24-infra-keys.txt"
# 1. Decrypt to variable (never print)
$plain = sops --decrypt --input-type dotenv --output-type dotenv secrets\<file>.env.sops
# 2. Append new key (value from $env:NEW_VALUE — never hardcoded)
$content = ($plain -join "`n") + "`nNEW_KEY=$env:NEW_VALUE`n"
# 3. WriteAllText (no BOM, LF)
[System.IO.File]::WriteAllText("$PWD\secrets\<file>.env.sops", $content, [System.Text.UTF8Encoding]::new($false))
# 4. Encrypt in-place
sops --encrypt --in-place --input-type dotenv --output-type dotenv secrets\<file>.env.sops
# 5. Canary
sops --decrypt --input-type dotenv --output-type dotenv secrets\<file>.env.sops | Out-Null
$env:NEW_VALUE = ""

Step 5 — Update infrastructure-overview.md

Add row to §MongoDB user map with: user, roles, db, SOPS file, consumer.

Rollback procedure

If a new user causes authentication failures:

  1. Identify affected service in Vercel/PM2 logs
  2. Revert SOPS: restore previous value of the connection URI
  3. Update Vercel env var via API (see Vercel API pattern in secrets-sync.yml)
  4. Trigger service redeploy
  5. The MongoDB user can remain (harmless) while root cause is investigated

Notes on authSource

  • Users created in admin db: authSource=admin in connection string
  • Current convention: create all users in admin db for consistency

Connection string format

mongodb://<user>:<password>@145.239.133.104:27017,51.68.155.224:27017/<database>?authSource=admin&replicaSet=rs0

Generate URL-safe passwords (no special chars that need encoding):

$pw = -join ((65..90)+(97..122)+(48..57) | Get-Random -Count 32 | ForEach-Object { [char]$_ })