Skip to main content
Infrastructure

Sentinel VPS

External Hetzner VPS — RustDesk relay, WireGuard exit node, monitoring, proxy, DNS failover

February 25, 2026

Sentinel VPS — External Infrastructure

Provisioned: 2026-02-25 Provider: Hetzner Cloud Status: Active Cost: $4.99/mo (cpx11)


Server Details

PropertyValue
Namesentinel
IPv4178.156.247.186
IPv62a01:4ff:f4:af22::/64
DNSsentinel.Arcturus-Prime.com
Tailscale IP100.107.51.11
Typecpx11 (2 vCPU / 2GB RAM / 40GB NVMe)
LocationAshburn, VA (us-east)
OSDebian 12 (Bookworm)
SSHssh [email protected] (key auth only)
Hetzner Server ID122123484
Hetzner Firewall ID10594517
Hetzner SSH Key ID108160348

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:

MonitorTypeIntervalWhat it catches
Arcturus-Prime (Arcturus-Prime.com)HTTP60sCF Pages outage, DNS failure
Gitea (git.Arcturus-Prime.com)HTTP120sTunnel down, Gitea crash
Meridian-Host Admin (mm.Arcturus-Prime.com)HTTP120sTunnel/container down
Argonaut Daemon (daemon.Arcturus-Prime.com)HTTP120sDaemon crash, tunnel down
OpenClaw (oc.Arcturus-Prime.com)HTTP120sGateway crash, tunnel down
Sentinel SSLHTTP300sSSL cert expiry, nginx crash
RustDesk (hbbs)Port 21116120sRustDesk service down
WireGuardPort 51820300sWireGuard service down
Squid ProxyPort 3128300sSquid 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.conf on VPS
  • Client config: /etc/wireguard/wg0.conf on pentest-tarn (192.168.20.229)

RustDesk Relay

All RustDesk clients should use these settings:

SettingValue
ID Serverrustdesk.Arcturus-Prime.com
Relay Serverrustdesk.Arcturus-Prime.com
Public KeyvXsOYr3dSisS0Nw6JIoEDJOdF2t8DM0QshH4cQHODTw=

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.pyvps_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):

TypeSpecMonthly
cpx112c/2GB/40GB$4.99 (current)
cpx213c/4GB/80GB$9.99
cpx314c/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)

ServiceWhat It Does
fail2banBans IPs after 3 failed SSH attempts for 1 hour
unattended-upgradesAuto-installs Debian security patches
Let’s EncryptAuto-renews SSL cert for sentinel.Arcturus-Prime.com (expires 2026-05-26)
Origin leak checkEvery 6h — scans DNS for accidental home IP exposure
CF Access checkDaily 03:30 — verifies /admin/* is protected

Firewall Rules (Hetzner)

PortProtocolDescription
22TCPSSH
80TCPHTTP (ACME/redirect)
443TCPHTTPS (Uptime Kuma via nginx)
3128TCPSquid proxy
21115-21117TCPRustDesk TCP
21116UDPRustDesk UDP
41234TCPTailscale DERP
41641UDPTailscale Direct
51820UDPWireGuard VPN

DNS Records

RecordTypeValueProxied
sentinel.Arcturus-Prime.comA178.156.247.186No
rustdesk.Arcturus-Prime.comA178.156.247.186No
rustdesk-udp.Arcturus-Prime.comA178.156.247.186No

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)

PropertyValue
URLhttps://sentinel.Arcturus-Prime.com/terminal/
Daemon bind127.0.0.1:7681 (NOT publicly exposed)
Nginx proxy/terminal/ location block
AuthBasic auth — daniel / e40fd605779e947c66037a02d64dddc1
Servicesystemctl status ttyd
Max clients2
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

PropertyValue
Production URLhttps://sentinel.Arcturus-Prime.com/pentest-api
Daemon bind127.0.0.1:8095 (NOT publicly exposed)
Nginx proxy/pentest-api/ location block
Servicesystemctl status pentest-daemon
Max concurrent scans2 (2GB RAM constraint)

Installed Tools (Recon Subset)

InstalledSkipped (use Tarn-Host/Izar-Host)
nmap, nikto, nuclei, subfindersqlmap, hydra, wfuzz, dirb
testssl.sh, sslscan, dnsreconamass (RAM-heavy), zaproxy (JVM)
wafw00f, whatweb, whois, digffuf, 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

PathPurpose
/opt/pentest-daemon/Daemon code + Python venv
/opt/pentest-daemon/.envAPI key, host, port config
/etc/systemd/system/pentest-daemon.servicesystemd unit
/var/lib/pentest-daemon/scans/Scan output files

Key Files on VPS

PathPurpose
/etc/wireguard/wg0.confWireGuard server config
/etc/nginx/sites-available/uptime-kumaNginx reverse proxy (includes /pentest-api/)
/etc/letsencrypt/live/sentinel.Arcturus-Prime.com/SSL certificates
/etc/squid/squid.confSquid proxy config
/etc/squid/proxy_usersSquid 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.shDNS origin leak scanner
/opt/monitoring/cf-access-check.shCF Access protection validator
/etc/fail2ban/jail.d/sshd.conffail2ban SSH jail config
/etc/systemd/system/pentest-daemon.servicePentest daemon systemd unit
/etc/systemd/system/ttyd.servicettyd web terminal unit
/etc/systemd/system/rustdesk-hbbs.serviceRustDesk ID/rendezvous server
/etc/systemd/system/rustdesk-hbbr.serviceRustDesk 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)
PerspectiveInside the LANPublic internet
Monitors35 (infra, services, media)9 (public endpoints + VPS self-check)
DetectsContainer/service crashesTunnel failures, DNS, CF outages
Feeds/status/ page on Arcturus-Prime.comsentinel.Arcturus-Prime.com dashboard
History22+ days (788K heartbeats)Started 2026-02-25
Data also used bykuma-history API (port 3009) → /status/ page 7-day viewAdmin-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.astro seeds 5-min localStorage slots from hourly server data
  • WeekView.astro seeds 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

sentinelvpshetznerwireguardrustdeskmonitoringproxy