|
|
|
|
@@ -1,282 +0,0 @@
|
|
|
|
|
# Project Memory
|
|
|
|
|
|
|
|
|
|
## Receipt Manager (separate from NextSnap)
|
|
|
|
|
- **Source**: `compute1:~/` (receipts.html + server.py), Gitea repo `kamaji/receipt-manager`
|
|
|
|
|
- **Container**: `receipt-manager` on docker1 (`~/receipt-manager/`), port 8082
|
|
|
|
|
- **Deploy**: `scp receipts.html server.py docker1:~/receipt-manager/ && ssh docker1 'cd ~/receipt-manager && docker-compose down && docker-compose up -d --build'`
|
|
|
|
|
- **Stack**: Python stdlib HTTP server, vanilla JS frontend, Nextcloud WebDAV for storage
|
|
|
|
|
- **NOT** part of NextSnap — separate app, separate container, separate repo
|
|
|
|
|
|
|
|
|
|
## NextSnap Architecture
|
|
|
|
|
- Flask PWA on docker1 (~/nextsnap), Docker container
|
|
|
|
|
- Deploy: `ssh docker1 'cd ~/nextsnap && docker-compose down && docker-compose up -d --build'`
|
|
|
|
|
- Files accessed via SSH (scp to deploy, ssh to run commands)
|
|
|
|
|
- Frontend: Vanilla JS, Dexie.js (IndexedDB), Service Worker for offline-first
|
|
|
|
|
- Reverse proxy: nginx-reverse host, config at `/etc/nginx/sites-available/reverse-proxy.conf`
|
|
|
|
|
|
|
|
|
|
## Infrastructure - nginx-reverse
|
|
|
|
|
- **VM**: on host `compute1` (migrated from console, 2026-02-22)
|
|
|
|
|
- **Disk**: `/1TB/vm/nginx-reverse.qcow2` on compute1
|
|
|
|
|
- **IP**: `192.168.128.19`
|
|
|
|
|
- **Sudo**: `%sudo` group has NOPASSWD:ALL; kamaji is in sudo group
|
|
|
|
|
- **SSH from compute1**: Uses `id_rsa` key (added to authorized_keys)
|
|
|
|
|
- **Certbot**: Webroot at `/var/www/letsencrypt`, auto-renewal enabled
|
|
|
|
|
- **Services**: nginx (reverse proxy), iperf3 (speed test), WireGuard (wg0 — direct VPN server)
|
|
|
|
|
- **WireGuard**: Direct VPN server on port 51820, clients connect via `104.52.199.76:51820`
|
|
|
|
|
- Autostart enabled on compute1
|
|
|
|
|
|
|
|
|
|
## Infrastructure - docker1
|
|
|
|
|
- **VM**: "docker1" on host `compute1` (migrated from "docker-box" on console, 2026-02-22)
|
|
|
|
|
- **Disk**: `/1TB/vm/docker-box.qcow2` on compute1
|
|
|
|
|
- **RAM**: 8 GB, **vCPUs**: 8
|
|
|
|
|
- **IP**: `192.168.128.5`
|
|
|
|
|
- **Root disk**: 24 GB `/dev/vda1` — tight with Docker; monitor usage
|
|
|
|
|
- **NFS mount**: `console:/Storage` → `/Storage` (in fstab with `_netdev`)
|
|
|
|
|
- **Nextcloud data**: symlinked `/opt/nextcloud/data` → `/Storage/nextcloud/data`
|
|
|
|
|
- **Nextcloud compose**: `/opt/nextcloud/docker-compose.yml` (app + redis + mariadb)
|
|
|
|
|
- **Nextcloud Office**: Built-in CODE (richdocumentscode AppImage), runs COOLWSD on port 9983 inside container
|
|
|
|
|
- Autostart enabled on compute1
|
|
|
|
|
|
|
|
|
|
## Nextcloud Backup (legacy)
|
|
|
|
|
- **Script**: `docker1:~/nextcloud-backup.sh` — hourly cron, predates snapback
|
|
|
|
|
- **Note**: Snapback now handles the same backups (nextcloud-data, nextcloud-app, nextcloud-db jobs)
|
|
|
|
|
- SSH from docker1→compute1 uses kamaji's ed25519 key; sudo rsync needs explicit SSH opts
|
|
|
|
|
|
|
|
|
|
## Two User Classes
|
|
|
|
|
- **Admin**: NC admin login (username+password), full access including admin panel
|
|
|
|
|
- **Tech**: Field workers, login with username+PIN, access to capture/queue/browser only
|
|
|
|
|
- Tech users stored in `data/tech_users.json` (Docker named volume `app_data` at `/app/data`)
|
|
|
|
|
- PIN hashed with werkzeug.security; NC password stored plaintext (admin-visible)
|
|
|
|
|
- Tech user creation auto-provisions NC account via OCS API
|
|
|
|
|
- Session stores NC password (base64) for transparent API calls
|
|
|
|
|
- `session['user_type']` = `'admin'` | `'tech'`; `session['is_admin']` for nav guards
|
|
|
|
|
- Admin panel: tappable user list → detail modal (enable/disable, reset PIN, reset NC password, delete)
|
|
|
|
|
- Login page: tabbed UI — "Tech Login" (default) and "Admin Login"
|
|
|
|
|
|
|
|
|
|
## Key Bugs & Lessons
|
|
|
|
|
|
|
|
|
|
### Service Worker Caching (CRITICAL)
|
|
|
|
|
- SW was cache-first for ALL routes including HTML pages → stale code served forever
|
|
|
|
|
- Fix: Only `/static/` uses cache-first; pages + API use network-first
|
|
|
|
|
- Always bump SW cache versions when changing any static asset
|
|
|
|
|
- Even after SW bump, user needs one hard refresh to pick up new SW
|
|
|
|
|
|
|
|
|
|
### DOM Element Destroyed by innerHTML (queue.html)
|
|
|
|
|
- `getElementById('empty-state')` returned null after `innerHTML = ''` destroyed it
|
|
|
|
|
- Caused silent crash on every subsequent `loadQueue()` call
|
|
|
|
|
- Counters still worked because `updateStats()` ran independently
|
|
|
|
|
- Lesson: Never reference DOM elements inside a container you clear with innerHTML
|
|
|
|
|
|
|
|
|
|
### iOS Safari Blob Eviction
|
|
|
|
|
- Blobs stored in IndexedDB are file-backed references that iOS can evict
|
|
|
|
|
- `instanceof Blob` and `.size` checks pass but reading data throws "Load failed"
|
|
|
|
|
- Fix: Store as ArrayBuffer (inline bytes) + `navigator.storage.persist()`
|
|
|
|
|
- `Storage.getBlob(photo)` converts ArrayBuffer→Blob at read time
|
|
|
|
|
|
|
|
|
|
### iOS Safari Fetch "Load Failed"
|
|
|
|
|
- Generic error for any network failure (backgrounding, connectivity)
|
|
|
|
|
- Upload POST fails client-side, never reaches server
|
|
|
|
|
- Show friendly messages, hide transient errors (< 3 retries) from UI
|
|
|
|
|
|
|
|
|
|
### Nextcloud OCS API 412 Error
|
|
|
|
|
- All OCS endpoints require `OCS-APIRequest: true` header (CSRF protection)
|
|
|
|
|
- Without it → 412 Precondition Failed
|
|
|
|
|
|
|
|
|
|
### Docker Volume Permissions
|
|
|
|
|
- Named volumes mounted to dirs created in Dockerfile inherit ownership from image
|
|
|
|
|
- If volume already exists with root ownership, must `docker-compose down -v` to recreate
|
|
|
|
|
- Container runs as `nextsnap` (uid 1000) — `/app/data` must be owned by nextsnap
|
|
|
|
|
|
|
|
|
|
### iOS Safari `inputmode="numeric"` + `type="password"`
|
|
|
|
|
- Combining these causes browser validation error "string did not match expected pattern"
|
|
|
|
|
- Fix: Use `type="text"` with `inputmode="numeric"` and validate in JS
|
|
|
|
|
|
|
|
|
|
### Nextcloud CODE "Document loading failed"
|
|
|
|
|
- COOLWSD (CODE server) stores temp files in `/tmp` jails inside the container
|
|
|
|
|
- Disk full → "Low disk space" / "Out of storage" in COOLWSD logs → 400/408 on proxy.php
|
|
|
|
|
- Memory exhaustion → inconsistent loading (works sometimes), CODE processes killed/timed out
|
|
|
|
|
- Fix: Free disk space, increase VM RAM; COOLWSD log at `/tmp/coolwsd.*/coolwsd.log` inside container
|
|
|
|
|
- After fixing, restart container so COOLWSD gets a clean start
|
|
|
|
|
|
|
|
|
|
### MikroTik (cornerLot)
|
|
|
|
|
- **Model**: RB750Gr3, RouterOS 6.48.2, identity `cornerLot`
|
|
|
|
|
- **IP**: `192.168.88.1` (clients), `192.168.128.1` (servers)
|
|
|
|
|
- **SSH**: Port 65523, user `kamaji`
|
|
|
|
|
- **SSH keys**: RouterOS 6.x disables password auth once a key is imported; key must work or user is locked out (WinBox recovery only)
|
|
|
|
|
- **SSH from compute1**: Dedicated key `~/.ssh/mikrotik_rsa`, SSH config alias `mikrotik`; requires `ssh-rsa` algo + `DEFAULT:SHA1` crypto policy (RHEL 9 blocks SHA-1 by default)
|
|
|
|
|
- **NAT rules**: Working dstnat rules use `dst-address=104.52.199.76`, NOT `in-interface=ether1`
|
|
|
|
|
- **Hairpin NAT**: srcnat masquerade for `192.168.0.0/16` at rule 2
|
|
|
|
|
- **Forward chain**: `action=accept` with no conditions at rule 3 (accepts all)
|
|
|
|
|
|
|
|
|
|
### Podman SELinux on compute1
|
|
|
|
|
- Volume mounts require `:z` flag (e.g. `-v /path:/mount:ro,z`)
|
|
|
|
|
- Without it: `PermissionError: [Errno 13] Permission denied` inside container
|
|
|
|
|
|
|
|
|
|
### NFS Mount Not in fstab
|
|
|
|
|
- `/Storage` was NFS-mounted manually (`console:/Storage`) but not in fstab
|
|
|
|
|
- After VM reboot, Docker couldn't start nextcloud-app: broken symlink to `/Storage/nextcloud/data`
|
|
|
|
|
- Fix: Added `console:/Storage /Storage nfs defaults,_netdev 0 0` to fstab
|
|
|
|
|
|
|
|
|
|
### Infrastructure - console
|
|
|
|
|
- **IP**: `192.168.88.5` (servers subnet)
|
|
|
|
|
- **Role**: KVM host (libvirt, Debian), NFS server (`/Storage`)
|
|
|
|
|
- **VMs remaining**: devtest1 (autostart), pihole2, unifi, win10-signed_to_outlook.com
|
|
|
|
|
- **Migrated off** (2026-02-22): docker-box→docker1, docker2, nginx-reverse, pihole, jellyfin2, git1 — all to compute1
|
|
|
|
|
- **win10-signed_to_outlook.com**: moved from compute1 (2026-02-23), `/var/lib/libvirt/images/win10.qcow2`, 8 GB RAM, 4 vCPUs, autostart enabled, Hyper-V enlightenments enabled (vpindex, synic, stimer, frequencies, reenlightenment, tlbflush — dropped idle CPU from ~32% to ~5%)
|
|
|
|
|
|
|
|
|
|
## Gitea (git1)
|
|
|
|
|
- **VM**: on host `compute1` (migrated from console, 2026-02-22)
|
|
|
|
|
- **Disk**: `/1TB/vm/git1.qcow2` on compute1
|
|
|
|
|
- **IP**: `192.168.128.23`
|
|
|
|
|
- **URL**: https://git.sdanywhere.com/
|
|
|
|
|
- **Version**: 1.25.4
|
|
|
|
|
- **Config**: `/etc/gitea/app.ini`, runs as `kamaji` user
|
|
|
|
|
- **Repo root**: `/srv/git/`
|
|
|
|
|
- **SSH push**: Use `kamaji@git1:kamaji/<repo>.git` format (Gitea SSH wrapper)
|
|
|
|
|
- **Do NOT** use bare repo paths (`git1:/srv/git/...`) — pre-receive hook rejects without Gitea env
|
|
|
|
|
- **authorized_keys**: Plain keys (docker1, nginx-reverse, dns1) for shell access; RSA key has Gitea `command=` wrapper for git operations
|
|
|
|
|
- **Shell access to git1**: Use docker1 (ed25519 key), not this machine (RSA key goes through Gitea wrapper)
|
|
|
|
|
- **API**: Available at `https://git.sdanywhere.com/api/v1/`, generate tokens via `gitea --config /etc/gitea/app.ini admin user generate-access-token`
|
|
|
|
|
- **Repos**: `kamaji/receipt-manager` (Receipt Manager), `kamaji/snapback` (backup system)
|
|
|
|
|
|
|
|
|
|
## Snapback (compute1)
|
|
|
|
|
- **Repo**: `kamaji/snapback` on Gitea
|
|
|
|
|
- **Deployed to**: `compute1:~/snapback/`
|
|
|
|
|
- **Backup root**: `/1TB/backups/` on compute1
|
|
|
|
|
- **Cron**: `0 * * * *` runs all jobs hourly
|
|
|
|
|
- **SSH key**: `id_rsa` on compute1 (not ed25519)
|
|
|
|
|
- **Log**: `~/snapback/snapback.log` (not /var/log — not writable by kamaji)
|
|
|
|
|
- **ntfy**: Sends to `https://ntfy.sdanywhere.com` topic `backups`
|
|
|
|
|
- **Retention**: 24 hourly, 7 daily, 4 weekly
|
|
|
|
|
- **Jobs**: nextcloud-data (rsync), nextcloud-app (rsync+sudo), nextcloud-db (db_dump), gitea-repos (rsync), gitea-data (rsync), gitea-db (db_dump), nginx-config (rsync), nginx-certs (rsync+sudo), nginx-wireguard (rsync+sudo), mikrotik-config (db_dump, `/export`), mikrotik-backup (db_dump, local, binary `.backup`)
|
|
|
|
|
- **Local jobs**: `local: true` on db_dump runs command directly instead of via SSH
|
|
|
|
|
- **MikroTik SSH**: Dedicated key `~/.ssh/mikrotik_rsa` on compute1; SSH config alias `mikrotik` (port 65523, user kamaji, `ssh-rsa` algo for RouterOS 6.x); requires `DEFAULT:SHA1` crypto policy on compute1 (RHEL 9)
|
|
|
|
|
- **MikroTik helper**: `~/snapback/mikrotik-backup.sh` — creates backup on router, SCPs it off, outputs to stdout
|
|
|
|
|
|
|
|
|
|
## Snapback Web Browser (compute1)
|
|
|
|
|
- **Container**: `snapback-web` via podman on compute1, port 8082
|
|
|
|
|
- **Public URL**: `https://backups.sdanywhere.com` (via nginx-reverse proxy)
|
|
|
|
|
- **Auth**: HTTP Basic Auth (BROWSER_USER/BROWSER_PASSWORD env vars)
|
|
|
|
|
- **Source**: `compute1:~/snapback/web/` (Flask + gunicorn, vanilla JS SPA)
|
|
|
|
|
- **Mounts**: `/1TB/backups:/backups:ro,z`, `~/.ssh:/ssh:ro,z`, `config.yml:/app/config.yml:ro,z`
|
|
|
|
|
- **Features**: Browse snapshots, preview text/images, download files/zips, restore via rsync
|
|
|
|
|
- **SSH key handling**: Copies /ssh/* to /tmp/.ssh/ with correct permissions at startup
|
|
|
|
|
- **Restore**: Only rsync-type jobs; builds rsync command from config job metadata
|
|
|
|
|
- **SELinux**: Podman on compute1 requires `:z` flag on volume mounts
|
|
|
|
|
- **Deploy**: `ssh compute1 'podman build -t snapback-web ~/snapback/web/ && podman stop snapback-web && podman rm snapback-web && podman run -d --name snapback-web -p 8082:8082 -v /1TB/backups:/backups:ro,z -v /home/kamaji/.ssh:/ssh:ro,z -v /home/kamaji/snapback/config.yml:/app/config.yml:ro,z -e BROWSER_USER=kamaji -e BROWSER_PASSWORD=<pw> --restart unless-stopped snapback-web'`
|
|
|
|
|
|
|
|
|
|
## Sysmon (compute1)
|
|
|
|
|
- **Source**: `devtest1:~/sysmon/`, deployed to `compute1:~/sysmon/`
|
|
|
|
|
- **Dashboard**: Podman container `sysmon` on compute1, port 8083 (pure aggregator, no `/proc`)
|
|
|
|
|
- **Stack**: Flask + gunicorn, vanilla JS frontend, dark theme
|
|
|
|
|
- **Architecture**: Single codebase, two modes controlled by `SYSMON_SERVERS` env var
|
|
|
|
|
- **Agent mode** (no env var): Reads local `/proc` + `sudo virsh` for VMs, serves `/api/stats`
|
|
|
|
|
- **Dashboard mode** (env var set): Pure aggregator, no `/proc` or CPU sampler; serves UI at `/`
|
|
|
|
|
- **Config**: `SYSMON_SERVERS="compute1:http://host.containers.internal:8084,console:http://192.168.88.5:8083"`
|
|
|
|
|
- **Agents**: compute1 (port 8084, systemd), console (port 8083, systemd) — both bare installs, `python3 -m gunicorn`
|
|
|
|
|
- **UI**: Hash-based routing — dashboard summary cards (`#`) + server detail view (`#<name>`) with CPU grid, memory, load, uptime, VM table
|
|
|
|
|
- **VM base info**: `sudo virsh dominfo` per VM (30s cache) — name, state, vcpus, memory, autostart
|
|
|
|
|
- **VM CPU %**: `sudo virsh domstats --cpu-total --balloon` (5s cache), delta tracking for CPU %
|
|
|
|
|
- **VM memory**: From balloon stats (`available - unused`); Windows VMs lack guest-side stats, show allocated only
|
|
|
|
|
- **VM disk**: `sudo virsh guestinfo --filesystem` per VM (30s cache, async background refresh to avoid blocking)
|
|
|
|
|
- **Dashboard fetch timeout**: 8s (cold-cache agent responses can take 2-3s due to dominfo calls)
|
|
|
|
|
- **Deploy dashboard**: `scp ~/sysmon/{app.py,templates/index.html,run.sh,Dockerfile} compute1:~/sysmon/ && ssh compute1 'mkdir -p ~/sysmon/templates && mv ~/sysmon/index.html ~/sysmon/templates/ && ~/sysmon/run.sh'`
|
|
|
|
|
- **Deploy agent**: `~/sysmon/deploy-agent.sh <host> [port]` (default port 8083)
|
|
|
|
|
- **SELinux note**: compute1 (RHEL 9) blocks `~/.local/bin/gunicorn` from systemd; use `python3 -m gunicorn` instead
|
|
|
|
|
- **Adding servers**: Add `name:url` to `SYSMON_SERVERS`, deploy agent on new host, rebuild dashboard
|
|
|
|
|
|
|
|
|
|
## iperf3 (nginx-reverse)
|
|
|
|
|
- **Service**: `iperf3.service` (systemd, installed via apt)
|
|
|
|
|
- **Port**: 5201 (TCP+UDP)
|
|
|
|
|
- **DNS**: `speed.sdanywhere.com`
|
|
|
|
|
- **MikroTik NAT**: dstnat rules use `dst-address=104.52.199.76` (NOT `in-interface=ether1` — WAN traffic doesn't match ether1)
|
|
|
|
|
- **Hairpin NAT**: srcnat masquerade for `192.168.0.0/16` (rule 2 in NAT table)
|
|
|
|
|
|
|
|
|
|
## Infrastructure - Pi-hole HA
|
|
|
|
|
- **pihole1** (primary): `192.168.128.3` on compute1, `/1TB/vm/pihole.qcow2`, 2 GB RAM, 2 vCPUs
|
|
|
|
|
- **pihole2** (replica): `192.168.128.2` on console, `/var/lib/libvirt/images/pihole2.qcow2`, 1 GB RAM, 2 vCPUs
|
|
|
|
|
- Both running Pi-hole v6.4, autostart enabled
|
|
|
|
|
- **Sync**: Nebula Sync container (`nebula-sync`) on docker1, full sync every 30 min (pihole1 → pihole2)
|
|
|
|
|
- **DHCP DNS**: MikroTik hands out both `192.168.128.3,192.168.128.2`
|
|
|
|
|
- **Cross-host redundancy**: pihole1 on compute1, pihole2 on console
|
|
|
|
|
- **Admin password**: both set to same password via `sudo pihole setpassword`
|
|
|
|
|
- **pihole2 SSH**: kamaji user, ed25519 key generated on pihole2, can SSH to pihole1
|
|
|
|
|
|
|
|
|
|
## Infrastructure - unifi
|
|
|
|
|
- **VM**: on host `console` (moved back from compute1, 2026-02-23)
|
|
|
|
|
- **Disk**: `/var/lib/libvirt/images/unifi.qcow2` on console
|
|
|
|
|
- **IP**: `192.168.128.6`
|
|
|
|
|
- **RAM**: 1 GB, **vCPUs**: 2
|
|
|
|
|
- **Services**: UniFi Network Controller
|
|
|
|
|
- Autostart enabled on console
|
|
|
|
|
|
|
|
|
|
## Infrastructure - jellyfin2
|
|
|
|
|
- **VM**: on host `compute1` (migrated from console, 2026-02-22)
|
|
|
|
|
- **Disk**: `/1TB/vm/jellyfin2.qcow2` on compute1
|
|
|
|
|
- **IP**: `192.168.128.4`
|
|
|
|
|
- **RAM**: 1 GB, **vCPUs**: 2
|
|
|
|
|
- **NFS mount**: `console:/Storage` → `/Storage` (in fstab with `_netdev`)
|
|
|
|
|
- **Services**: Jellyfin media server (port 8096)
|
|
|
|
|
- Autostart enabled on compute1
|
|
|
|
|
|
|
|
|
|
## Infrastructure - zoneminder
|
|
|
|
|
- **VM**: on host `compute1`, cloned from debian12-template (2026-02-23)
|
|
|
|
|
- **Disk**: `/1TB/vm/zoneminder.qcow2` on compute1
|
|
|
|
|
- **IP**: `192.168.128.20`
|
|
|
|
|
- **RAM**: 4 GB, **vCPUs**: 4
|
|
|
|
|
- **OS**: Debian 12 Bookworm
|
|
|
|
|
- **Services**: ZoneMinder 1.36.33 (CCTV/surveillance), Apache2, MariaDB
|
|
|
|
|
- **DB**: `zm` database, user `zmuser`/`zmpass`
|
|
|
|
|
- **Web UI**: `http://192.168.128.20/zm/`
|
|
|
|
|
- **Config**: `/etc/zm/zm.conf` (group `www-data`), overrides in `/etc/zm/conf.d/`
|
|
|
|
|
- **ffmpeg**: Configured in `/etc/zm/conf.d/03-ffmpeg.conf`
|
|
|
|
|
- **Sudo**: kamaji has NOPASSWD:ALL via `/etc/sudoers.d/kamaji`
|
|
|
|
|
- Autostart enabled on compute1
|
|
|
|
|
|
|
|
|
|
## Infrastructure - compute1 (VM host)
|
|
|
|
|
- **IP**: `192.168.88.9` (servers subnet), also reachable as `compute1`
|
|
|
|
|
- **OS**: RHEL 9 (Rocky), QEMU at `/usr/libexec/qemu-kvm`
|
|
|
|
|
- **libvirt**: Machine type `q35` (RHEL variant), VNC graphics (no SPICE support)
|
|
|
|
|
- **Networking**: Bridge `br0` on `enp4s0f1` for VM bridged networking
|
|
|
|
|
- **VM storage**: `/1TB/vm/` on 932 GB local disk
|
|
|
|
|
- **VMs hosted**: docker1, docker2, nginx-reverse, pihole, jellyfin2, git1, zoneminder, macos/SheepShaver, dos/DOSBox
|
|
|
|
|
- **Migration notes**: Adapted from console (Debian): emulator path, machine type, macvtap→bridge, SPICE→VNC
|
|
|
|
|
|
|
|
|
|
## Retro VMs
|
|
|
|
|
See [retro-vms.md](retro-vms.md) — DOSBox (192.168.128.31) and SheepShaver/Mac OS 9 (192.168.128.30) on compute1, via Guacamole
|
|
|
|
|
|
|
|
|
|
## Guacamole (docker1)
|
|
|
|
|
- **Containers**: `guacamole`, `guacd`, `guac-mysql` on docker1
|
|
|
|
|
- **Compose**: `docker1:~/guacamole/docker-compose.yml`
|
|
|
|
|
- **DB**: MySQL 8.0, user `guacuser` / `guacpassword`, database `guacamole`
|
|
|
|
|
- **Port**: 8080 (guacamole webapp)
|
|
|
|
|
- **Auth**: MySQL + LDAP (OpenLDAP, `ou=users,dc=sdanywhere,dc=com`)
|
|
|
|
|
- **kamaji entity_id**: 2
|
|
|
|
|
|
|
|
|
|
## VPN (Direct WireGuard)
|
|
|
|
|
- **Server**: nginx-reverse, `10.10.10.1/24` (wg0), ListenPort 51820
|
|
|
|
|
- **Public endpoint**: `104.52.199.76:51820` (MikroTik dstnat → `192.168.128.19:51820`)
|
|
|
|
|
- **Config**: `/etc/wireguard/wg0.conf` on nginx-reverse, `wg-quick@wg0` enabled
|
|
|
|
|
- **Split tunnel**: Clients route `192.168.128.0/24`, `192.168.88.0/24`, `10.10.10.0/24` through VPN
|
|
|
|
|
- **DNS**: `192.168.128.3` (Pi-hole primary), `192.168.128.2` (Pi-hole replica) — no public fallback
|
|
|
|
|
- **Clients**: iPhone at `10.10.10.2/32`, MikroTik travel router at `10.10.10.3/32`, Windows laptop at `10.10.10.4/32`, Linux client at `10.10.10.5/32`
|
|
|
|
|
- **Routing**: No masquerade — MikroTik has static route `10.10.10.0/24 via 192.168.128.19` so LAN devices can reach VPN clients directly; bidirectional FORWARD rules; MSS clamping for TCP on both enp1s0 and wg0
|
|
|
|
|
- **MikroTik notrack**: Raw prerouting rule `dst-address=10.10.10.0/24 action=notrack` — required because nginx-reverse delivers VPN→LAN packets directly on L2 (same subnet), creating asymmetric routing; without notrack, MikroTik conntrack marks reply packets as invalid and drops them intermittently
|
|
|
|
|
- **sdanywhere.com**: VPN services disabled (2026-02-22), still active as web server (64.227.104.26, Debian 12, login `root`)
|
|
|
|
|
|
|
|
|
|
## ntfy (docker2)
|
|
|
|
|
- **VM**: on host `compute1` (migrated from console, 2026-02-22)
|
|
|
|
|
- **Disk**: `/1TB/vm/docker2.qcow2` on compute1
|
|
|
|
|
- **IP**: `192.168.128.8`
|
|
|
|
|
- **Container**: `binwiederhier/ntfy` on docker2, port 8080; uses `docker compose` v2
|
|
|
|
|
- **Deploy**: `ssh docker2 'cd ~/snapback && docker compose down && docker compose up -d'`
|
|
|
|
|
- **URL**: `https://ntfy.sdanywhere.com`, auth deny-all, admin user `kamaji`
|
|
|
|
|
- **Config**: `docker2:~/snapback/ntfy/server.yml`, upstream relay to ntfy.sh for mobile push
|
|
|
|
|
- **Topic**: `backups` (requires auth)
|
|
|
|
|
- Autostart enabled on compute1
|
|
|
|
|
|
|
|
|
|
## NextSnap File Locations
|
|
|
|
|
See [nextsnap-files.md](nextsnap-files.md)
|
|
|
|
|
|
|
|
|
|
## Conversations
|
|
|
|
|
See [conversations.md](conversations.md)
|