forked from ms/transocks
116 lines
3.1 KiB
Markdown
116 lines
3.1 KiB
Markdown
|
Design notes
|
||
|
============
|
||
|
|
||
|
transocks should work as a SOCKS5 client used as a transparent proxy
|
||
|
agent running on every hosts in trusted (i.e. data center) networks.
|
||
|
|
||
|
Destination NAT (DNAT)
|
||
|
----------------------
|
||
|
|
||
|
On Linux, redirecting locally-generated packet to transocks can be done
|
||
|
by iptables with DNAT (or REDIRECT) target.
|
||
|
|
||
|
Since DNAT/REDIRECT modifies packet's destination address, transocks
|
||
|
need to recover the destination address by using `getsockopt` with
|
||
|
`SO_ORIGINAL_DST` for IPv4 or with `IP6T_SO_ORIGINAL_DST` for IPv6.
|
||
|
This is, of course, Linux-specific, and Go does not provide standard
|
||
|
API for them.
|
||
|
|
||
|
Policy-based routing
|
||
|
--------------------
|
||
|
|
||
|
Except for DNAT, some operating systems provide a way to route packets
|
||
|
to a specific program. In order to receive such packets, the program
|
||
|
need to set special options on the listening socket before `bind`.
|
||
|
|
||
|
Difficult is that Go does not allow setting socket options before `bind`.
|
||
|
|
||
|
### Linux TPROXY
|
||
|
|
||
|
Linux iptables has [TPROXY][] target that can route packets to a
|
||
|
specific local port. The socket option is:
|
||
|
|
||
|
* IPv4
|
||
|
|
||
|
```
|
||
|
setsockopt(IPPROTO_IP, IP_TRANSPARENT)
|
||
|
```
|
||
|
|
||
|
* IPv6
|
||
|
|
||
|
```
|
||
|
setsockopt(IPPROTO_IPV6, IPV6_TRANSPARENT)
|
||
|
```
|
||
|
|
||
|
To set this option, transocks must have `CAP_NET_ADMIN` capability.
|
||
|
Run transocks as root user, or grant `CAP_NET_ADMIN` for the file by:
|
||
|
|
||
|
```
|
||
|
sudo setcap 'cap_net_admin+ep' transocks
|
||
|
```
|
||
|
|
||
|
### FreeBSD, NetBSD, OpenBSD
|
||
|
|
||
|
Use [PF with divert-to][pf] to route packets to a specific local port.
|
||
|
|
||
|
The listening program needs to set a socket option before `bind`:
|
||
|
|
||
|
* FreeBSD (IPv4)
|
||
|
|
||
|
```
|
||
|
setsockopt(IPPROTO_IP, IP_BINDANY)
|
||
|
```
|
||
|
|
||
|
* FreeBSD (IPv6)
|
||
|
|
||
|
```
|
||
|
setsockopt(IPPROTO_IPV6, IPV6_BINDANY)
|
||
|
```
|
||
|
|
||
|
* NetBSD, OpenBSD
|
||
|
|
||
|
```
|
||
|
setsockopt(SOL_SOCKET, SO_BINDANY)
|
||
|
```
|
||
|
|
||
|
For this to work, transocks must run as root.
|
||
|
|
||
|
Implementation strategy
|
||
|
-----------------------
|
||
|
|
||
|
We use Go for its efficiency and simpleness.
|
||
|
|
||
|
For SOCKS5, [golang.org/x/net/proxy][x/net] already provides SOCKS5 client.
|
||
|
|
||
|
For Linux NAT, we need to use [golang.org/x/sys/unix][x/sys] and
|
||
|
[unsafe.Pointer][] to use non-standard `getsockopt` options.
|
||
|
|
||
|
To set socket options before `bind`, we need to create sockets manually
|
||
|
by using [golang.org/x/sys/unix] and then convert the native socket to
|
||
|
`*net.TCPListener` by [net.FileListener][].
|
||
|
|
||
|
CONNECT tunnel
|
||
|
--------------
|
||
|
|
||
|
As golang.org/x/net/proxy can add custom dialers, we can implement
|
||
|
a proxy using http CONNECT method for tunneling through HTTP proxies
|
||
|
such as [Squid][].
|
||
|
|
||
|
Note that the default Squid configuration file installed for
|
||
|
Ubuntu 14.04 prohibits CONNECT to ports other than 443.
|
||
|
|
||
|
```
|
||
|
# Deny CONNECT to other than secure SSL ports
|
||
|
http_access deny CONNECT !SSL_ports
|
||
|
```
|
||
|
|
||
|
Remove or comment out the line to allow CONNECT to ports other than 443.
|
||
|
|
||
|
[TPROXY]: https://www.kernel.org/doc/Documentation/networking/tproxy.txt
|
||
|
[pf]: http://wiki.squid-cache.org/ConfigExamples/Intercept/OpenBsdPf
|
||
|
[x/net]: https://godoc.org/golang.org/x/net/proxy#SOCKS5
|
||
|
[x/sys]: https://godoc.org/golang.org/x/sys/unix
|
||
|
[unsafe.Pointer]: https://golang.org/pkg/unsafe/#Pointer
|
||
|
[net.FileListener]: https://golang.org/pkg/net/#FileListener
|
||
|
[Squid]: http://www.squid-cache.org/
|