No description
Find a file
Alvin 389c355709
Some checks failed
CI/CD / Go tests (push) Has been cancelled
CI/CD / Dashboard build (push) Has been cancelled
CI/CD / Deploy production (push) Has been cancelled
feat: enforce tenant isolation and strict Reality profiles
2026-08-30 18:14:58 +07:00
.forgejo/workflows feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
.omp/skills/vless-overlay-dev SKill created for this project 2026-07-29 16:32:08 +07:00
.wiki Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
authentik feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
cmd feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
config feat: persist GUI-managed server settings 2026-08-04 15:03:06 +07:00
dashboard@9f9ed49abc feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
deploy feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
docs/adr feat: Management API server — bridges dashboard to VLESS backend 2026-07-23 12:03:03 +07:00
mgmt feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
protocol feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
raw/sources feat: add authenticated device enrollment 2026-07-29 18:45:45 +07:00
tools feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
vless-client@ea2ce5d0aa feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
wiki docs: record all-interfaces origin observation 2026-08-09 13:44:38 +07:00
wintun Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
xray feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
.env.example feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
.gitignore feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
.gitmodules add submodule config for dashboard and vless-client 2026-07-24 11:44:31 +07:00
_filter.py fix: add missing strings import, PnP cleanup + retry 2026-07-24 19:00:53 +07:00
client.bat feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
client.ps1 feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
client.sh feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
config.go fix: comprehensive logging throughout TUN creation, connect, disconnect 2026-07-24 14:59:35 +07:00
CONTEXT.md feat: add authenticated device enrollment 2026-07-29 18:45:45 +07:00
device.go fix: code review findings — P0/P1 spec bugs + standards smells 2026-07-22 13:53:30 +07:00
device_windows.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
filter.go fix: code review findings — P0/P1 spec bugs + standards smells 2026-07-22 13:53:30 +07:00
filter_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
firewall.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
firewall_test.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
go.mod fix: use VLESS-branded WinTUN adapter with deterministic per-machine GUID 2026-07-24 13:59:22 +07:00
go.sum fix: use VLESS-branded WinTUN adapter with deterministic per-machine GUID 2026-07-24 13:59:22 +07:00
index.md fix(dashboard): allow remote dev HMR 2026-07-29 13:28:59 +07:00
integration_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
ip.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
ip_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
log.md fix(dashboard): allow remote dev HMR 2026-07-29 13:28:59 +07:00
mesh.go fix(mesh): keep idle peers connected 2026-07-29 11:28:41 +07:00
nat.go fix: drop multicast/broadcast packets at TUN layer 2026-07-23 15:18:09 +07:00
nat_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
packet.go feat: mesh VPN relay, firewall engine, server NAT, and dashboard firewall UI 2026-07-24 12:30:14 +07:00
packet_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
README.md feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
router.go fix: code review findings — P0/P1 spec bugs + standards smells 2026-07-22 13:53:30 +07:00
router_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
server.bat feat: expose dashboard on all interfaces by default; accept any interface origin 2026-08-09 13:43:31 +07:00
server.sh feat: expose dashboard on all interfaces by default; accept any interface origin 2026-08-09 13:43:31 +07:00
server_nat.go feat: enforce tenant isolation and strict Reality profiles 2026-08-30 18:14:58 +07:00
tcpflow.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
tcpflow_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
transport.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
transport_test.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
udp.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
udp_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
vless-client.exe stuff 2026-07-29 11:28:41 +07:00
vless.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
vless_combined.exe fix: add missing strings import, PnP cleanup + retry 2026-07-24 19:00:53 +07:00
vless_combined.exe~ fix: add missing strings import, PnP cleanup + retry 2026-07-24 19:00:53 +07:00
vless_conn.go fix: code review findings — P0/P1 spec bugs + standards smells 2026-07-22 13:53:30 +07:00
vless_conn_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
vless_mgmt.exe chore: update build artifacts and IPAM state 2026-07-24 14:23:56 +07:00
vless_server.go fix: re-review — race condition, silent writes, config wiring 2026-07-22 14:20:22 +07:00
vless_server_test.go Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
vless_test.go fix: harden mesh relay and control plane 2026-07-29 16:09:05 +07:00
vless_tun.exe feat: persistent sticky IPAM with collision detection and tests 2026-07-24 13:44:00 +07:00
vlessd.exe~ fix: add missing strings import, PnP cleanup + retry 2026-07-24 19:00:53 +07:00
wintun.dll Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00
wintun.zip Initial commit: vless_tun — full VLESS TUN stack (9 phases, 82 tests) 2026-07-22 13:43:09 +07:00

VLESS Overlay Network

A Tailscale/Netbird competitor using VLESS as the data plane instead of WireGuard.

Architecture

                    ┌─────────────┐
                    │  Dashboard  │  Next.js 14 (:3000)
                    │  (Next.js)  │
                    └──────┬──────┘
                           │ REST API
                    ┌──────┴──────┐
                    │  Management  │  Go API (:8080)
                    │     API      │
                    └──────┬──────┘
                           │
              ┌────────────┴────────────┐
              │    VLESS Proxy (:8443)   │  Go, tenant-scoped
              └────────────┬────────────┘
                           │
         ┌─────────────────┼─────────────────┐
         │                 │                 │
    ┌────┴────┐      ┌────┴────┐      ┌────┴────┐
    │ vlessd  │      │ vlessd  │      │ vlessd  │   Daemon (Linux/macOS/Windows)
    │ :9070   │      │ :9070   │      │ :9070   │
    └────┬────┘      └────┬────┘      └────┬────┘
         │                 │                 │
    ┌────┴────┐      ┌────┴────┐      ┌────┴────┐
    │   CLI   │      │   CLI   │      │Wails GUI│   Clients
    └─────────┘      └─────────┘      └─────────┘

Components

Component Directory Role
VLESS Proxy cmd/vless_combined/ Handshake + TCP forwarding over VLESS
Management API mgmt/ Tenants, inbounds, connections, JWT auth
Dashboard dashboard/ Next.js 14 UI for server management
vlessd (daemon) cmd/vlessd/ VLESS conn + WinTUN + data plane + local API
vless_cli cmd/vless_cli/ Thin CLI → daemon API
Wails Client vless-client/ Windows GUI → daemon API
VLESS Protocol vless_tun/ Wire protocol, NAT, packet parsing, tests

Data Flow

OS traffic → WinTUN → raw IP → ParseIPPacket → FlowNAT → VLESS → Proxy → Dest
                                                                           ↓
OS ← WinTUN ← BuildIPv4 ← NAT tunWrite ← TCPFlow ← VLESS ← Proxy response

Multicast/broadcast (mDNS, SSDP, LLMNR) is dropped at the TUN layer.

Production CI/CD

Production deployment is handled by Forgejo Actions rather than by manually starting server.sh or relying on a one-time git pull. Pull requests run the Go tests and dashboard production build/typecheck. Successful pushes to main deploy the exact commit on the vless-production runner, restart the systemd services, check API and dashboard health, and roll back automatically if deployment fails. Host bootstrap and manual rollback instructions are in deploy/README.md.

Quick Start

Server

# Test server — on the first run, server.bat creates stable, ignored test
# credentials and a self-signed TLS certificate in .vless-test/.
server.bat

It prints local test credentials for exercising the relay and management processes. The one-URL Wails/CLI flow requires a configured public HTTPS dashboard origin and a management-provided relay profile; the old TLS-only/localhost test bundle is not a supported client deployment.

To create a portable local-test profile for another machine:

server.bat init-client laptop-b

This creates .vless-test\clients\laptop-b\ for lower-level relay tests. The bundle is not a substitute for the public dashboard URL required by clients; keep its setup key and certificate private.

# Production example — use a real domain certificate. Device credentials are
# issued after setup-key enrollment, not placed in an environment allowlist.
$env:VLESS_ADMIN_PASSWORD = "use-a-unique-12-plus-character-password"
$env:VLESS_ADMIN_EMAIL = "admin@example.com"
$env:VLESS_JWT_SECRET = "use-a-unique-random-32-plus-character-secret"
$env:VLESS_TLS_CERT_FILE = "C:\path\to\fullchain.pem"
$env:VLESS_TLS_KEY_FILE = "C:\path\to\privkey.pem"
# Build and run behind the public HTTPS dashboard origin (private API :9080 + TLS VLESS :8443)
server.bat

Log in as admin@example.com with VLESS_ADMIN_PASSWORD (set VLESS_ADMIN_EMAIL to use another email address), open Devices & keys, and create a setup key. The dashboard shows the setup key exactly once. Clients enter only the public dashboard URL and choose Browser approval or Setup key. Setup-key enrollment exchanges the key once over the dashboard HTTPS endpoint; management returns the device credential and public relay profile.

Setup keys default to one use and expire after 24 hours. A device authorization defaults to 30 days; disable or extend it from Devices & keys. Once it expires or is revoked, the relay closes the active connection and the client must authenticate again: desktop clients use browser approval, while CLI and automation clients use a valid setup key. For Ansible, create a reusable key with a maximum-use limit (or 0 for unlimited) and a suitable setup-key expiry; previously enrolled devices do not depend on that setup key remaining valid.

Set VLESS_TLS_SERVER_NAME only for the relay certificate when required; it is not a client-facing dashboard URL.

Reality relay (optional)

The relay uses a pinned Xray sidecar for VLESS + RAW + REALITY. The Windows client launchers download and checksum-verify the pinned Xray release, then bundle xray.exe beside the client binaries. Linux deployments should install the same pinned release beside vlessd; set the Reality variables in .env from .env.example. Keep the server private key only on the relay; clients receive the public profile through management enrollment. Generate the private key with xray x25519 and keep it only on the relay. The client-facing public key is derived from VLESS_REALITY_PRIVATE_KEY at relay startup, so there is no second public-key value to keep synchronized.

With the public HTTPS dashboard configured, a new client enters one dashboard URL and chooses Browser approval or Setup key. Browser approval opens the system browser and keeps a copyable approval URL in the same Wails window. The daemon persists the public profile returned by management and uses it as the authoritative relay target. A missing profile is a clear enrollment error, not a silent legacy relay fallback.

The Reality listener and the dashboard are separate trust boundaries. Keep the mesh gateway on its loopback address and do not expose the management API directly.

If a previously enrolled client reports an unsupported Reality fingerprint or an invalid public key, its stored profile is stale. Set the server's matching public values first, then re-enroll the device; do not replace the public key with an arbitrary value on the client.

The management API defaults to 127.0.0.1:9080. Expose the dashboard and API only through the configured HTTPS reverse proxy/domain; never give the private management listener or relay address to clients.

Cloudflare and Single-IP Front Door

For one public IP, Xray owns public TCP 443. It admits valid Reality connections and forwards ordinary TLS connections that fail Reality admission to an internal Caddy listener. Caddy then routes by hostname and path:

  1. reality.vless.tokoliza.org: DNS-only, handled by Xray Reality.
  2. relay.vless.tokoliza.org: proxied, forwarded by Caddy to the VLESS WSS listener.
  3. dashboard.vless.tokoliza.org: proxied, forwarded by Caddy to Next.js and /api/* to management.
  4. netbird.tokoliza.org: proxied or DNS-only, forwarded by Caddy to the NetBird LXC.
  5. firewall.tokoliza.org: optional, forwarded by Caddy to the private OPNsense WebGUI.

The internal flow is:

WAN:443 -> OPNsense NAT -> VLESS/Xray LXC:443
                                      |
                                      +-- valid Reality -> mesh gateway
                                      +-- ordinary TLS -> Caddy:8443
                                                           +-- NetBird LXC
                                                           +-- dashboard LXC
                                                           +-- WSS Xray inbound

The sample Caddy layout is in deploy/Caddyfile.cloudflare.example. Caddy must listen only on the OPNsense LAN address or a dedicated proxy LXC address; it must not be another public 443 listener. VLESS_REALITY_TARGET is the private Caddy address, for example 192.168.10.1:8443.

Use a publicly trusted certificate on Caddy because the Reality hostname is DNS-only. Cloudflare Full (strict) also accepts that certificate. Enable Network > WebSockets, keep caching disabled for /api/* and /vless/ws, and do not put Cloudflare Access in front of the relay path. If you expose the firewall hostname, put Cloudflare Access with MFA in front of that hostname.

The exact production values for vless.tokoliza.org are in deploy/vless.tokoliza.org.env.example. The Authentik callback is:

https://dashboard.vless.tokoliza.org/api/v1/auth/oidc/callback

The current client tries Reality first and WSS second. This preserves public port 443 for both transports without requiring a second public IP.

Public Dashboard URL And Migration

  1. Set VLESS_DASHBOARD_ORIGIN=https://dashboard.example.com/panel, NEXT_PUBLIC_BASE_PATH=/panel, and NEXT_PUBLIC_DASHBOARD_ORIGIN=https://dashboard.example.com/panel.
  2. Configure the reverse proxy to serve the dashboard at /panel, strip the /panel prefix, and forward /panel/api/* as /api/* to the private management listener. Set VLESS_TRUST_PROXY=true only when the proxy overwrites X-Forwarded-Host and X-Forwarded-Proto.
  3. Remove NEXT_PUBLIC_API_ORIGIN and any client-facing VLESS_MGMT_ADDR or relay URL. Keep VLESS_MGMT_ADDR=127.0.0.1:9080 as an operator-only bind address.
  4. Rebuild the dashboard and restart the server. Existing config retains its legacy server field for inspection, but new connections use the saved management URL and profile.
  5. Existing Wails local storage containing a relay URL is ignored. Enter only the public dashboard URL and choose an explicit auth mode.

Changing the dashboard URL clears the stored identity and profile before the selected mode reauthenticates. Setup keys are never persisted by Wails or the daemon.

Authentik OIDC SSO

The server supports native Authentik Authorization Code + PKCE login. Create a dedicated OAuth2/OpenID provider in the self-hosted Authentik instance running with this VLESS VM. VLESS should use that local instance's issuer, client ID, and client secret:

Copy-Item .env.example .env
# Edit .env with the TLS paths, dashboard URL, local Authentik issuer, client ID,
# client secret, and VLESS_OIDC_OPERATOR_EMAILS.
# server.bat loads it automatically.

Set VLESS_AUTH_MODE=oidc, NEXT_PUBLIC_AUTH_MODE=oidc, VLESS_OIDC_ISSUER, VLESS_OIDC_CLIENT_ID, VLESS_OIDC_CLIENT_SECRET, and VLESS_OIDC_REDIRECT_URL. The callback is normally https://dashboard.example.com/api/v1/auth/oidc/callback. VLESS discovers Authentik's authorization, token, userinfo, and logout endpoints from the issuer metadata, exchanges the authorization code with the client secret and PKCE verifier, reads the verified email from userinfo, and creates an HttpOnly VLESS session cookie.

The dashboard does not pass a callback URI parameter. VLESS infers the origin from the request and accepts a direct callback only for a currently assigned dashboard interface address on VLESS_DASHBOARD_PORT when interface discovery is enabled. Run the one-shot cmd/vless_authentik_provision binary with a narrowly scoped local Authentik API token to reconcile those strict callback and logout URIs automatically. The deployment script runs it when VLESS_AUTHENTIK_RECONCILE=true. Keep the token outside the daemon's environment. VLESS_OIDC_REDIRECT_URLS is only for fixed, non-interface origins that are intentionally managed separately.

The public reverse proxy still routes the dashboard to Next.js and /api/* to the management API; it does not perform the OIDC login. 0.0.0.0 remains a bind address only. The provisioner includes the public dashboard URL and the currently assigned direct interface addresses in the local Authentik provider. Use the public dashboard URL in the Wails client.

For the initial deployment, leave VLESS_OIDC_OPERATOR_EMAILS unset and set VLESS_OIDC_BOOTSTRAP_FIRST_USER=true in .env. The first identity accepted by Authentik becomes the persistent operator; later verified identities are persisted as user accounts rather than rejected. Operators can promote, demote, add, or remove assignments from Access control in the dashboard. The service protects the last persisted operator from removal or demotion. An explicit VLESS_OIDC_OPERATOR_EMAILS list gives those identities an operator role when they first sign in; it does not overwrite assignments that were already managed in the dashboard. Password login is disabled in native OIDC mode. The older VLESS_AUTH_MODE=oidc-proxy mode remains available for deployments that intentionally delegate OIDC to a trusted reverse proxy.

Users have My devices self-service rather than tenant or infrastructure access: they can create a one-use setup key for the default tenant, list and revoke only their own keys and devices, and renew one of their own device authorizations for up to 30 days. They cannot create reusable or permanent credentials, see another identitys records, manage roles, or alter network configuration.

Windows Client (GUI + TUN)

# Right-click → Run as Administrator (or double-click for auto-elevation)
client.bat production

Builds and launches: vlessd (daemon, :9070) + vless-client.exe (Wails GUI). Use client.bat test only for the checked-out .vless-test environment. The PowerShell launcher accepts the same explicit choice: .\client.ps1 -Mode production or .\client.ps1 -Mode test.

Linux/macOS Client (CLI only)

./client.sh <dashboard-url> <name> [setup-key]
# Example:
./client.sh https://dashboard.example.com/panel my-laptop vsk_...

Builds and launches: vlessd (daemon) + vless_cli connect.

CLI Commands

vless_cli connect -management-url https://dashboard.example.com/panel -auth-mode setup_key -name my-device -setup-key vsk_...
vless_cli connect -management-url https://dashboard.example.com/panel -auth-mode browser_approval -name my-device
vless_cli status
vless_cli disconnect

Project Structure

vless_tun/
├── cmd/
│   ├── vless_combined/   # Server (API + VLESS proxy)
│   ├── vlessd/           # Client daemon
│   ├── vless_cli/        # CLI client
│   ├── vless_mgmt/       # Standalone management server
│   └── vless_tun/        # Standalone TUN example
├── mgmt/                 # Management API package
├── dashboard/            # Next.js 14 dashboard
├── vless-client/         # Wails desktop app (submodule)
├── vless_tun/            # VLESS protocol + NAT + packet
│   ├── nat.go            # FlowNAT — per-flow VLESS connections
│   ├── tcpflow.go         # TCPFlow — per-TCP-flow state machine
│   ├── packet.go          # IP/TCP packet builder
│   ├── ip.go              # IP packet parser
│   ├── device.go          # Cross-platform TUN device
│   ├── device_windows.go  # WinTUN adapter (Windows only)
│   └── *_test.go          # 82 tests
├── server.bat            # Launch server
├── client.bat            # Launch Windows client (auto-admin)
├── client.ps1            # PowerShell client launcher
├── client.sh             # Linux/macOS client launcher
└── vlessd.log            # Daemon log file

IP Addressing

  • The shared default overlay pool is 10.100.0.0/24 and starts at 10.100.0.2.
  • Operators can configure an IPv4 IP range for a tenant through the admin tenant API; when configured, that tenant's devices are allocated addresses from its range.
  • Tenants without a configured range use the shared default pool.
  • Assigned on first VLESS connect (port 0 registration)

Tenant traffic is isolated server-side: cross-tenant overlay frames are dropped before forwarding. This applies to both TCP and UDP mesh forwarding; egress policy is unchanged.

Encoding Git Remote (SSO-gated)

git remote add origin https://github.com/org/repo.git
git push -u origin main
# Username: email, Password: PAT (Personal Access Token)

License

MIT