README 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. Userspace DNS tunnel with support for DoH and DoT
  2. https://www.bamsoftware.com/software/dnstt/
  3. David Fifield <[email protected]>
  4. Public domain
  5. dnstt is a DNS tunnel with these features:
  6. * Works over DNS over HTTPS (DoH) and DNS over TLS (DoT) as well as
  7. plaintext UDP DNS.
  8. * Embeds a sequencing and session protocol (KCP/smux), which means that
  9. the client does not have to wait for a response before sending more
  10. data, and any lost packets are automatically retransmitted.
  11. * Encrypts the contents of the tunnel and authenticates the server by
  12. public key.
  13. dnstt is an application-layer tunnel that runs in userspace. It doesn't
  14. provide a TUN/TAP interface; it only hooks up a local TCP port with a
  15. remote TCP port (like netcat or `ssh -L`) by way of a DNS resolver. It
  16. does not itself provide a SOCKS or HTTP proxy interface, but you can get
  17. the same effect by running a proxy on the tunnel server and having the
  18. tunnel terminate at the proxy.
  19. ```
  20. .------. | .---------. .------.
  21. |tunnel| | | public | |tunnel|
  22. |client|<---DoH/DoT--->|recursive|<--UDP DNS-->|server|
  23. '------' |c |resolver | '------'
  24. | |e '---------' |
  25. .------. |n .------.
  26. |local | |s |remote|
  27. | app | |o | app |
  28. '------' |r '------'
  29. ```
  30. ## DNS zone setup
  31. Because the server side of the tunnel acts like an authoritative name
  32. server, you need to own a domain name and set up a subdomain for the
  33. tunnel. Let's say your domain name is example.com and your server's IP
  34. addresses are 203.0.113.2 and 2001:db8::2. Go to your name registrar and
  35. add three new records:
  36. ```
  37. A tns.example.com points to 203.0.113.2
  38. AAAA tns.example.com points to 2001:db8::2
  39. NS t.example.com is managed by tns.example.com
  40. ```
  41. The labels `tns` and `t` can be anything you want, but the `tns` label
  42. should not be a subdomain of the `t` label (that space is reserved for
  43. the contents of the tunnel), and the `t` label should be short (because
  44. there is limited space available in a DNS message, and the domain name
  45. takes up part of that space).
  46. Now, when a recursive DNS resolver receives a query for a name like
  47. aaaa.t.example.com, it will forward the query to the tunnel server at
  48. 203.0.113.2 or 2001:db8::2.
  49. ## Tunnel server setup
  50. Compile the server:
  51. ```
  52. tunnel-server$ cd dnstt-server
  53. tunnel-server$ go build
  54. ```
  55. First you need to generate the server keypair that will be used to
  56. authenticate the server and encrypt the tunnel.
  57. ```
  58. tunnel-server$ ./dnstt-server -gen-key -privkey-file server.key -pubkey-file server.pub
  59. privkey written to server.key
  60. pubkey written to server.pub
  61. ```
  62. Run the server. You need to provide an address that will listen for UDP
  63. DNS packets (`:5300`), the private key file (`server.key`), the root of
  64. the DNS zone (`t.example.com`), and a TCP address to which incoming
  65. tunnel streams will be forwarded (`127.0.0.1:8000`).
  66. ```
  67. tunnel-server$ ./dnstt-server -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000
  68. ```
  69. The tunnel server needs to be able to receive packets on an external
  70. port 53. You can have it listen on port 53 directly using `-udp :53`,
  71. but that requires the program to run as root. It is better to run the
  72. program as an ordinary user and have it listen on an unprivileged port
  73. (`:5300` above), and port-forward port 53 to it. On Linux, use these
  74. commands to forward external port 53 to localhost port 5300:
  75. ```
  76. tunnel-server$ sudo iptables -I INPUT -p udp --dport 5300 -j ACCEPT
  77. tunnel-server$ sudo iptables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-ports 5300
  78. tunnel-server$ sudo ip6tables -I INPUT -p udp --dport 5300 -j ACCEPT
  79. tunnel-server$ sudo ip6tables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-ports 5300
  80. ```
  81. You need to also run something for the tunnel server to connect to. It
  82. can be a proxy server or anything else. For testing, you can use an
  83. Ncat listener:
  84. ```
  85. tunnel-server$ ncat -l -k -v 127.0.0.1 8000
  86. ```
  87. ## Tunnel client setup
  88. Compile the client:
  89. ```
  90. tunnel-client$ cd dnstt-client
  91. tunnel-client$ go build
  92. ```
  93. Copy the server.pub file from the server to the client. You don't need
  94. server.key on the client; leave it on the server.
  95. Choose a public DoH or DoT resolver. There is a list of DoH resolvers
  96. here:
  97. * https://github.com/curl/curl/wiki/DNS-over-HTTPS#publicly-available-servers
  98. And DoT resolvers here:
  99. * https://dnsprivacy.org/wiki/display/DP/DNS+Privacy+Public+Resolvers#DNSPrivacyPublicResolvers-DNS-over-TLS%28DoT%29
  100. * https://dnsencryption.info/imc19-doe.html
  101. To run the tunnel client using DoH, you need to provide the URL of the
  102. DoH resolver (`https://doh.example/dns-query`), the server's public key
  103. files (`server.pub`), the root of the DNS zone (`t.example.com`), and
  104. the local TCP port that will receive connections and forward them
  105. through the tunnel (`127.0.0.1:7000`):
  106. ```
  107. tunnel-client$ ./dnstt-client -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
  108. ```
  109. For DoT, it's the same, but use the `-dot` option instead:
  110. ```
  111. tunnel-client$ ./dnstt-client -dot dot.example:853 -pubkey-file server.pub t.example.com 127.0.0.1:7000
  112. ```
  113. Once the tunnel client is running, you can connect to the local end of
  114. the tunnel, type something, and see it appear at the remote end.
  115. ```
  116. tunnel-client$ ncat -v 127.0.0.1 7000
  117. ```
  118. The client also has a plaintext UDP mode that can work through a
  119. recursive resolver or directly to the tunnel server
  120. (`-udp tns.example.com`), but it does not provide any covertness for the
  121. tunnel and should only be used for testing.
  122. ## How to make a proxy
  123. dnstt is only a tunnel; it's up to you what you want to connect to it.
  124. You can make the tunnel work like an ordinary SOCKS or HTTP proxy by
  125. having the tunnel server forward to a standard proxy server. There are
  126. many ways to set it up; here are some examples.
  127. ### Ncat HTTP proxy
  128. Ncat has a simple built-in HTTP/HTTPS proxy, good for testing. Be aware
  129. that Ncat's proxy isn't intended for use by untrusted clients; it won't
  130. prevent them from connecting to localhost ports on the tunnel server,
  131. for example.
  132. ```
  133. tunnel-server$ ncat -l -k --proxy-type http 127.0.0.1 8000
  134. tunnel-server$ ./dnstt-server -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000
  135. ```
  136. On the client, have the tunnel client listen on 127.0.0.1:7000, and configure
  137. your applications to use 127.0.0.1:7000 as an HTTP proxy.
  138. ```
  139. tunnel-client$ ./dnstt-client -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
  140. tunnel-client$ curl --proxy http://127.0.0.1:7000/ https://wtfismyip.com/text
  141. ```
  142. ### SSH SOCKS proxy
  143. OpenSSH has a built-in SOCKS proxy, which makes it easy to add a SOCKS
  144. proxy to a server that already has sshd installed.
  145. On the server, make a localhost SSH connection, using the `-D` option to
  146. open a SOCKS listener at port 8000. Then configure the tunnel server to
  147. forward incoming connections to port 8000. Have the tunnel client listen
  148. on its own local port 7000.
  149. ```
  150. tunnel-server$ ssh -N -D 127.0.0.1:8000 -o NoHostAuthenticationForLocalhost=yes 127.0.0.1
  151. # Enter the password of the local user on tunnel-server
  152. tunnel-server$ ./dnstt-server -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:8000
  153. ```
  154. ```
  155. tunnel-client$ ./dnstt-client -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
  156. tunnel-client$ curl --proxy socks5h://127.0.0.1:7000/ https://wtfismyip.com/text
  157. ```
  158. The above configuration, by locating the SOCKS client port on the
  159. server, makes a SOCKS proxy that can be used by anyone with access to
  160. the DNS tunnel. Alternatively, you can make an SSH SOCKS proxy for your
  161. own private use, with the SSH connection going through the tunnel and
  162. the SOCKS client port being located at the client.
  163. Let's assume you have the SSH details configured so that you can run
  164. `ssh tunnel-server` on the tunnel client. Make sure `AllowTcpForwarding`
  165. is set to `yes` (the default value) in sshd_config. Run the tunnel
  166. server and have it forward directly to the SSH port.
  167. ```
  168. tunnel-server$ ./dnstt-server -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:22
  169. ```
  170. Run the tunnel client with the local listening port at 127.0.0.1:7000.
  171. The `HostKeyAlias` ssh option lets you connect to the SSH server as if
  172. it were located at 127.0.0.1:8000. Replace `tunnel-server` with the
  173. hostname or IP address of the SSH server.
  174. ```
  175. tunnel-client$ ./dnstt-client -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:8000
  176. tunnel-client$ ssh -N -D 127.0.0.1:7000 -o HostKeyAlias=tunnel-server -p 8000 127.0.0.1
  177. tunnel-client$ curl --proxy socks5h://127.0.0.1:7000/ https://wtfismyip.com/text
  178. ```
  179. ### Tor bridge
  180. You can run a Tor bridge on the tunnel server and tunnel the connection
  181. to the bridge with dnstt, using dnstt as like a pluggable transport. The
  182. Tor client provides a SOCKS interface that other programs can use. Let's
  183. say your Tor bridge's ORPort is 9001.
  184. ```
  185. tunnel-server$ ./dnstt-server -udp :5300 -privkey-file server.key t.example.com 127.0.0.1:9001
  186. ```
  187. ```
  188. tunnel-client$ ./dnstt-client -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
  189. ```
  190. Add a Bridge line to /etc/tor/torrc, or paste it into Tor Browser. You
  191. can get `FINGERPRINT` from /var/lib/tor/fingerprint on the bridge.
  192. ```
  193. Bridge 127.0.0.1:7000 FINGERPRINT
  194. ```
  195. If you use a system tor, the client SOCKS port will be 127.0.0.1:9050.
  196. If you use Tor Browser, it will be 127.0.0.1:9150.
  197. ## Covertness
  198. Support for DoH and DoT is only to make it more difficult for a local
  199. observer to see that a DNS tunnel is being used, not for the overall
  200. security of the connection. There is a separate encryption layer inside
  201. the tunnel that protects the contents of the tunnel from the resolver
  202. itself.
  203. The encryption of DoH or DoT prevents a network observer between the
  204. tunnel client and the resolver from seeing the remote destination of the
  205. tunnel. An observer can see that the tunnel client is connecting to a
  206. resolver, but cannot see where the resolver is forwarding its queries.
  207. An observer can probably infer, based on volume and other traffic
  208. characteristics, that a tunnel is being used, though it cannot tell
  209. where the remote end of the tunnel is, nor what the contents of the
  210. tunnel are. If the tunnel client is not using DoH or DoT but instead UDP
  211. (`-udp` option), then even an observer between the tunnel client and the
  212. resolver can see that a tunnel is being used and where the remote end of
  213. the tunnel is.
  214. An observer between the resolver and the tunnel server (this includes
  215. the resolver itself) can easily tell that a tunnel is being used and
  216. where the remote end of the tunnel is, because there is no DoH or DoT
  217. encryption at that point. This kind of observer still cannot read the
  218. contents of the tunnel, because there is an additional layer of
  219. end-to-end encryption between the tunnel client and the tunnel server.
  220. An observer who watches what leaves the tunnel server will be able to
  221. see anything that the tunnel server forwards to some other host (if the
  222. tunnel server is acting as a proxy, for example), unless that data has
  223. been separately encrypted before being sent through the tunnel.
  224. The dnstt client does not do anything special to disguise its TLS
  225. fingerprint. It uses the crypto/tls package from Go, and its TLS
  226. fingerprint will depend on what version of Go it was compiled with. You
  227. should assume that the DNS tunnel client is identifiable by TLS
  228. fingerprint. A path to hiding the TLS fingerprint would be to integrate
  229. uTLS (https://github.com/refraction-networking/utls).
  230. ## Encryption and authentication
  231. The tunnel uses a Noise protocol (https://noiseprotocol.org/noise.html)
  232. for end-to-end security between the tunnel client and tunnel server.
  233. This protocol is independent of the DoH or DoT encryption between the
  234. tunnel client and resolver. The specific protocol is Noise_NK_25519_ChaChaPoly_BLAKE2s
  235. (https://noiseprotocol.org/noise.html#protocol-names-and-modifiers).
  236. The NK handshake pattern authenticates the server but not the client.
  237. The Noise layer is sandwiched between two other protocol layers: KCP
  238. (https://github.com/xtaci/kcp-go) which creates a reliable stream on top
  239. of unreliable datagrams, and smux (https://github.com/xtaci/smux) which
  240. provides stream multiplexing and session features. An observer who can
  241. see DNS messages, such as the intermediary resolver, will be able to see
  242. the headers of the KCP layer, but not of the smux layer nor of the
  243. streams that are inside. The model is similar to what you would get with
  244. TLS or SSH over TCP: an observer can see TCP-level ACKs and sequence
  245. numbers, but cannot read the stream data.
  246. ```
  247. application data
  248. smux
  249. Noise
  250. KCP
  251. DNS messages
  252. DoH / DoT / UDP DNS
  253. ```
  254. When you run `dnstt-server -gen-key`, you can save the private and
  255. public keys to a file using the `-privkey-file` and `-pubkey-file`
  256. options. You can then load the keys later using `-privkey-file` on the
  257. server and `-pubkey-file` on the client. Alternatively, you can deal
  258. with the keys as literal hexadecimal strings rather than files. If you
  259. run `dnstt-server -gen-key` without the `-privkey-file` and
  260. `-pubkey-file` options, it will display the keys rather than save them
  261. to files. You can then use the keys with `-privkey` on the server and
  262. `-pubkey` on the client.
  263. ```
  264. $ ./dnstt-server -gen-key
  265. privkey 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
  266. pubkey 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff
  267. $ ./dnstt-server -udp :5300 -privkey 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef t.example.com 127.0.0.1:8000
  268. $ ./dnstt-client -dot dot.example:853 -pubkey 0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff t.example.com 127.0.0.1:7000
  269. ```
  270. If you run the server without `-privkey-file` or `-privkey`, it will
  271. generate a temporary keypair and print the public key in the log. But
  272. the key will be different the next time you restart the server, and you
  273. will have to reconfigure clients.
  274. ## Payload sizes
  275. In the client, the available space for user data per query depends on
  276. the length of the domain name in use. Shorter domain names leave more
  277. space for user data.
  278. In the server, the available space for user data per response depends on
  279. the maximum UDP payload size. The larger the UDP payload size, the more
  280. space there is for user data. You want to use as large a UDP payload
  281. size as possible, but not larger than what is supported by the resolver
  282. you are using. Values above 1452 may cause IP fragmentation which can
  283. reduce performance. You can control the maximum UDP payload size with
  284. the `-mtu` option on the server. The default is 1232 bytes; this ought
  285. to be supported by most resolvers that understand EDNS(0) (RFC 6891).
  286. For maximum compatibility, set the maximum to 512, but know that doing
  287. so will reduce downstream bandwidth.
  288. ```
  289. $ ./dnstt-server -mtu 512 -doh https://doh.example/dns-query -pubkey-file server.pub t.example.com 127.0.0.1:7000
  290. ```
  291. The client and server emit an "effective MTU" log line when starting up
  292. that shows how much space is available for user data in each query or
  293. response. For the server, there may be more space available in some
  294. responses and less in others (depending on the size of the corresponding
  295. query); the logged value is the minimum that is guaranteed to be
  296. supported in any response.