Skip to content

[RFC] (Implemented) Per-App Transparent Proxy #842

@PeterFuApps

Description

@PeterFuApps

Summary

Hi @ginuerzh @fernvenue ,

Proposal to add per-application transparent proxying via a new gost run subcommand. This routes traffic from specific applications through gost's proxy chain while leaving other system traffic unaffected.

gost run -F socks5://proxy:1080 -- firefox

I have already implemented this as a standalone Go wrapper with working Linux and Windows versions. If you're interested, I'd be happy to port it to gost v3 as a PR.

Why gost run Subcommand?

The existing -- separator in gost is used for multi-process spawning (cmd/gost/main.go:44-67):

# Existing: spawns two gost processes
gost -L socks5://:1111 -- -L rtcp://:3333/:1111 -F sshd://server:2222

A dedicated run subcommand avoids this conflict and provides cleaner semantics for per-app isolation.

Motivation

gost already has excellent TUN support for system-wide proxying. However, users often need to proxy only specific applications:

Use Case Current Solution Limitation
Proxy only browser proxychains Fails with static binaries (Linux)
Dev/test isolation Docker + gost Heavy, slow startup
Nested proxy chains Not possible TUN captures all traffic

Platform Support

Platform Status Implementation
Linux ✅ Implemented User namespaces + TUN + gVisor netstack
Windows ✅ Implemented WinDivert packet interception + gVisor netstack
macOS ❌ Not feasible Network Extension API barriers

Why Not macOS?

Requirement Issue
Apple Developer Account $99/year recurring cost (required even for local builds)
App notarization Must be signed by Apple
Language Swift/Obj-C required; no pure Go

Proposed Usage

Basic

# Linux (no root required)
gost run -F socks5://proxy:1080 -- firefox

# Windows (administrator required)
gost run -F socks5://proxy:1080 -- firefox.exe

With Different Protocols

# Relay protocol
gost run -F relay://server:8080 -- curl https://example.com

# Tunnel protocol
gost run -F tunnel://server:8080?tunnel.id=my-id -- wget https://example.com

Proxy Chain

gost run --chain -F socks5://proxy1:1080 -F relay://proxy2:8421 -- curl https://example.com

Unix Socket

# Works on both Linux and Windows (Windows 10 1803+ supports AF_UNIX)
gost run -F socks5+unix:///path/to/socks5.sock -- curl https://example.com

Config File

gost run -C run-config.yaml

run-config.yaml:

forwards:
  - socks5://proxy1:1080
  - socks5://proxy2:1080

command: ["firefox", "--private-window"]
uid: 1000   # Linux only
gid: 1000   # Linux only

net: 192.168.123.1/24
mtu: 1350

Implementation

Linux: User Namespaces

┌─────────────────────────────────────────────────────────────────────┐
│  gost run -F socks5://proxy:1080 -- firefox                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. Parse command after "--"                                        │
│                                                                     │
│  2. Create child process with new namespaces:                       │
│     └── unshare(CLONE_NEWUSER | CLONE_NEWNET)                      │
│                                                                     │
│  3. Child creates TUN device inside namespace                       │
│     └── Has root capabilities inside user namespace                │
│                                                                     │
│  4. Pass TUN fd to parent via Unix socket pair                      │
│                                                                     │
│  5. Parent attaches TUN to gVisor netstack                          │
│     └── Forward TCP/UDP through chain                              │
│                                                                     │
│  6. Child sets TUN as default route, execs application              │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

No root required — user namespaces are unprivileged.

Windows: WinDivert

┌─────────────────────────────────────────────────────────────────────┐
│  gost run -F socks5://proxy:1080 -- firefox.exe                      │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. Parse command after "--", launch application                    │
│                                                                     │
│  2. Track launched PID and child process tree                       │
│                                                                     │
│  3. WinDivert intercepts outbound packets by PID                    │
│     └── If packet's PID matches → forward through chain            │
│     └── Else → re-inject directly                                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

Requires administrator privileges. WinDivert is an external DLL loaded at runtime (no CGO), same pattern as gost's existing Wintun dependency. Just ship WinDivert.dll + WinDivert64.sys alongside the binary. Both are pre-signed.

Integration with gost

Reuse Existing Components

Component Reuse
gVisor netstack Already used by TUN handler (Linux)
Chain forwarding All connectors (socks5, relay, tunnel, http, etc.)
Config parsing YAML/JSON support

Dependencies

Component License Platform Notes
gVisor netstack Apache 2.0 Both Already in gost
WinDivert LGPL Windows External DLL, same pattern as Wintun
divert-go MIT Windows Go bindings, no CGO required for cross-compile

Nested Proxy Chains

gost run -F socks5://proxy1:1080 \
     -- gost run -F socks5://proxy2:1080 \
             -- firefox

Traffic: firefox → proxy2 → proxy1 → internet

How it works:

  • Linux: Each nested gost runs in its own network namespace
  • Windows: WinDivert priority system — inner gost uses higher priority, catches packets first, injects to outer gost

Unix Socket for Nested Mode (Linux)

TCP doesn't work across namespaces. Unix sockets can be bind-mounted:

gost run -F relay+unix:///tmp/gost.sock \
     -L relay+unix:///tmp/gost.sock \
     -- gost run -F relay+unix:///tmp/gost.sock \
             -- firefox

Use Cases

  1. Selective routing — Proxy only specific applications
  2. Development & testing — Route apps through different network paths
  3. Nested chains — Hierarchical routing

Questions

  1. Interest level? Would you be interested in per-app isolation via gost run?

  2. Implementation scope?

    • Linux first, Windows follow-up
    • Both together

Looking forward to your feedback! Happy to submit a PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions