Today it is difficult to surprise someone with the terms DoH (DNS over HTTPS), DoT (DNS over TLS) and other crypto-gadgets for DNS. If someone does not understand what this is about, DNS (Domain Name System) is the domain name system that each of us uses hundreds and thousands of times during the day. All human-readable domain names lead to a specific IP address, which a computer can find out by contacting special servers.

Large enterprises, home Internet providers, as well as anyone who wants to have their own DNS server, because the DNS server is very simple in its device. Among other considerations, they deploy their servers for reasons of additional privacy, because the administrator of a foreign server, to which we turn, will see our address and will know which web resource we decided to visit.

The DNS protocol is quite old (~ 1987), so it does not provide any encryption. All DNS requests and responses go over the network in the clear, so in the initial variation, not only the administrator of the DNS server, but also the operators of all intermediate nodes through which traffic passes, can spy on the user. Modern solutions like DNSCrypt, DNS over HTTPS and so on solve the problem of intercepting information along the way, as they encrypt DNS requests from the user to the server and in the opposite direction. But! The protocols that encrypt traffic do not solve one important issue – the analysis of all requests on the side of the server itself, which still sees both the request itself and the IP address from which it came. The DNSCrypt project has an additional gadget for this, but I was not impressed by this layering on a three-story pie. Perhaps because I have my own pie … I will be glad to adequate criticism, but I hope there will be no stupid comments that every person on the planet needs to have his own personal DNS server to maintain privacy.

DNS over the anonymous network I2P is a concept in which DNS requests are encrypted, but also: first, the server administrator has no idea about the real IP address of the user; secondly, the user does not know the location of the server he is accessing (also good, in order to avoid possible pressure on the administrator). DNS over I2P, or DoI2P (or DoI at all) is a very entertaining variation on the use of hidden networks, which we will now consider in detail.

Theory

The standard calls for a DNS server to run on port 53 (websites, for example, run on standard ports 80 and 443), but in this case, another thing is more interesting: what transport protocol does DNS use. This information is required to create suitable tunnels over the I2P network.

An I2P router that provides access to the I2P network provides proxies of the SOCKS and HTTP types at the local address. In most cases, it is a proxy that is used to work with the network, but to fine-tune the connection through a hidden network, separate tunnels are created in a special configuration file. Tunnels can be server and client tunnels. Their essence lies in accepting connections from a hidden network to a designated local port of some service, for example, a web server, or in transferring client connections from a local tunnel address to a hidden network. Tunnels are divided into two main types: TCP and UDP.

The main working protocol of DNS is UDP, however, the standard provides for the transfer of some information over the TCP protocol. This means that you need to create two servers and two client tunnels: the first for UDP connections, the second for TCP.

Server

The example uses a dnsmasq server that is extremely easy to install and configure, but you can use any server you like. The simplest configuration option for this server looks like this:

port = 53
listen-address = 256.257.258.259
domain-needed
bogus-priv
server = 8.8.8.8

This configuration means that absolutely all requests will be sent to the address 8.8.8.8. Such a server does not make much sense, but as a layer of anonymity and just an example for an article – that’s it. The server accepts requests to the IP address 256.257.258.259, port 53. The fictitious IP address acts solely as an example, as if portraying an already existing DNS server accessible from the regular Internet. In fact, you can use the local address 127.0.0.1 and any port at your discretion, if you serve users exclusively through a hidden network.

To make the DNS server accessible from I2P, you need to create server tunnels. I am using i2pd on Debian 10. The default tunnel configuration file is /etc/i2pd/tunnels.conf. Below is the minimum configuration required to work.

[DNS-TCP]
type = server
host = 256.257.258.259
port = 53
keys = hidden-dns.dat

[DNS-UDP]
type = udpserver
host = 256.257.258.259
address = 256.257.258.259
port = 53
keys = hidden-dns.dat

Notice the address line in the UDP tunnel section. This is a necessary parameter for a non-local address, which tells i2pd from which address incoming requests from the hidden network will come from. This parameter is required for UDP tunnels. It can be omitted if the address is 127.0.0.1.

The keys parameter specifies the keys that form the intranet address. By default, they are located in the / var / lib / i2pd directory. If the file is not found, a new one is created.

Restart i2pd for the changes to take effect. The I2P address of the tunnel can be seen in the web console, under the “I2P Tunnels” tab. In my case, it is dnsgzxkak4zlrrs5tfh42ob57iley4xrp7srrltn2j2yl2ynbiaq.b32.i2p.

Client

In my case, the client machine also uses i2pd and the Debian operating system, the tunnels file is located in the same place as on the server – /etc/i2pd/tunnels.conf. The client configuration might look like this:

[DNS-CLIENT-TCP]
type = client
address = 127.0.0.1
port = 35353
inbound.length = 2
outbound.length = 2
destination = dnsgzxkak4zlrrs5tfh42ob57iley4xrp7srrltn2j2yl2ynbiaq.b32.i2p
keys = transient-dns


[DNS-CLIENT-UDP]
type = udpclient
address = 127.0.0.1
port = 35353
destination = dnsgzxkak4zlrrs5tfh42ob57iley4xrp7srrltn2j2yl2ynbiaq.b32.i2p
keys = transient-dns

The inbound.length and outbound.length parameters are responsible for the length of the incoming and outgoing tunnels. By default, they are three hops, but I have reduced these values ​​to two to minimize latency a little. Similar parameters are applicable for server tunnels as well. Additional parameters are specified only in the first section, since the very first block defines the parameters that apply to all tunnels using the same key (in my case, it is transient-dns). Keys starting with the word transient are temporary – after each restart of i2pd, the client will contact the server from a new intranet address.

For new tunnels to be created, you need to restart i2pd. On this it may seem that everything is done. But no, there is one more nuance left.

The file responsible for configuring DNS on my operating system (Debian 10) does not support specifying a port. Only the IP address of the server can be specified. All requests will be sent to port 53, but our tunnel hangs on port 35353. If you specify port 53 in client tunnels, an error will occur and no tunnels will be created, because all ports below 1024 are privileged – reserved for special needs. Only the superuser (root) can start the service on this port, and i2pd, like other applications, works without superuser rights. Take a deep breath before running i2pd (or some other third-party software) as root, and then read on.

We remove the old DNS from /etc/resolv.conf so that all requests go exclusively through I2P, and add a single server – local: nameserver 127.0.0.1. This will tell the operating system to address 127.0.0.1:53 for name-to-address resolution. It remains to ask the system kernel to redirect requests from port 53 to 35353, where our TCP / UDP tunnels hang, and then send responses in the opposite direction. It’s time for the iptables magic (sorry not the newfangled netfilter, I’m the old school magician).

iptables -t nat -A PREROUTING -i lo -p udp --dport 53 -j REDIRECT --to-port 35353
iptables -t nat -I OUTPUT -p udp -d 127.0.0.1 --dport 53 -j REDIRECT --to-port 35353

iptables -t nat -A PREROUTING -i lo -p tcp --dport 53 -j REDIRECT --to-port 35353
iptables -t nat -I OUTPUT -p tcp -d 127.0.0.1 --dport 53 -j REDIRECT --to-port 35353

Do you hear? Listen, this is fanfare! Check the resolving in any convenient way, I'll use the nslookup utility:

acetone @ adeb: ~ $ nslookup habr.com
Server: 127.0.0.1
Address: 127.0.0.1 # 53

Non-authoritative answer:
Name: habr.com
Address: 178.248.237.68

Afterword

During the configuration, I noticed that dnsmasq only occupies a UDP socket in standby mode, but I decided to leave the TCP tunnel according to the DNS standard, which provides for the transfer of some information over TCP. Be that as it may, the functionality described is perfectly observed even in the absence of TCP tunnels.
The above configuration takes on average 1-2 seconds to request and respond to the DNS server. If your result seems too slow for you, you can reduce the length of the server and client tunnels to 2. There are options with tunnels in one transit node, but this is applicable only for devices that you do not worry about being compromised: for example, if your DNS is not secret , the length of the tunnels can be equal to one or even zero! In this case, you enable the user to build a longer anonymizing tunnel from his side. The main thing is to do everything wisely and not be lazy to get acquainted with the documentation, as well as consult with knowledgeable people.