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