Harden PostgreSQL security and add nginx basic auth for screenshots

- PostgreSQL: roles, passwords, pg_hba.conf, listen_addresses configured
- nginx: basic auth on /screenshots/ with htpasswd
- warden-zfs: fix snapshot naming convention, clarify dry-run scope
- .env: add password generation pattern for all credentials

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam & Claude 2026-03-08 21:48:30 +00:00
parent 9abcc9754d
commit a2f3cbf28b
4 changed files with 85 additions and 13 deletions

View file

@ -51,9 +51,42 @@ This skill does not replace:
nginx-ssl.html # Nginx + SSL guide
stripe-agents.html # Stripe agents guide
tailscale-vpn.html # Tailscale VPN guide
screenshots/ # Diagnostic screenshots (basic auth protected)
clawdie-eng-v1.md # English content source
```
## Protected paths
| Path | Auth | htpasswd file | Credentials |
| --- | --- | --- | --- |
| `/screenshots/` | basic auth | `/usr/local/etc/nginx/screenshots.htpasswd` | in `.env` (`SCREENSHOTS_USER`, `SCREENSHOTS_PASSWORD`) |
### Adding basic auth to a new path
```sh
# 1. generate password hash
openssl passwd -apr1 'your-password'
# 2. write htpasswd file
sudo sh -c 'cat > /usr/local/etc/nginx/newpath.htpasswd << EOF
username:$apr1$hash...
EOF'
sudo chmod 640 /usr/local/etc/nginx/newpath.htpasswd
sudo chown root:www /usr/local/etc/nginx/newpath.htpasswd
# 3. add location block to vhost
# location /newpath/ {
# auth_basic "Description";
# auth_basic_user_file /usr/local/etc/nginx/newpath.htpasswd;
# try_files $uri $uri/ =404;
# }
# 4. test and reload
sudo nginx -t && sudo service nginx reload
```
Store credentials in `/home/clawdie/clawdie-ai/.env`. Never in skill files.
## Safe defaults
- Always run `nginx -t` before reloading

View file

@ -21,7 +21,8 @@ Never expose this role to remote connections.
Set a password anyway as defense in depth:
```sh
sudo jexec db su - postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD 'secure-admin-password';\""
. /home/clawdie/clawdie-ai/.env
sudo jexec db su - postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD '$POSTGRES_ADMIN_PASSWORD';\""
```
### Role: clawdie_app (Clawdie memory)
@ -29,8 +30,9 @@ sudo jexec db su - postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD 'sec
For the Clawdie agent memory database:
```sh
. /home/clawdie/clawdie-ai/.env
sudo jexec db su - postgres -c "createuser clawdie_app"
sudo jexec db su - postgres -c "psql -c \"ALTER USER clawdie_app WITH PASSWORD 'secure-clawdie-password';\""
sudo jexec db su - postgres -c "psql -c \"ALTER USER clawdie_app WITH PASSWORD '$CLAWDIE_DB_PASSWORD';\""
sudo jexec db su - postgres -c "createdb -O clawdie_app clawdie_memory"
```
@ -39,8 +41,9 @@ sudo jexec db su - postgres -c "createdb -O clawdie_app clawdie_memory"
For the Strapi content management database:
```sh
. /home/clawdie/clawdie-ai/.env
sudo jexec db su - postgres -c "createuser strapi_user"
sudo jexec db su - postgres -c "psql -c \"ALTER USER strapi_user WITH PASSWORD 'secure-strapi-password';\""
sudo jexec db su - postgres -c "psql -c \"ALTER USER strapi_user WITH PASSWORD '$STRAPI_DB_PASSWORD';\""
sudo jexec db su - postgres -c "createdb -O strapi_user strapi_cms"
```
@ -147,10 +150,21 @@ Should fail with: `FATAL: pg_hba.conf rejects connection`
## Password storage
Store database passwords in `.env` files inside the respective jails:
All database passwords live in `/home/clawdie/clawdie-ai/.env`:
- controlplane: `/home/clawdie/clawdie-ai/.env``DATABASE_PASSWORD=...`
- cms: `/home/clawdie/strapi/.env``DATABASE_PASSWORD=...`
```
POSTGRES_ADMIN_PASSWORD=<generated>
CLAWDIE_DB_PASSWORD=<generated>
STRAPI_DB_PASSWORD=<generated>
```
Generate with: `python3 -c "import secrets; print(secrets.token_urlsafe(24))"`
Source before running setup commands: `. /home/clawdie/clawdie-ai/.env`
When jails need their own `.env`, copy only the relevant variable:
- controlplane: `CLAWDIE_DB_PASSWORD`
- cms: `STRAPI_DB_PASSWORD`
Never commit passwords to git. Never store them in skill files.
@ -171,11 +185,9 @@ Never commit passwords to git. Never store them in skill files.
## ZFS snapshots
```sh
# before security changes — dry-run first
sudo zfs snapshot -nv zroot/clawdie-runtime/jails/db@pre-security-DD-mon-YYYY
sudo zfs snapshot zroot/clawdie-runtime/jails/db@pre-security-DD-mon-YYYY
# before security changes (snapshots are non-destructive, no dry-run needed)
sudo zfs snapshot zroot/clawdie-runtime/jails/db@pre-security_YYYY-MM-DD_HH:MM:SS
# after successful setup
sudo zfs snapshot -nv zroot/clawdie-runtime/jails/db@security-configured-DD-mon-YYYY
sudo zfs snapshot zroot/clawdie-runtime/jails/db@security-configured-DD-mon-YYYY
sudo zfs snapshot zroot/clawdie-runtime/jails/db@security-configured_YYYY-MM-DD_HH:MM:SS
```

View file

@ -35,7 +35,9 @@ namespace.
## Dry-run rule (mandatory)
**Every destructive or mutating ZFS command MUST be run with `-n` (dry-run) first.** This applies to `zfs destroy`, `zfs send`, `zfs rollback`, `zpool create`, and `zpool destroy`. Only after reviewing the dry-run output and confirming it is correct should the real command be executed.
**Every destructive ZFS command MUST be run with `-n` (dry-run) first.** This applies to `zfs destroy`, `zfs rollback`, `zfs send`, `zpool destroy`, and `zpool create`. Only after reviewing the dry-run output and confirming it is correct should the real command be executed.
Note: `zfs snapshot` does NOT support `-n` because snapshots are non-destructive (instant, zero-cost). Just run `zfs snapshot` directly.
Workflow:
1. Run the command with `-nv` (dry-run + verbose) to preview what will happen
@ -58,9 +60,24 @@ Commands that are read-only (`zfs list`, `zfs get`, `zpool status`) do not need
- keep Bastille on `zroot`
- keep Bastille prefix `clawdie-runtime`
- snapshot before risky jail changes
- use day-first snapshot names with fixed month abbreviations
- do not handcraft parallel jail dataset trees outside Bastille
## Snapshot naming
Sanoid automated snapshots follow: `autosnap_YYYY-MM-DD_HH:MM:SS_<policy>`
Operator-requested snapshots follow: `<description>_YYYY-MM-DD_HH:MM:SS`
Examples:
```
autosnap_2026-03-08_18:46:47_hourly # sanoid
pre-security_2026-03-08_21:30:00 # operator request
postgres-ready_2026-03-08_19:00:00 # operator milestone
```
Do not use day-first or month-abbreviation formats (e.g., `08-mar-2026`).
Stick to ISO-style `YYYY-MM-DD` for consistent sorting and Sanoid compatibility.
## Expected operator outcomes
- Bastille datasets exist under `zroot/clawdie-runtime`

View file

@ -17,3 +17,13 @@ OPENROUTER_API_KEY=
# Channels
TELEGRAM_BOT_TOKEN=
# PostgreSQL (db jail at 192.168.100.2)
# Generate with: python3 -c "import secrets; print(secrets.token_urlsafe(24))"
POSTGRES_ADMIN_PASSWORD=
CLAWDIE_DB_PASSWORD=
STRAPI_DB_PASSWORD=
# Nginx basic auth (clawdie.si/screenshots/)
SCREENSHOTS_USER=clawdie
SCREENSHOTS_PASSWORD=