Skip to content

Commit d778eac

Browse files
Merge pull request #2 from shadowy-pycoder/transparent
Transparent proxy support
2 parents 929cc04 + 0bf50d1 commit d778eac

File tree

8 files changed

+659
-99
lines changed

8 files changed

+659
-99
lines changed

README.md

Lines changed: 166 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ Specify http server in proxy configuration of Postman
3434
- **Proxy Chain functionality**
3535
Supports `strict`, `dynamic`, `random`, `round_robin` chains of SOCKS5 proxy
3636

37+
- **Transparent proxy**
38+
Supports `redirect` (SO_ORIGINAL_DST) and `tproxy` (IP_TRANSPARENT) modes
39+
3740
- **DNS Leak Protection**
3841
DNS resolution occurs on SOCKS5 server side.
3942

@@ -65,7 +68,7 @@ You can download the binary for your platform from [Releases](https://github.com
6568
Example:
6669

6770
```shell
68-
HPTS_RELEASE=v1.5.0; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h
71+
HPTS_RELEASE=v1.6.0; wget -v https://github.com/shadowy-pycoder/go-http-proxy-to-socks/releases/download/$HPTS_RELEASE/gohpts-$HPTS_RELEASE-linux-amd64.tar.gz -O gohpts && tar xvzf gohpts && mv -f gohpts-$HPTS_RELEASE-linux-amd64 gohpts && ./gohpts -h
6972
```
7073

7174
Alternatively, you can install it using `go install` command (requires Go [1.24](https://go.dev/doc/install) or later):
@@ -102,27 +105,31 @@ GitHub: https://github.com/shadowy-pycoder/go-http-proxy-to-socks
102105
Usage: gohpts [OPTIONS]
103106
Options:
104107
-h Show this help message and exit.
108+
-M value
109+
Transparent proxy mode: [redirect tproxy]
110+
-T string
111+
Address of transparent proxy server (no HTTP)
105112
-U string
106-
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
113+
User for HTTP proxy (basic auth). This flag invokes prompt for password (not echoed to terminal)
107114
-c string
108-
Path to certificate PEM encoded file
109-
-d Show logs in DEBUG mode
115+
Path to certificate PEM encoded file
116+
-d Show logs in DEBUG mode
110117
-f string
111-
Path to server configuration file in YAML format
112-
-j Show logs in JSON format
118+
Path to server configuration file in YAML format
119+
-j Show logs in JSON format
113120
-k string
114-
Path to private key PEM encoded file
121+
Path to private key PEM encoded file
115122
-l string
116-
Address of HTTP proxy server (default "127.0.0.1:8080")
123+
Address of HTTP proxy server (default "127.0.0.1:8080")
117124
-s string
118-
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
125+
Address of SOCKS5 proxy server (default "127.0.0.1:1080")
126+
-t string
127+
Address of transparent proxy server (it starts along with HTTP proxy server)
119128
-u string
120-
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
121-
-v print version
129+
User for SOCKS5 proxy authentication. This flag invokes prompt for password (not echoed to terminal)
130+
-v print version
122131
```
123132

124-
## Example
125-
126133
### Configuration via CLI flags
127134

128135
```shell
@@ -217,6 +224,152 @@ server:
217224
218225
To learn more about proxy chains visit [Proxychains Github](https://github.com/rofl0r/proxychains-ng)
219226
227+
## Transparent proxy
228+
229+
> Also known as an `intercepting proxy`, `inline proxy`, or `forced proxy`, a transparent proxy intercepts normal application layer communication without requiring any special client configuration. Clients need not be aware of the existence of the proxy. A transparent proxy is normally located between the client and the Internet, with the proxy performing some of the functions of a gateway or router
230+
>
231+
> -- _From [Wiki](https://en.wikipedia.org/wiki/Proxy_server)_
232+
233+
This functionality available only on Linux systems and requires additional setup (`iptables`, ip route, etc)
234+
235+
`-T address` flag specifies the address of transparent proxy server (`GoHPTS` will be running without HTTP server).
236+
237+
`-t address` flag specifies the address of transparent proxy server (`HTTP` proxy and other functionality stays the same).
238+
239+
In other words, `-T` spins up a single server, but `-t` two servers, `http` and `tcp`.
240+
241+
There are two modes `redirect` and `tproxy` that can be specified with `-M` flag
242+
243+
## `redirect` (via _NAT_ and _SO_ORIGINAL_DST_)
244+
245+
In this mode proxying happens with `iptables` `nat` table and `REDIRECT` target. Host of incoming packet changes to the address of running `redirect` transparent proxy, but it also contains original destination that can be retrieved with `getsockopt(SO_ORIGINAL_DST)`
246+
247+
To run `GoHPTS` in this mode you use `-t` or `-T` flags with `-M redirect`
248+
249+
### Example
250+
251+
```shell
252+
# run the proxy
253+
gohpts -s 1080 -t 1090 -M redirect -d
254+
```
255+
256+
```shell
257+
# run socks5 server on 127.0.0.1:1080
258+
ssh remote -D 1080 -Nf
259+
```
260+
261+
Setup your operating system:
262+
263+
```shell
264+
# commands below require elevated privileges (you can run it with `sudo -i`)
265+
266+
#enable ip forwarding
267+
sysctl -w net.ipv4.ip_forward=1
268+
269+
# create `GOHPTS` nat chain
270+
iptables -t nat -N GOHPTS
271+
272+
# set no redirection rules for local, http proxy, ssh and redirect procy itself
273+
iptables -t nat -A GOHPTS -d 127.0.0.0/8 -j RETURN
274+
iptables -t nat -A GOHPTS -p tcp --dport 8080 -j RETURN
275+
iptables -t nat -A GOHPTS -p tcp --dport 1090 -j RETURN
276+
iptables -t nat -A GOHPTS -p tcp --dport 22 -j RETURN
277+
278+
# redirect traffic to transparent proxy
279+
iptables -t nat -A GOHPTS -p tcp -j REDIRECT --to-ports 1090
280+
281+
# setup prerouting by adding our proxy
282+
iptables -t nat -A PREROUTING -p tcp -j GOHPTS
283+
284+
# intercept local traffic for testing
285+
iptables -t nat -A OUTPUT -p tcp -j GOHPTS
286+
```
287+
288+
Test connection:
289+
290+
```shell
291+
#traffic should be redirected via 127.0.0.1:1090
292+
curl http://example.com
293+
```
294+
295+
```shell
296+
#traffic should be redirected via 127.0.0.1:8080
297+
curl --proxy http://127.0.0.1:8080 http://example.com
298+
```
299+
300+
Undo everything:
301+
302+
```shell
303+
sysctl -w net.ipv4.ip_forward=0
304+
iptables -t nat -D PREROUTING -p tcp -j GOHPTS
305+
iptables -t nat -D OUTPUT -p tcp -j GOHPTS
306+
iptables -t nat -F GOHPTS
307+
iptables -t nat -X GOHPTS
308+
```
309+
310+
## `tproxy` (via _MANGLE_ and _IP_TRANSPARENT_)
311+
312+
In this mode proxying happens with `iptables` `mangle` table and `TPROXY` target. Transparent proxy sees destination address as is, it is not being rewrited by the kernel. For this to work the proxy binds with socket option `IP_TRANSPARENT`, `iptables` intercepts traffic using TPROXY target, routing rules tell marked packets to go to the local proxy without changing their original destination.
313+
314+
This mode requires elevated privileges to run `GoHPTS`. You can do that by running the follwing command:
315+
316+
```shell
317+
sudo setcap 'cap_net_admin+ep' ~/go/bin/gohpts
318+
```
319+
320+
To run `GoHPTS` in this mode you use `-t` or `-T` flags with `-M tproxy`
321+
322+
### Example
323+
324+
```shell
325+
# run the proxy
326+
gohpts -s 1080 -T 0.0.0.0:1090 -M tproxy -d
327+
```
328+
329+
```shell
330+
# run socks5 server on 127.0.0.1:1080
331+
ssh remote -D 1080 -Nf
332+
```
333+
334+
Setup your operating system:
335+
336+
```shell
337+
ip netns exec ns-client ip route add default via 10.0.0.1
338+
sysctl -w net.ipv4.ip_forward=1
339+
340+
iptables -t mangle -A PREROUTING -i veth1 -p tcp -j TPROXY --on-port 1090 --tproxy-mark 0x1/0x1
341+
342+
ip rule add fwmark 1 lookup 100
343+
ip route add local 0.0.0.0/0 dev lo table 100
344+
```
345+
346+
Test connection:
347+
348+
```shell
349+
ip netns exec ns-client curl http://1.1.1.1
350+
```
351+
352+
Undo everything:
353+
354+
```shell
355+
sysctl -w net.ipv4.ip_forward=0
356+
iptables -t mangle -F
357+
ip rule del fwmark 1 lookup 100
358+
ip route flush table 100
359+
ip netns del ns-client
360+
ip link del veth1
361+
```
362+
363+
## Links
364+
365+
Learn more about transparent proxies by visiting the following links:
366+
367+
- [Transparent proxy support in Linux Kernel](https://docs.kernel.org/networking/tproxy.html)
368+
- [Transparent proxy tutorial by Gost](https://latest.gost.run/en/tutorials/redirect/)
369+
- [Simple tproxy example](https://github.com/FarFetchd/simple_tproxy_example)
370+
- [Golang TProxy](https://github.com/KatelynHaworth/go-tproxy)
371+
- [Transparent Proxy Implementation using eBPF and Go](https://medium.com/all-things-ebpf/building-a-transparent-proxy-with-ebpf-50a012237e76)
372+
220373
## License
221374

222375
MIT

cmd/gohpts/cli.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"flag"
55
"fmt"
66
"os"
7+
"runtime"
8+
"slices"
79

810
gohpts "github.com/shadowy-pycoder/go-http-proxy-to-socks"
911
"golang.org/x/term"
@@ -13,6 +15,7 @@ const (
1315
app string = "gohpts"
1416
addrSOCKS = "127.0.0.1:1080"
1517
addrHTTP = "127.0.0.1:8080"
18+
tproxyOS = "linux"
1619
)
1720
const usagePrefix string = `
1821
_____ _ _ _____ _______ _____
@@ -40,6 +43,18 @@ func root(args []string) error {
4043
flags.StringVar(&conf.CertFile, "c", "", "Path to certificate PEM encoded file")
4144
flags.StringVar(&conf.KeyFile, "k", "", "Path to private key PEM encoded file")
4245
flags.StringVar(&conf.ServerConfPath, "f", "", "Path to server configuration file in YAML format")
46+
if runtime.GOOS == tproxyOS {
47+
flags.StringVar(&conf.TProxy, "t", "", "Address of transparent proxy server (it starts along with HTTP proxy server)")
48+
flags.StringVar(&conf.TProxyOnly, "T", "", "Address of transparent proxy server (no HTTP)")
49+
flags.Func("M", fmt.Sprintf("Transparent proxy mode: %s", gohpts.SupportedTProxyModes), func(flagValue string) error {
50+
if !slices.Contains(gohpts.SupportedTProxyModes, flagValue) {
51+
fmt.Fprintf(os.Stderr, "%s: %s is not supported (type '%s -h' for help)\n", app, flagValue, app)
52+
os.Exit(2)
53+
}
54+
conf.TProxyMode = flagValue
55+
return nil
56+
})
57+
}
4358
flags.BoolFunc("d", "Show logs in DEBUG mode", func(flagValue string) error {
4459
conf.Debug = true
4560
return nil
@@ -64,9 +79,35 @@ func root(args []string) error {
6479
}
6580
seen := make(map[string]bool)
6681
flags.Visit(func(f *flag.Flag) { seen[f.Name] = true })
82+
if seen["t"] && seen["T"] {
83+
return fmt.Errorf("cannot specify both -t and -T flags")
84+
}
85+
if seen["t"] {
86+
if !seen["M"] {
87+
return fmt.Errorf("Transparent proxy mode is not provided: -M flag")
88+
}
89+
}
90+
if seen["T"] {
91+
for _, da := range []string{"U", "c", "k", "l"} {
92+
if seen[da] {
93+
return fmt.Errorf("-T flag only works with -s, -u, -f, -M, -d and -j flags")
94+
}
95+
}
96+
if !seen["M"] {
97+
return fmt.Errorf("Transparent proxy mode is not provided: -M flag")
98+
}
99+
}
100+
if seen["M"] {
101+
if !seen["t"] && !seen["T"] {
102+
return fmt.Errorf("Transparent proxy mode requires -t or -T flag")
103+
}
104+
}
67105
if seen["f"] {
68106
for _, da := range []string{"s", "u", "U", "c", "k", "l"} {
69107
if seen[da] {
108+
if runtime.GOOS == tproxyOS {
109+
return fmt.Errorf("-f flag only works with -t, -T, -M, -d and -j flags")
110+
}
70111
return fmt.Errorf("-f flag only works with -d and -j flags")
71112
}
72113
}
@@ -89,6 +130,7 @@ func root(args []string) error {
89130
conf.ServerPass = string(bytepw)
90131
fmt.Print("\033[2K\r")
91132
}
133+
92134
p := gohpts.New(&conf)
93135
p.Run()
94136
return nil

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ require (
66
github.com/goccy/go-yaml v1.18.0
77
github.com/rs/zerolog v1.34.0
88
golang.org/x/net v0.40.0
9+
golang.org/x/sys v0.33.0
910
golang.org/x/term v0.32.0
1011
)
1112

1213
require (
1314
github.com/mattn/go-colorable v0.1.13 // indirect
1415
github.com/mattn/go-isatty v0.0.19 // indirect
15-
golang.org/x/sys v0.33.0 // indirect
1616
)

0 commit comments

Comments
 (0)