Sentinel VPS
External Hetzner VPS — RustDesk relay, WireGuard exit node, monitoring, proxy, DNS failover
Sentinel VPS — External Infrastructure
Provisioned: 2026-02-25 Provider: Hetzner Cloud Status: Active Cost: $4.99/mo (cpx11)
Server Details
| Property | Value |
|---|---|
| Name | sentinel |
| IPv4 | 178.156.247.186 |
| IPv6 | 2a01:4ff:f4:af22::/64 |
| DNS | sentinel.Arcturus-Prime.com |
| Tailscale IP | 100.107.51.11 |
| Type | cpx11 (2 vCPU / 2GB RAM / 40GB NVMe) |
| Location | Ashburn, VA (us-east) |
| OS | Debian 12 (Bookworm) |
| SSH | ssh [email protected] (key auth only) |
| Hetzner Server ID | 122123484 |
| Hetzner Firewall ID | 10594517 |
| Hetzner SSH Key ID | 108160348 |
How to Use Each Service
SSH Access
ssh [email protected] # direct (fastest)
ssh [email protected] # via Tailscale (only if direct fails)
Only key-based auth. The daniel-callisto SSH key is the only authorized key.
Uptime Kuma — External Monitoring
URL: https://sentinel.Arcturus-Prime.com
Login: daniel / [REDACTED]!
Admin link: /admin/homelab → Sentinel VPS → Uptime Kuma
9 monitors watching from outside the network:
| Monitor | Type | Interval | What it catches |
|---|---|---|---|
| Arcturus-Prime (Arcturus-Prime.com) | HTTP | 60s | CF Pages outage, DNS failure |
| Gitea (git.Arcturus-Prime.com) | HTTP | 120s | Tunnel down, Gitea crash |
| Meridian-Host Admin (mm.Arcturus-Prime.com) | HTTP | 120s | Tunnel/container down |
| Argonaut Daemon (daemon.Arcturus-Prime.com) | HTTP | 120s | Daemon crash, tunnel down |
| OpenClaw (oc.Arcturus-Prime.com) | HTTP | 120s | Gateway crash, tunnel down |
| Sentinel SSL | HTTP | 300s | SSL cert expiry, nginx crash |
| RustDesk (hbbs) | Port 21116 | 120s | RustDesk service down |
| WireGuard | Port 51820 | 300s | WireGuard service down |
| Squid Proxy | Port 3128 | 300s | Squid service down |
Why this matters: The Altair-Link Kuma (35 monitors) watches from inside the LAN. It can’t detect Cloudflare tunnel failures or DNS issues because it reaches services directly. Sentinel watches from the public internet — same perspective as your users.
WireGuard VPN — Pentest Traffic Exit
Routes all Kali VM traffic through the VPS public IP so your home ISP never sees scan traffic.
# On Kali VM (ssh [email protected])
sudo wg-quick up wg0 # start tunnel
curl https://ifconfig.me # verify: should show 178.156.247.186
# ... run scans ...
sudo wg-quick down wg0 # stop tunnel, revert to home IP
- Server subnet: 10.66.0.1/24 (VPS)
- Client: 10.66.0.2/24 (Kali)
- NAT masquerade on eth0 — outbound traffic exits through VPS public IP
- DNS: 1.1.1.1 (Cloudflare) through the tunnel
- Server config:
/etc/wireguard/wg0.confon VPS - Client config:
/etc/wireguard/wg0.confon pentest-tarn (192.168.20.229)
RustDesk Relay
All RustDesk clients should use these settings:
| Setting | Value |
|---|---|
| ID Server | rustdesk.Arcturus-Prime.com |
| Relay Server | rustdesk.Arcturus-Prime.com |
| Public Key | vXsOYr3dSisS0Nw6JIoEDJOdF2t8DM0QshH4cQHODTw= |
Key sync: Both the VPS and Altair-Link use the same ed25519 keypair (original Altair-Link key). Clients don’t need reconfiguration when switching servers.
DNS switch: Managed via /admin/homelab → RustDesk DNS panel. One-click toggle between VPS and home. Updates both rustdesk.Arcturus-Prime.com and rustdesk-udp.Arcturus-Prime.com A records via Cloudflare API.
- Binaries:
/opt/rustdesk/hbbs,/opt/rustdesk/hbbr - hbbs flags:
-r rustdesk.Arcturus-Prime.com(tells clients where the relay is) - Ports: 21115 (NAT), 21116 (TCP+UDP ID), 21117 (relay)
- Services:
systemctl status rustdesk-hbbs rustdesk-hbbr
Squid Proxy — Job Scraping
http://daniel:[email protected]:3128
For Applyr/JobApplyr to bypass LinkedIn/Indeed/Glassdoor IP bans on BrightData residential proxies. The Hetzner datacenter IP is clean — not flagged by job boards.
Test:
curl -x http://daniel:[email protected]:3128 https://ifconfig.me
# Should return: 178.156.247.186
Already configured in:
~/Development/JobSpyAdvanced-Lab/proxies.txt~/Development/JobSpyAdvanced-Lab/config/job_search_config.py→vps_proxy- Routes LinkedIn, Google, Glassdoor through VPS; Indeed stays on BrightData
Config: /etc/squid/squid.conf (auth required, forwarded-for headers stripped)
Credentials: /etc/squid/proxy_users
Tailscale — Mesh Networking
- VPS Tailscale IP: 100.107.51.11
- Status: Active, accepting subnet routes from Altair-Link
- Direct connection: Via UDP 41641 (~55ms to Capella-Outpost)
- Reach homelab from VPS:
ssh [email protected] "ping 10.42.0.100"works via subnet routing
Scaling — Adjust Resources
Resize via Hetzner API or console (requires reboot, ~30 seconds):
| Type | Spec | Monthly |
|---|---|---|
| cpx11 | 2c/2GB/40GB | $4.99 (current) |
| cpx21 | 3c/4GB/80GB | $9.99 |
| cpx31 | 4c/8GB/160GB | $17.99 |
# Scale up
curl -X POST -H "Authorization: Bearer $HETZNER_API_TOKEN" \
"https://api.hetzner.cloud/v1/servers/122123484/actions/change_type" \
-d '{"server_type":"cpx21","upgrade_disk":true}'
# WARNING: "upgrade_disk":true is irreversible — can't shrink disk after.
# Use "upgrade_disk":false if you want to scale back down later.
Automatic Services (No Action Needed)
| Service | What It Does |
|---|---|
| fail2ban | Bans IPs after 3 failed SSH attempts for 1 hour |
| unattended-upgrades | Auto-installs Debian security patches |
| Let’s Encrypt | Auto-renews SSL cert for sentinel.Arcturus-Prime.com (expires 2026-05-26) |
| Origin leak check | Every 6h — scans DNS for accidental home IP exposure |
| CF Access check | Daily 03:30 — verifies /admin/* is protected |
Firewall Rules (Hetzner)
| Port | Protocol | Description |
|---|---|---|
| 22 | TCP | SSH |
| 80 | TCP | HTTP (ACME/redirect) |
| 443 | TCP | HTTPS (Uptime Kuma via nginx) |
| 3128 | TCP | Squid proxy |
| 21115-21117 | TCP | RustDesk TCP |
| 21116 | UDP | RustDesk UDP |
| 41234 | TCP | Tailscale DERP |
| 41641 | UDP | Tailscale Direct |
| 51820 | UDP | WireGuard VPN |
DNS Records
| Record | Type | Value | Proxied |
|---|---|---|---|
| sentinel.Arcturus-Prime.com | A | 178.156.247.186 | No |
| rustdesk.Arcturus-Prime.com | A | 178.156.247.186 | No |
| rustdesk-udp.Arcturus-Prime.com | A | 178.156.247.186 | No |
Deleted records (security):
direct.Arcturus-Prime.com— Was exposing home IP. Deleted.rustdesk-relay.Arcturus-Prime.com— CNAME to CF tunnel, useless for raw TCP. Deleted.
RustDesk DNS Switch
Architecture
rustdesk.Arcturus-Prime.com ──── managed via /admin/homelab
│
┌────┴────┐
▼ ▼
┌─────────┐ ┌──────────────┐
│ VPS │ │ Altair-Link │
│ Primary │ │ Fallback │
│ :21115 │ │ :21116 │
└─────────┘ └──────────────┘
Switched via: /admin/homelab → RustDesk DNS panel (one-click toggle)
API: POST /api/admin/rustdesk-dns — updates both rustdesk.Arcturus-Prime.com and rustdesk-udp.Arcturus-Prime.com A records via Cloudflare Global API
Shared key: Both servers use the same ed25519 keypair — no client reconfiguration needed
Old cron-based updater on Altair-Link has been disabled (2026-02-26).
CF Pages env vars required: CF_EMAIL, CF_GLOBAL_API_KEY
Pentest Daemon — External Scan Node
Deployed: 2026-02-25 Status: Active (default pentest node)
The VPS runs a lightweight pentest-daemon instance (same FastAPI codebase as the Tarn-Host/Izar-Host Kali VMs) focused on external reconnaissance. It’s the default node in the pentest suite — scans run from the VPS’s clean public IP, with automatic failover to Tarn-Host → Izar-Host if offline.
Web Terminal (ttyd)
| Property | Value |
|---|---|
| URL | https://sentinel.Arcturus-Prime.com/terminal/ |
| Daemon bind | 127.0.0.1:7681 (NOT publicly exposed) |
| Nginx proxy | /terminal/ location block |
| Auth | Basic auth — daniel / e40fd605779e947c66037a02d64dddc1 |
| Service | systemctl status ttyd |
| Max clients | 2 |
| Shell | /bin/bash (root) |
Embeddable from /admin/homelab — double-click the Terminal card under Sentinel VPS.
Why External Matters
Scanning from inside the LAN gives a different view than scanning from the public internet. Internal scans bypass Cloudflare, DNS resolution, and firewall rules. The VPS scans from the same perspective as an attacker — Cloudflare-fronted, public DNS, no LAN shortcuts.
Access
| Property | Value |
|---|---|
| Production URL | https://sentinel.Arcturus-Prime.com/pentest-api |
| Daemon bind | 127.0.0.1:8095 (NOT publicly exposed) |
| Nginx proxy | /pentest-api/ location block |
| Service | systemctl status pentest-daemon |
| Max concurrent scans | 2 (2GB RAM constraint) |
Installed Tools (Recon Subset)
| Installed | Skipped (use Tarn-Host/Izar-Host) |
|---|---|
| nmap, nikto, nuclei, subfinder | sqlmap, hydra, wfuzz, dirb |
| testssl.sh, sslscan, dnsrecon | amass (RAM-heavy), zaproxy (JVM) |
| wafw00f, whatweb, whois, dig | ffuf, gobuster, wpscan |
Testing
# Health check
curl -s https://sentinel.Arcturus-Prime.com/pentest-api/api/health
# Service status
ssh [email protected] "systemctl status pentest-daemon"
Key Files
| Path | Purpose |
|---|---|
/opt/pentest-daemon/ | Daemon code + Python venv |
/opt/pentest-daemon/.env | API key, host, port config |
/etc/systemd/system/pentest-daemon.service | systemd unit |
/var/lib/pentest-daemon/scans/ | Scan output files |
Key Files on VPS
| Path | Purpose |
|---|---|
/etc/wireguard/wg0.conf | WireGuard server config |
/etc/nginx/sites-available/uptime-kuma | Nginx reverse proxy (includes /pentest-api/) |
/etc/letsencrypt/live/sentinel.Arcturus-Prime.com/ | SSL certificates |
/etc/squid/squid.conf | Squid proxy config |
/etc/squid/proxy_users | Squid auth credentials (htpasswd) |
/opt/rustdesk/ | RustDesk binaries + keys |
/opt/uptime-kuma/ | Uptime Kuma installation + SQLite DB |
/opt/pentest-daemon/ | Pentest daemon + venv |
/opt/monitoring/origin-leak-check.sh | DNS origin leak scanner |
/opt/monitoring/cf-access-check.sh | CF Access protection validator |
/etc/fail2ban/jail.d/sshd.conf | fail2ban SSH jail config |
/etc/systemd/system/pentest-daemon.service | Pentest daemon systemd unit |
/etc/systemd/system/ttyd.service | ttyd web terminal unit |
/etc/systemd/system/rustdesk-hbbs.service | RustDesk ID/rendezvous server |
/etc/systemd/system/rustdesk-hbbr.service | RustDesk relay server |
Security Hardening
- SSH: Key auth only (
PasswordAuthentication no,PermitRootLogin prohibit-password) - fail2ban: 3 retries / 1 hour ban / systemd backend
- Automatic security updates via unattended-upgrades
- Hetzner firewall: Only required ports open (no ICMP)
- Squid proxy: Auth-required, no anonymous access, forwarded-for stripped
- Let’s Encrypt SSL with auto-renewal
WireGuard Configs
Server (/etc/wireguard/wg0.conf on VPS)
[Interface]
Address = 10.66.0.1/24
ListenPort = 51820
PrivateKey = <server-private-key>
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; sysctl -w net.ipv4.ip_forward=1
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <kali-public-key>
PresharedKey = <preshared-key>
AllowedIPs = 10.66.0.2/32
Client (/etc/wireguard/wg0.conf on pentest-tarn)
[Interface]
Address = 10.66.0.2/24
PrivateKey = OMfxVN3C+vc66lH4/pQ/0D9J5+EQk+dHx6HONQ1md0E=
DNS = 1.1.1.1
[Peer]
PublicKey = ZwjfdS0Ie1c3HMxvwmObEpC0eEb96WcwjUSFuCYLZTs=
PresharedKey = V0Tbt/SW+YHaUYfZNIdlzbwvnZ2OttuHJe6JrmJRyxk=
Endpoint = 178.156.247.186:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
Testing — Quick Smoke Test
Run from Capella-Outpost:
echo "=== SSH ===" && ssh -o ConnectTimeout=5 [email protected] "echo OK" && echo "PASS" || echo "FAIL"
echo "=== HTTPS/SSL ===" && curl -sf https://sentinel.Arcturus-Prime.com -o /dev/null && echo "PASS" || echo "FAIL"
echo "=== RustDesk DNS ===" && dig +short rustdesk.Arcturus-Prime.com | grep -q "178.156.247.186" && echo "PASS" || echo "FAIL"
echo "=== Squid Proxy ===" && curl -sf -x http://daniel:[email protected]:3128 https://ifconfig.me | grep -q "178.156.247.186" && echo "PASS" || echo "FAIL"
echo "=== Tailscale ===" && tailscale ping --c 1 100.107.51.11 > /dev/null 2>&1 && echo "PASS" || echo "FAIL"
echo "=== WireGuard ===" && ssh -o ConnectTimeout=5 [email protected] "ss -ulnp | grep -q 51820" && echo "PASS" || echo "FAIL"
echo "=== fail2ban ===" && ssh -o ConnectTimeout=5 [email protected] "fail2ban-client status sshd > /dev/null 2>&1" && echo "PASS" || echo "FAIL"
echo "=== Uptime Kuma ===" && ssh -o ConnectTimeout=5 [email protected] "ss -tlnp | grep -q 3001" && echo "PASS" || echo "FAIL"
echo "=== Pentest Daemon ===" && curl -sf https://sentinel.Arcturus-Prime.com/pentest-api/api/health -o /dev/null && echo "PASS" || echo "FAIL"
echo "=== ttyd Terminal ===" && curl -sf -o /dev/null -w '%{http_code}' https://sentinel.Arcturus-Prime.com/terminal/ | grep -q "401" && echo "PASS (auth required)" || echo "FAIL"
Individual Tests
WireGuard end-to-end: SSH to Kali (ssh [email protected]), sudo wg-quick up wg0, curl https://ifconfig.me → should show 178.156.247.186.
Squid proxy auth: curl -x http://178.156.247.186:3128 https://ifconfig.me → should return 407 (auth required).
Tailscale direct: tailscale ping --c 1 100.107.51.11 → should show via 178.156.247.186:41641 (direct, not DERP relay).
SSL cert expiry: curl -vI https://sentinel.Arcturus-Prime.com 2>&1 | grep expire → May 26 2026.
Monitoring crons: ssh [email protected] "crontab -l" and tail /var/log/origin-leak.log.
Two Uptime Kumas — Why Both
| Altair-Link Kuma (port 3003) | Sentinel Kuma (VPS) | |
|---|---|---|
| Perspective | Inside the LAN | Public internet |
| Monitors | 35 (infra, services, media) | 9 (public endpoints + VPS self-check) |
| Detects | Container/service crashes | Tunnel failures, DNS, CF outages |
| Feeds | /status/ page on Arcturus-Prime.com | sentinel.Arcturus-Prime.com dashboard |
| History | 22+ days (788K heartbeats) | Started 2026-02-25 |
| Data also used by | kuma-history API (port 3009) → /status/ page 7-day view | Admin-only dashboard |
Not redundant — complementary perspectives. Internal Kuma can’t detect tunnel failures. External Kuma can’t see internal services.
kuma-history Integration
The kuma-history container on Altair-Link (port 3009) reads from Altair-Link Kuma’s SQLite DB and serves aggregated hourly/daily/per-service data. Tunnel exposes it at status.Arcturus-Prime.com/api/history/*.
Wired into the Arcturus-Prime /status/ page as of 2026-02-25:
ContributionGrid.astroseeds 5-min localStorage slots from hourly server dataWeekView.astroseeds 7-day hourly grid + per-service bars from server data- API proxy:
/api/kuma-history/[...path].ts - Solves the “status page forgets history” problem — no longer depends solely on browser localStorage
Pending Tasks
- Replace cron-based failover with admin panel DNS switch (2026-02-26)
- Set up pentest daemon on VPS as external scan node (2026-02-25)
- Fix RustDesk hbbs relay flag (
-r rustdesk.Arcturus-Prime.com) (2026-02-26) - Install ttyd web terminal, proxy via nginx
/terminal/(2026-02-26) - Add Terminal + Pentest Daemon service cards to homelab page (2026-02-26)
- Update all RustDesk clients to use VPS public key
- Set up backup receiver (rsync from Gitea, D1 exports)
Last updated: 2026-02-26