Iodine DNS Tunnel on Port 53

Iodine tunnels your network activity as DNS requests on port 53. This can be useful when all other ports are blocked or censored. The name Iodine was suggested by the initial letters of “IP over DNS.” By a happy coincidence, the element Iodine has atomic number 53, which just so happens to be the port number for DNS.

To construct an Iodine tunnel, you will need:

In this tutorial, the server and client both run Debian 10. The same procedures will work for recent versions of Ubuntu.

1. Create DNS Records

1.1. Select DNS Service

Almost all domain name registrars provide a DNS service, but not all registrars let you create type NS records. If you cannot add NS records add your registrar, you may need to switch your DNS service from your domain name registrar to Cloudflare.

1.2. Choose Subdomain

You are going to delegate the DNS service for one subdomain to your Iodine service. I suggest using a short name for the subdomain, so that as much space as possible is available for the tunneled data.

In our examples, we will use x as the subdomain we delegate to Iodine.

1.3. Create DNS Records

At your DNS service (whether at your domain name registrar or elsewhere):

1.4. Check DNS Records

You will need to wait for your DNS changes to propagate. How long this takes depends on your DNS service. You can check that your DNS A record for your server resolves by going to your PC and issuing these commands:

sudo apt install dnsutils
dig +short nsx.example.com

2. Set Up Server

2.1. Open Firewall

SSH into your server.

We will open the firewall for port 53/udp and 53/tcp. The Iodine documentation implies that Iodine only actually uses 53/udp, but we will open both. The actual DNS protocol requires 53/tcp to be available as a backup in case 53/udp fails.

We also need to open the firewall for SSH input from the virtual network, for which we use IP addresses 10.10.0.0/24 in the examples in this article.

Firewalls may be implemented with nftables, iptables, ufw, firewalld, or security groups. We will give the example of using nftables with a policy of drop on Debian 10. Here the commands would be:

nft add rule inet filter input udp dport 53 counter accept
nft add rule inet filter input tcp dport 53 counter accept
nft add rule inet filter input tcp dport 22 ip saddr 10.10.0.0/24 counter accept
nft list ruleset > /etc/nftables.conf

If you are not using nftables, then make the equivalent changes for your firewall.

2.2. Obtain Source

Download the source code for Iodine like this:

apt install git -y
git clone https://github.com/yarrick/iodine.git

2.3. Compile and Install

Compile Iodine on your server like this:

apt install build-essential pkg-config zlib1g-dev man-db -y
cd iodine
make
make install

After the install, the two binaries are installed to /usr/local/sbin/:

You can view documentation for Iodine by issuing the command:

man iodine

2.4. Generate Password

The maximum password length for Iodine is 32 characters. Generate a strong password of length 32 like with this command:

openssl rand -base64 24

The 24 byte length is equivalent to 32 base-64 characters. You will obtain a result that looks like this:

VjhNxS2kuNO5P3QpqOpsHQSJ8Kh/zq6S

2.5. Create Systemd Service File

We will make Iodine run as a service. Create the systemd service file for iodined:

vi /usr/lib/systemd/system/iodined.service

Insert contents to run the Iodine server binary like this:

[Unit]
Description=Iodine Server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/iodined -f -c -P VjhNxS2kuNO5P3QpqOpsHQSJ8Kh/zq6S 10.10.0.1 x.example.com
Restart=on-failure

[Install]
WantedBy=multi-user.target

Save the file.

2.6. Start Iodine

Enable and start the Iodine service, so that it starts after every reboot:

systemctl enable iodined
systemctl start iodined

2.7. Carry Out Initial Checks

Check that the service is active (running) like this:

systemctl status iodined

Check that the dns0 virtual interface exists like this:

ip a

Check that Iodine is listening on port 53 like this:

ss -tulpn | grep 53

3. Set Up Client

3.1. Obtain Source

Download the source for Iodine like this:

sudo apt install git -y
git clone https://github.com/yarrick/iodine.git

3.2. Compile and Install

Compile Iodine on your client like this:

sudo apt install build-essential pkg-config zlib1g-dev man-db -y
cd iodine
make
sudo make install

3.3. Run

Check that the Iodine binaries are in your path:

echo $PATH

The results should include /usr/local/sbin.

Install ifconfig:

sudo apt install net-tools -y

Run the Iodine client from the command line like this:

sudo iodine -P VjhNxS2kuNO5P3QpqOpsHQSJ8Kh/zq6S x.example.com

You will see output like this:

Opened dns0
Opened IPv4 UDP socket
Sending DNS queries for x.example.com to 192.168.1.254
Autodetecting DNS query type (use -T to override).
Using DNS type NULL queries
Version ok, both using protocol v 0x00000502. You are user #0
Setting IP of dns0 to 10.10.0.2
Setting MTU of dns0 to 1130
Server tunnel IP is 10.10.0.1
Requesting server address to attempt raw UDP mode (skip with -r)
Server is at xx.xx.xx.xx, trying raw login: (skip with -r) ....failed
Using EDNS0 extension
Switching upstream to codec Base128
Server switched upstream to codec Base128
No alternative downstream codec available, using default (Raw)
Switching to lazy mode for low-latency
Server switched to lazy mode
Autoprobing max downstream fragment size... (skip with -m fragsize)
768 ok.. 1152 ok.. ...1344 not ok.. ...1248 not ok.. ...1200 not ok.. 1176 ok.. 1188 ok.. will use 1188-2=1186
Setting downstream fragment size to max 1186...
Connection setup complete, transmitting data.
Detaching from terminal...

3.4. Carry Out Initial Checks

Check that the dns0 virtual interface exists like this:

ip a

Check that you can reach your server through the tunnel like this:

ping 10.10.0.1

You will need to replace 10.10.0.1 by whatever IP address you chose for your server, if it was different from 10.10.0.1.

Stop ping from running by pressing Ctrl+c.

3.5. Set Up SSH Proxy Tunnel

Iodine tunnels traffic over DNS unencrypted. Therefore we will set up an SSH tunnel within the DNS tunnel.

On the command line, SSH into your server through the DNS tunnel, with dynamic forwarding of port 1080 through the SSH tunnel:

ssh -D 1080 root@10.10.0.1

3.6. Proxy Firefox

On your PC, open Firefox. From the hamburger menu, select Preferences > General. Scroll down to Network Settings. Click the Settings button.

  1. Select Manual proxy configuration
  2. Enter SOCKS Host 127.0.0.1
  3. Enter Port 1080
  4. Select SOCKS v5
  5. Check the box for Proxy DNS when using SOCKS v5
  6. Click OK

3.7. Test End to End

Check the end-to-end functionality to confirm that Iodine, SSH, and Firefox are all configured correctly. Visit IP Chicken. You should see the IP address of the server, not your local client.

4. Get Help and Report Issues