mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-03 02:26:43 +09:00
Self-hosted tunneled reverse proxy: a service on a NAT'd host (e.g. a home Jellyfin box) gets a public HTTPS hostname with no port-forwarding, by pairing two agents over WireGuard. Implements Phases 0-3 of docs/REMOTE_ACCESS_ROADMAP.md. Agent (Go): - new internal/wireguard package: Manager interface + kernel backend (wg/ip) and a userspace netstack backend (wireguard-go + gVisor) so Windows/macOS peers need no driver/admin; in-process keygen returns only the public key (private keys never leave the host) - wireguard:keygen/interface/peer/status/forward/unforward + firewall :allow_port/:deny_port commands; wireguard + firewall capability probes - TCP forwarder (tnet.ListenTCP / net.Listen -> dial localhost) carries traffic to the real service behind a netstack peer Backend (Python): - Tunnel + ExposedService models; tunnel_netutil (subnet allocator, health); tunnel_broker_service (keygen->up->peer->verify, firewall open, reconcile-on- reconnect); tunnel_publish_service (forward -> nginx -> DNS -> auth -> cert) - nginx remote-upstream vhost (WebSocket + proxy_buffering off) - /api/v1/tunnels CRUD + /<id>/services publish/unpublish - panel-authoritative reconcile hooked into the agent gateway on reconnect Frontend (React): - Remote Access page (tunnels + exposed services, status, teardown) and an "Expose a Local Service" wizard; new services/api/tunnels.js Verified: go build (win/linux) + vet + go tests (forwarder + no-leak parsers); py_compile + pytest (netutil, reconcile, nginx template); create_app boot; eslint + npm run build. Live multi-host run pending. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
27 lines
1.0 KiB
Python
27 lines
1.0 KiB
Python
"""Phase 2 #12: the nginx remote-upstream vhost. Pure template/validation
|
|
checks — no nginx or filesystem needed."""
|
|
|
|
from app.services.nginx_service import NginxService
|
|
|
|
|
|
def test_remote_upstream_template_renders():
|
|
cfg = NginxService.REMOTE_UPSTREAM_TEMPLATE.format(
|
|
name='jellyfin.example.com',
|
|
domains='jellyfin.example.com',
|
|
upstream='10.88.3.2:8096',
|
|
)
|
|
assert 'proxy_pass http://10.88.3.2:8096;' in cfg
|
|
assert 'server_name jellyfin.example.com;' in cfg
|
|
# streaming + websocket essentials (Jellyfin)
|
|
assert 'proxy_buffering off;' in cfg
|
|
assert 'proxy_set_header Upgrade $http_upgrade;' in cfg
|
|
assert "proxy_set_header Connection 'upgrade';" in cfg
|
|
|
|
|
|
def test_create_site_remote_requires_upstream():
|
|
# The 'remote' branch rejects a missing upstream before touching the FS.
|
|
res = NginxService.create_site(
|
|
name='x', app_type='remote', domains=['x.example.com'], root_path='')
|
|
assert res.get('success') is False
|
|
assert 'upstream' in res.get('error', '').lower()
|