Sanitized mirror from private repository - 2026-04-20 01:32:01 UTC
Some checks failed
Documentation / Build Docusaurus (push) Failing after 5m3s
Documentation / Deploy to GitHub Pages (push) Has been skipped

This commit is contained in:
Gitea Mirror Bot
2026-04-20 01:32:01 +00:00
commit e7652c8dab
1445 changed files with 364095 additions and 0 deletions

View File

@@ -0,0 +1,661 @@
# Credential Rotation Runbook
## Overview
Step-by-step rotation procedures for all credentials exposed in the
`homelab-optimized` public mirror (audited 2026-02-20). Work through each
section in priority order. After updating secrets in compose files, commit
and push — GitOps will redeploy automatically.
> **Note:** Almost all of these stem from the same root cause — secrets were
> hard-coded in compose files, then those files were committed to git, then
> `generate_service_docs.py` and wiki-upload scripts duplicated those secrets
> into documentation, creating 35× copies of every secret across the repo.
> See the "Going Forward" section for how to prevent this.
## Prerequisites
- [ ] SSH / Tailscale access to Atlantis, Calypso, Homelab VM, Seattle VM, matrix-ubuntu-vm
- [ ] Gitea admin access (`git.vish.gg`)
- [ ] Authentik admin access
- [ ] Google account access (Gmail app passwords)
- [ ] Cloudflare dashboard access
- [ ] OpenAI platform access
- [ ] Write access to this repository
## Metadata
- **Estimated Time**: 46 hours
- **Risk Level**: Medium (service restarts required for most items)
- **Requires Downtime**: Brief per-service restart only
- **Reversible**: Yes (old values can be restored if something breaks)
- **Last Updated**: 2026-02-20
---
## Priority 1 — Rotate Immediately (Externally Usable Tokens)
### 1. Gitea API Tokens
Two tokens hard-coded across scripts and docs.
#### 1a. Wiki/scripts token (`77e3ddaf...`)
**Files to update:**
- `scripts/cleanup-gitea-wiki.sh`
- `scripts/upload-all-docs-to-gitea-wiki.sh`
- `scripts/upload-to-gitea-wiki.sh`
- `scripts/create-clean-organized-wiki.sh`
- `scripts/upload-organized-wiki.sh`
- `docs/admin/DOCUMENTATION_MAINTENANCE_GUIDE.md`
```bash
# 1. Go to https://git.vish.gg/user/settings/applications
# 2. Revoke the token starting 77e3ddaf
# 3. Generate new token, name: homelab-wiki, scope: repo
# 4. Replace in all files:
NEW_TOKEN=REDACTED_TOKEN
for f in scripts/cleanup-gitea-wiki.sh \
scripts/upload-all-docs-to-gitea-wiki.sh \
scripts/upload-to-gitea-wiki.sh \
scripts/create-clean-organized-wiki.sh \
scripts/upload-organized-wiki.sh \
docs/admin/DOCUMENTATION_MAINTENANCE_GUIDE.md; do
sed -i "s/REDACTED_GITEA_TOKEN/$NEW_TOKEN/g" "$f"
done
```
#### 1b. Retro-site clone token (`52fa6ccb...`)
**File:** `Calypso/retro-site.yaml` and `hosts/synology/calypso/retro-site.yaml`
```bash
# 1. Go to https://git.vish.gg/user/settings/applications
# 2. Revoke the token starting 52fa6ccb
# 3. Generate new token, name: retro-site-deploy, scope: repo:read
# 4. Update the git clone URL in both compose files
# Consider switching to a deploy key for least-privilege access
```
---
### 2. Cloudflare API Token (`FGXlHM7doB8Z...`)
Appears in 13 files including active dynamic DNS updaters on multiple hosts.
**Files to update (active deployments):**
- `hosts/synology/atlantis/dynamicdnsupdater.yaml`
- `hosts/physical/guava/portainer_yaml/dynamic_dns.yaml`
- `hosts/physical/concord-nuc/dyndns_updater.yaml`
- Various Calypso/homelab-vm DDNS configs
**Files to sanitize (docs):**
- `docs/infrastructure/cloudflare-dns.md`
- `docs/infrastructure/npm-migration-jan2026.md`
- Any `docs/services/individual/ddns-*.md` files
```bash
# 1. Go to https://dash.cloudflare.com/profile/api-tokens
# 2. Find the token (FGXlHM7doB8Z...) and click Revoke
# 3. Create a new token: use "Edit zone DNS" template, scope to your zone only
# 4. Replace in all compose files above
# 5. Replace hardcoded value in docs with: YOUR_CLOUDFLARE_API_TOKEN
# Verify DDNS containers restart and can still update DNS:
docker logs cloudflare-ddns --tail 20
```
---
### 3. OpenAI API Key (`sk-proj-C_IYp6io...`)
**Files to update:**
- `hosts/vms/homelab-vm/hoarder.yaml`
- `docs/services/individual/web.md` (replace with placeholder)
```bash
# 1. Go to https://platform.openai.com/api-keys
# 2. Delete the exposed key
# 3. Create a new key, set a usage limit
# 4. Update OPENAI_API_KEY in hoarder.yaml
# 5. Replace value in docs with: YOUR_OPENAI_API_KEY
```
---
## Priority 2 — OAuth / SSO Secrets
### 4. Grafana ↔ Authentik OAuth Secret
**Files to update:**
- `hosts/vms/homelab-vm/monitoring.yaml`
- `hosts/synology/atlantis/grafana.yml`
- `docs/infrastructure/authentik-sso.md` (replace with placeholder)
- `docs/services/individual/grafana-oauth.md` (replace with placeholder)
```bash
# 1. Log into Authentik admin: https://auth.vish.gg/if/admin/
# 2. Applications → Providers → find Grafana OAuth2 provider
# 3. Edit → regenerate Client Secret → copy both Client ID and Secret
# 4. Update in both compose files:
# GF_AUTH_GENERIC_OAUTH_CLIENT_ID: NEW_ID
# GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET: NEW_SECRET
# 5. Commit and push — both Grafana stacks restart automatically
# Verify SSO works after restart:
curl -I https://gf.vish.gg
```
---
### 5. Seafile ↔ Authentik OAuth Secret
**Files to update:**
- `hosts/synology/calypso/seafile-oauth-config.py`
- `docs/services/individual/seafile-oauth.md` (replace with placeholder)
```bash
# 1. Log into Authentik admin
# 2. Applications → Providers → find Seafile OAuth2 provider
# 3. Regenerate client secret
# 4. Update OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET in seafile-oauth-config.py
# 5. Re-run the config script on the Seafile server to apply
```
---
### 6. Authentik Secret Key (`RpRexcYo5HAz...`)
**Critical** — this key encrypts all Authentik data (tokens, sessions, stored credentials).
**File:** `hosts/synology/calypso/authentik/docker-compose.yaml`
```bash
# 1. Generate a new secret:
python3 -c "import secrets; print(secrets.token_urlsafe(50))"
# 2. Update AUTHENTIK_SECRET_KEY in docker-compose.yaml
# 3. Commit and push — Authentik will restart
# WARNING: All active Authentik sessions will be invalidated.
# Users will need to log back in. SSO-protected services
# may temporarily show login errors while Authentik restarts.
# Verify Authentik is healthy after restart:
docker logs authentik_server --tail 30
```
---
## Priority 3 — Application Secrets (Require Service Restart)
### 7. Gmail App Passwords
Five distinct app passwords were found across the repo. Revoke all of them
in Google Account → Security → App passwords, then create new per-service ones.
| Password | Used For | Active Files |
|----------|----------|-------------|
| (see Vaultwarden) | Mastodon, Joplin, Authentik SMTP | `matrix-ubuntu-vm/mastodon/.env.production.template`, `atlantis/joplin.yml`, `calypso/authentik/docker-compose.yaml` |
| (see Vaultwarden) | Vaultwarden SMTP | `atlantis/vaultwarden.yaml` |
| (see Vaultwarden) | Documenso SMTP | `atlantis/documenso/documenso.yaml` |
| (see Vaultwarden) | Reactive Resume v4 (archived) | `archive/reactive_resume_v4_archived/docker-compose.yml` |
| (see Vaultwarden) | Reactive Resume v5 (active) | `calypso/reactive_resume_v5/docker-compose.yml` |
**Best practice:** Create one app password per service, named clearly (e.g.,
`homelab-joplin`, `homelab-mastodon`). Update each file's `SMTP_PASS` /
`SMTP_PASSWORD` / `MAILER_AUTH_PASSWORD` / `smtp_password` field.
---
### 8. Matrix Synapse Secrets
Three secrets in `homeserver.yaml`, plus the TURN shared secret.
**File:** `hosts/synology/atlantis/matrix_synapse_docs/homeserver.yaml`
```bash
# Generate fresh values for each:
python3 -c "import secrets; print(secrets.token_urlsafe(48))"
# Fields to rotate:
# registration_shared_secret
# macaroon_secret_key
# form_secret
# turn_shared_secret
# After updating homeserver.yaml, restart Synapse:
docker restart synapse # or via Portainer
# Also update coturn config on the server directly:
ssh atlantis
nano /path/to/turnserver.conf
# Update: static-auth-secret=NEW_TURN_SECRET
systemctl restart coturn
# Update instructions.txt — replace old values with REDACTED
```
---
### 9. Mastodon `SECRET_KEY_BASE` + `OTP_SECRET`
**File:** `hosts/synology/atlantis/mastodon.yml`
**Also in:** `docs/services/individual/mastodon.md` (replace with placeholder)
```bash
# Generate new values:
openssl rand -hex 64 # for SECRET_KEY_BASE
openssl rand -hex 64 # for OTP_SECRET
# Update both in mastodon.yml
# Commit and push — GitOps restarts Mastodon
# WARNING: All active user sessions are invalidated. Users must log back in.
# Verify Mastodon web is accessible:
curl -I https://your-mastodon-domain/
docker logs mastodon_web --tail 20
```
---
### 10. Documenso Secrets (3 keys)
**Files:**
- `hosts/synology/atlantis/documenso/documenso.yaml`
- `hosts/synology/atlantis/documenso/Secrets.txt` (will be removed by sanitizer)
- `docs/services/individual/documenso.md` (replace with placeholder)
```bash
# Generate new values:
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # NEXTAUTH_SECRET
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # NEXT_PRIVATE_ENCRYPTION_KEY
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # NEXT_PRIVATE_ENCRYPTION_SECONDARY_KEY
# Update all three in documenso.yaml
# NOTE: Rotating encryption keys will invalidate signed documents.
# Confirm this is acceptable before rotating.
```
---
### 11. Paperless-NGX API Token
**Files:**
- `hosts/synology/calypso/paperless/paperless-ai.yml`
- `hosts/synology/calypso/paperless/README.md` (replace with placeholder)
- `docs/services/paperless.md` (replace with placeholder)
```bash
# 1. Log into Paperless web UI
# 2. Admin → Auth Token → delete existing, generate new
# 3. Update PAPERLESS_API_TOKEN in paperless-ai.yml
# 4. Commit and push
```
---
### 12. Immich JWT Secret (Both NAS)
**Files:**
- `hosts/synology/atlantis/immich/stack.env` (will be removed by sanitizer)
- `hosts/synology/calypso/immich/stack.env` (will be removed by sanitizer)
Since these files are removed by the sanitizer, ensure they are in `.gitignore`
or managed via Portainer env variables going forward.
```bash
# Generate new secret:
openssl rand -base64 96
# Update JWT_SECRET in both stack.env files locally,
# then apply via Portainer (not committed to git).
# WARNING: All active Immich sessions invalidated.
```
---
### 13. Revolt/Stoatchat — LiveKit API Secret + VAPID Private Key
**Files:**
- `hosts/vms/seattle/stoatchat/livekit.yml`
- `hosts/vms/seattle/stoatchat/Revolt.overrides.toml`
- `hosts/vms/homelab-vm/stoatchat.yaml`
- `docs/services/stoatchat/Revolt.overrides.toml` (replace with placeholder)
- `hosts/vms/seattle/stoatchat/DEPLOYMENT_SUMMARY.md` (replace with placeholder)
```bash
# Generate new LiveKit API key/secret pair:
# Use the LiveKit CLI or generate random strings:
python3 -c "import secrets; print(secrets.token_urlsafe(24))" # API key
python3 -c "import secrets; print(secrets.token_urlsafe(32))" # API secret
# Generate new VAPID key pair:
npx web-push generate-vapid-keys
# or: python3 -c "from py_vapid import Vapid; v=Vapid(); v.generate_keys(); print(v.private_key)"
# Update in livekit.yml and Revolt.overrides.toml
# Restart LiveKit and Revolt services
```
---
### 14. Jitsi Internal Auth Passwords (6 passwords)
**File:** `hosts/synology/atlantis/jitsi/jitsi.yml`
**Also in:** `hosts/synology/atlantis/jitsi/.env` (will be removed by sanitizer)
```bash
# Generate new passwords for each variable:
for var in JICOFO_COMPONENT_SECRET JICOFO_AUTH_PASSWORD JVB_AUTH_PASSWORD \
JIGASI_XMPP_PASSWORD JIBRI_RECORDER_PASSWORD JIBRI_XMPP_PASSWORD; do
echo "$var=$(openssl rand -hex 10)"
done
# Update all 6 in jitsi.yml
# Restart the entire Jitsi stack — all components must use the same passwords
docker compose -f jitsi.yml down && docker compose -f jitsi.yml up -d
```
---
### 15. SNMP v3 Auth + Priv Passwords
Used for NAS monitoring — same credentials across 6 files.
**Files to update:**
- `hosts/synology/setillo/prometheus/snmp.yml`
- `hosts/synology/atlantis/grafana_prometheus/snmp.yml`
- `hosts/synology/atlantis/grafana_prometheus/snmp_mariushosting.yml`
- `hosts/synology/calypso/grafana_prometheus/snmp.yml`
- `hosts/vms/homelab-vm/monitoring.yaml`
```bash
# 1. Log into each Synology NAS DSM
# 2. Go to Control Panel → Terminal & SNMP → SNMP tab
# 3. Update SNMPv3 auth password and privacy password to new values
# 4. Update the same values in all 5 config files above
# 5. The archive file (deprecated-monitoring-stacks) can just be left for
# the sanitizer to redact.
```
---
### 16. Invidious `hmac_key`
**Files:**
- `hosts/physical/concord-nuc/invidious/invidious.yaml`
- `hosts/physical/concord-nuc/invidious/invidious_old/invidious.yaml`
- `hosts/synology/atlantis/invidious.yml`
```bash
# Generate new hmac_key:
python3 -c "import secrets; print(secrets.token_hex(16))"
# Update hmac_key in each active invidious.yaml
# Restart Invidious containers
```
---
### 17. Open WebUI Secret Keys
**Files:**
- `hosts/vms/contabo-vm/ollama/docker-compose.yml`
- `hosts/synology/atlantis/ollama/docker-compose.yml`
- `hosts/synology/atlantis/ollama/64_bit_key.txt` (will be removed by sanitizer)
```bash
# Generate new key:
openssl rand -hex 32
# Update WEBUI_SECRET_KEY in both compose files
# Restart Open WebUI containers — active sessions invalidated
```
---
### 18. Portainer Edge Key
**File:** `hosts/vms/homelab-vm/portainer_agent.yaml`
```bash
# 1. Log into Portainer at https://192.168.0.200:9443
# 2. Go to Settings → Edge Compute → Edge Agents
# 3. Find the homelab-vm agent and regenerate its edge key
# 4. Update EDGE_KEY in portainer_agent.yaml with the new base64 value
# 5. Restart the Portainer edge agent container
```
---
### 19. OpenProject Secret Key
**File:** `hosts/vms/homelab-vm/openproject.yml`
**Also in:** `docs/services/individual/openproject.md` (replace with placeholder)
```bash
openssl rand -hex 64
# Update OPENPROJECT_SECRET_KEY_BASE in openproject.yml
# Restart OpenProject — sessions invalidated
```
---
### 20. RomM Auth Secret Key
**File:** `hosts/vms/homelab-vm/romm/romm.yaml`
**Also:** `hosts/vms/homelab-vm/romm/secret_key.yaml` (will be removed by sanitizer)
```bash
openssl rand -hex 32
# Update ROMM_AUTH_SECRET_KEY in romm.yaml
# Restart RomM — sessions invalidated
```
---
### 21. Hoarder NEXTAUTH Secret
**File:** `hosts/vms/homelab-vm/hoarder.yaml`
**Also in:** `docs/services/individual/web.md` (replace with placeholder)
```bash
openssl rand -base64 36
# Update NEXTAUTH_SECRET in hoarder.yaml
# Restart Hoarder — sessions invalidated
```
---
## Priority 4 — Shared / Weak Passwords
### 22. `REDACTED_PASSWORD123!` — Used Across 5+ Services
This password is the same for all of the following. Change each to a
**unique** strong password:
| Service | File | Variable |
|---------|------|----------|
| NetBox | `hosts/synology/atlantis/netbox.yml` | `SUPERUSER_PASSWORD` |
| Paperless admin | `hosts/synology/calypso/paperless/docker-compose.yml` | `PAPERLESS_ADMIN_PASSWORD` |
| Seafile admin | `hosts/synology/calypso/seafile-server.yaml` | `INIT_SEAFILE_ADMIN_PASSWORD` |
| Seafile admin (new) | `hosts/synology/calypso/seafile-new.yaml` | `INIT_SEAFILE_ADMIN_PASSWORD` |
| PhotoPrism | `hosts/physical/anubis/photoprism.yml` | `PHOTOPRISM_ADMIN_PASSWORD` |
| Hemmelig | `hosts/vms/bulgaria-vm/hemmelig.yml` | `SECRET_JWT_SECRET` |
| Vaultwarden admin | `hosts/synology/atlantis/bitwarden/bitwarden_token.txt` | (source password) |
For each: generate `openssl rand -base64 18`, update in the compose file,
restart the container, then log in to verify.
---
### 23. `REDACTED_PASSWORD` — Used Across 3 Services
| Service | File | Variable |
|---------|------|----------|
| Gotify | `hosts/vms/homelab-vm/gotify.yml` | `GOTIFY_DEFAULTUSER_PASS` |
| Pi-hole | `hosts/synology/atlantis/pihole.yml` | `WEBPASSWORD` |
| Stirling PDF | `hosts/synology/atlantis/stirlingpdf.yml` | `SECURITY_INITIAL_LOGIN_PASSWORD` |
---
### 24. `mastodon_pass_2026` — Live PostgreSQL Password
**Files:**
- `hosts/vms/matrix-ubuntu-vm/mastodon/.env.production.template`
- `hosts/vms/matrix-ubuntu-vm/docs/SETUP.md`
```bash
# On the matrix-ubuntu-vm server:
ssh YOUR_WAN_IP
sudo -u postgres psql
ALTER USER mastodon WITH PASSWORD 'REDACTED_PASSWORD';
\q
# Update the password in .env.production.template and Mastodon's running config
# Restart Mastodon services
```
---
### 25. Watchtower API Token (`REDACTED_WATCHTOWER_TOKEN`)
| File |
|------|
| `hosts/synology/atlantis/watchtower.yml` |
| `hosts/synology/calypso/prometheus.yml` |
```bash
# Generate a proper random token:
openssl rand -hex 20
# Update WATCHTOWER_HTTP_API_TOKEN in both files
# Update any scripts that call the Watchtower API
```
---
### 26. `test:test` SSH Credentials on `YOUR_WAN_IP`
The matrix-ubuntu-vm CREDENTIALS.md shows a `test` user with password `test`.
```bash
# SSH to the server and remove or secure the test account:
ssh YOUR_WAN_IP
passwd test # change to a strong password
# or: userdel -r test # remove entirely if unused
```
---
## Priority 5 — Network Infrastructure
### 27. Management Switch Password Hashes
**File:** `mgmtswitch.conf` (will be removed from public mirror by sanitizer)
The SHA-512 hashes for `root`, `vish`, and `vkhemraj` switch accounts are
crackable offline. Rotate the switch passwords:
```bash
# SSH to the management switch
ssh admin@10.0.0.15
# Change passwords for all local accounts:
enable
configure terminal
username root secret NEW_PASSWORD
username vish secret NEW_PASSWORD
username vkhemraj secret NEW_PASSWORD
write memory
```
---
## Final Verification
After completing all rotations:
```bash
# 1. Commit and push all file changes
git add -A
git commit -m "chore(security): rotate all exposed credentials"
git push origin main
# 2. Wait for the mirror workflow to complete, then pull:
git -C /home/homelab/organized/repos/homelab-optimized pull
# 3. Verify none of the old secrets appear in the public mirror:
cd /home/homelab/organized/repos/homelab-optimized
grep -r "77e3ddaf\|52fa6ccb\|FGXlHM7d\|sk-proj-C_IYp6io\|ArP5XWdkwVyw\|bdtrpmpce\|toiunzuby" . 2>/dev/null
grep -r "244c619d\|RpRexcYo5\|mastodon_pass\|REDACTED_PASSWORD\|REDACTED_PASSWORD\|REDACTED_WATCHTOWER_TOKEN" . 2>/dev/null
grep -r "2e80b1b7d3a\|eca299ae59\|rxmr4tJoqfu\|ZjCofRlfm6\|QE5SudhZ99" . 2>/dev/null
# All should return no results
# 4. Verify GitOps deployments are healthy in Portainer:
# https://192.168.0.200:9443
```
---
## Going Forward — Preventing This Again
The root cause: secrets hard-coded in compose files that get committed to git.
**Rules:**
1. **Never hard-code secrets in compose files** — use Docker Secrets, or an
`.env` file excluded by `.gitignore` (Portainer can load env files from the
host at deploy time)
2. **Never put real values in documentation** — use `YOUR_API_KEY` placeholders
3. **Never create `Secrets.txt` or `CREDENTIALS.md` files in the repo** — use
a password manager (you already have Vaultwarden/Bitwarden)
4. **Run the sanitizer locally** before any commit that touches secrets:
```bash
# Test in a temp copy — see what the sanitizer would catch:
tmpdir=$(mktemp -d)
cp -r /path/to/homelab "$tmpdir/"
python3 "$tmpdir/homelab/.gitea/sanitize.py"
```
## Related Documentation
- [Security Hardening](../security/SERVER_HARDENING.md)
- [Repository Sanitization](../admin/REPOSITORY_SANITIZATION.md)
- [GitOps Deployment Guide](../admin/gitops-deployment-guide.md)
## Portainer Git Credential Rotation
The saved Git credential **`portainer-homelab`** (credId: 1) is used by ~43 stacks to
pull compose files from `git.vish.gg`. When the Gitea token expires or is rotated,
all those stacks fail to redeploy.
```bash
# 1. Generate a new Gitea token at https://git.vish.gg/user/settings/applications
# Scope: read:repository
# 2. Test the token:
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: token YOUR_NEW_TOKEN" \
"https://git.vish.gg/api/v1/repos/Vish/homelab"
# Should return 200
# 3. Update in Portainer:
curl -k -s -X PUT \
-H "X-API-Key: "REDACTED_API_KEY" \
-H "Content-Type: application/json" \
"https://192.168.0.200:9443/api/users/1/gitcredentials/1" \
-d '{"name":"portainer-homelab","username":"vish","password":"YOUR_NEW_TOKEN"}'
```
> Note: The API update may not immediately propagate to automated pulls.
> Pass credentials inline in redeploy calls to force use of the new token.
---
## Change Log
- 2026-02-27 — Incident: sanitization commit `037d766a` replaced credentials with
`REDACTED_PASSWORD` placeholders across 14 compose files. All affected containers
detected via Portainer API env scan and restored from `git show 037d766a^`. Added
Portainer Git credential rotation section above.
- 2026-02-20 — Initial creation (8 items)
- 2026-02-20 — Expanded after full private repo audit (27 items across 34 exposure categories)