Access internal corporate resources from your laptop by tunneling TCP connections through a remote machine's network position.
┌─────────── Laptop ─────────┐ ┌── Azure App Service ──┐ ┌──────── Win Machine ──────┐
│ │ │ │ │ │
│ Browser Proxy │ │ Relay │ │ Agent Internal │
│ kubectl ◄──► SOCKS5 :1080 │◄──►│ WebSocket Multiplexer │◄──►│ WebSocket ◄──► hosts │
│ CLI HTTP :3128 │ │ │ │ TCP tunnel │
│ │ │ │ │ │
└────────────────────────────┘ └───────────────────────┘ └───────────────────────────┘
- A deployed relay — you need a running relay instance and its URL. See Deploying the Relay.
- Azure CLI — install the Azure CLI and run
az loginbefore starting the agent or proxy. Tokens are acquired automatically at runtime. - Azure AD account — your account must belong to a tenant allowed by the relay (
NETBRIDGE_ALLOWED_TENANTS). - A Windows machine (VDI) — with access to the internal resources you want to reach. The agent will be installed on this machine.
Download netbridge.exe and run it on the Windows machine. A dialog will prompt for your relay URL. The agent installs itself, registers for Windows Startup, and connects automatically.
The tray icon shows connection status: green (connected), yellow (connecting), red (disconnected), orange (login required).
Requires Homebrew. If you don't have it:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Then install the proxy:
brew tap chrishham/tap
brew install netbridge-socksContinue with these instructions: homebrew-tap README.
Download netbridge-socks.exe and run it. On first launch a dialog will prompt for your relay hostname. The executable installs itself to %LOCALAPPDATA%\NetBridgeSocks\, registers for Windows Startup, and launches automatically.
The tray icon shows proxy status: green (connected, tunnel working end to end), purple ring (relay reached but the VDI agent is unreachable), yellow (connecting), red (disconnected), orange (login required). The proxy checks the full path to the VDI as soon as it connects, and keeps checking while the agent is missing, so the icon reflects whether traffic can actually flow — not just whether the relay accepted the connection. Right-click the tray icon for options:
- Check Connection Now — re-run the end-to-end check immediately
- Change Relay URL — switch to a different relay and restart
- Login (az login) — re-authenticate with Azure
- View Logs — open the current log file
- Uninstall — remove the app from your system
Configuration is stored in %LOCALAPPDATA%\NetBridgeSocks\config.json and logs in %LOCALAPPDATA%\NetBridgeSocks\logs\.
If your relay restricts destinations and rejects the default health-check target, set "probe_target": "host:port" in config.json to a destination the relay allows.
The proxy exposes two local endpoints:
- SOCKS5 on
localhost:1080 - HTTP on
localhost:3128
- Settings → Network Settings → Manual proxy
- SOCKS Host:
localhost, Port:1080, SOCKS v5 - Check "Proxy DNS when using SOCKS v5"
# in ~/.kube/config under cluster
clusters:
- cluster:
server: https://your-k8s-cluster.example.com:6443
proxy-url: socks5://localhost:1080# Bash/Zsh
export HTTP_PROXY="http://localhost:3128"
export HTTPS_PROXY="http://localhost:3128"
# git over HTTPS
git config --global http.proxy http://localhost:3128# Via SOCKS5 (socks5h resolves DNS through the proxy)
curl --proxy socks5h://localhost:1080 https://internal-api.example.com/health
# Via HTTP proxy
curl --proxy http://localhost:3128 https://internal-api.example.com/healthBy default the proxy binds to localhost only. To allow connections from other machines:
netbridge-socks --relay <URL> --host 0.0.0.0 --allow-remote --proxy-auth user:pass--allow-remote requires --proxy-auth to prevent open proxy abuse.
e2e/ drives the full client → proxy → relay → agent → target path. CI runs it from source on Linux for pushes to main and on pull requests, and against the installed Windows exes (e2e-windows.yml) on PRs and before every Windows release; releases publish exactly the exe that passed. See e2e/README.md.
The agent loads configuration from %LOCALAPPDATA%\NetBridge\config.json:
| Field | Description | Default |
|---|---|---|
relay_url |
Relay hostname or full WebSocket URL | — |
auto_connect |
Connect to relay on startup | true |
show_notifications |
Show desktop notifications | true |
log_level |
DEBUG, INFO, WARNING, ERROR |
"INFO" |
allow_private_destinations |
Allow connections to RFC 1918 private ranges (10/8, 172.16/12, 192.168/16) |
true |
allowed_destinations |
List of allowed CIDRs or hostnames (allowlist mode) | [] |
denied_destinations |
List of denied CIDRs or hostnames | [] |
Proxy settings are auto-detected from the system (PAC file / WinHTTP) per target URL. Loopback and link-local addresses are always blocked regardless of configuration. When allowed_destinations is set, only matching destinations are reachable. The deny list is checked first. These are agent-side filters, complementing the relay-side RELAY_ALLOWED_DESTINATIONS / RELAY_DENIED_DESTINATIONS.
The relay is configured entirely through environment variables.
Authentication:
| Variable | Description | Default |
|---|---|---|
NETBRIDGE_ALLOWED_TENANTS |
Comma-separated Azure AD tenant IDs (required) | — |
NETBRIDGE_ALLOWED_USERS |
Comma-separated allowed user emails | all users in tenant |
NETBRIDGE_ALLOWED_GROUPS |
Comma-separated allowed Azure AD group IDs | all groups |
NETBRIDGE_MAX_TOKEN_AGE_HOURS |
Reject tokens issued more than N hours ago | 0 (disabled) |
NETBRIDGE_ALLOW_NO_AUTH |
Set to true to permit --no-auth (loopback only) |
false |
Rate Limiting:
| Variable | Description | Default |
|---|---|---|
RELAY_RATE_CONNECTIONS_PER_MIN |
Max WebSocket connections per user per minute | 10 |
RELAY_RATE_MESSAGES_PER_SEC |
Max messages per user per second | 500 |
RELAY_RATE_STREAMS_PER_MIN |
Max new TCP streams per user per minute | 300 |
RELAY_RATE_IP_CONNECTIONS_PER_MIN |
Max connections per IP per minute (pre-auth) | 30 |
RELAY_MAX_ACTIVE_STREAMS |
Global maximum concurrent TCP streams | 500 |
RELAY_GLOBAL_BANDWIDTH_LIMIT_MBPS |
Global bandwidth cap in Mbps (0 = unlimited) |
0 |
Destination Filtering:
| Variable | Description | Default |
|---|---|---|
RELAY_BLOCKED_PORTS |
Comma-separated blocked TCP ports (e.g. 3389,22) |
— |
RELAY_DENIED_DESTINATIONS |
Comma-separated CIDRs / hostname globs to deny | — |
RELAY_ALLOWED_DESTINATIONS |
Comma-separated CIDRs / hostname globs to allow (allowlist mode) | — |
When RELAY_ALLOWED_DESTINATIONS is set only matching destinations are reachable. The deny list is checked first.
Timeouts:
| Variable | Description | Default |
|---|---|---|
RELAY_HEARTBEAT_INTERVAL |
WebSocket ping interval in seconds | 30 |
RELAY_STREAM_TIMEOUT |
Idle stream timeout in seconds | 120 |
RELAY_CLEANUP_INTERVAL |
Stale-stream sweep interval in seconds | 30 |
RELAY_MAX_MESSAGE_SIZE |
Maximum WebSocket message size in bytes | 1048576 (1 MB) |
Logging:
| Variable | Description | Default |
|---|---|---|
RELAY_LOG_FORMAT |
json for structured JSON output, text for human-readable |
text |
Run netbridge-socks --help for all CLI options. The following environment variables tune proxy behavior:
| Variable | Description | Default |
|---|---|---|
NETBRIDGE_TOKEN |
ARM access token (alternative to --token CLI flag) |
— |
NETBRIDGE_HTTP_MAX_BODY_BYTES |
Maximum HTTP proxy request body size in bytes | 67108864 (64 MB) |
These environment variables are shared between the agent and proxy for connection tuning:
| Variable | Description | Default |
|---|---|---|
NETBRIDGE_HEARTBEAT_INTERVAL |
WebSocket ping interval in seconds | 30 |
NETBRIDGE_WS_CONNECT_TIMEOUT |
WebSocket connection timeout in seconds | 30 |
NETBRIDGE_IDLE_STREAM_TIMEOUT |
Idle stream timeout in seconds | 120 |
NETBRIDGE_MAX_CONCURRENT_STREAMS |
Max concurrent TCP streams (proxy) | 200 |
NETBRIDGE_MAX_ACTIVE_STREAMS |
Max active TCP streams (agent) | 500 |
NETBRIDGE_CLEANUP_INTERVAL |
Stale-stream cleanup interval in seconds | 30 |
Agent: right-click the tray icon → Change Relay URL. The agent restarts automatically.
Proxy (macOS/Linux): see the homebrew-tap README for config file location.
Proxy (Windows): right-click the tray icon → Change Relay URL. The proxy restarts automatically.
- This is not a shell or RCE tool — it only tunnels TCP connections
- All traffic is encrypted (TLS) between each component and the relay
- No credentials are stored or transmitted — authentication uses Azure CLI tokens at runtime
- Both proxy servers bind to localhost only by default
- Authentication uses Azure AD ARM tokens (
az login) - When behind a TLS-intercepting proxy, prefer
--ca-bundle <file>(orNETBRIDGE_CA_BUNDLEenv var) to supply your corporate CA certificate instead of disabling verification entirely - SSL verification can be disabled (
--no-verify-ssl) as a last resort — this requires settingNETBRIDGE_ALLOW_INSECURE=1and weakens security
The relay runs as a Docker container and needs an HTTPS endpoint that supports WebSockets. Two common options:
Azure App Service (Container):
- Create a Web App for Containers in the Azure Portal
- Point it at the GHCR image:
ghcr.io/chrishham/netbridge-relay:latest - Under Settings → Configuration, add the required environment variables (at minimum
NETBRIDGE_ALLOWED_TENANTS) - Ensure WebSockets are enabled under Settings → Configuration → General settings
- The relay listens on port 8080 — set this as the container port
Self-hosted (k3s / Docker Compose / any container host):
- Pull the image:
docker pull ghcr.io/chrishham/netbridge-relay:latest - Run with the required environment variables:
docker run -d -p 8080:8080 \ -e NETBRIDGE_ALLOWED_TENANTS=<your-tenant-id> \ ghcr.io/chrishham/netbridge-relay:latest
- Place a reverse proxy (e.g. Traefik, Caddy, nginx) in front to terminate TLS and provide an HTTPS URL with WebSocket support
The relay exposes a /status endpoint you can use as a health check.
Each component is a separate Python package managed with uv:
cd <component>
# Run the component (use --native-tls in corporate environments)
uv run --native-tls <component-name>
# Run tests
uv run --native-tls pytestEach component is versioned and released independently:
# Release the agent (builds Windows .exe and creates a GitHub Release)
git tag agent-v0.5.0 && git push --tags
# Release the relay (builds and pushes Docker image to GHCR)
git tag relay-v1.0.0 && git push --tags
# Release the socks proxy (runs tests and updates Homebrew formula)
git tag socks-v0.2.0 && git push --tags
# Release the socks proxy Windows exe (builds .exe and creates a GitHub Release)
git tag socks-exe-v1.0.0 && git push --tags