Tunneling all traffic over DNS with a SOCKS proxy

This blogpost will be a short tutorial about the right settings and configuration files you need to achieve the setup outlined in the headline: Sending all IP traffic over a SOCKS proxy through a DNS-based tunnel into the internet.

You basically only need three things:

A DNS tunnel with Iodine

I won't dive into the details on how to setup Iodine, because there are already enough good tutorials on that. Basically, Iodine can be used to build an IP tunnel over DNS.
However, my described setup assumes that the private network 192.168.99.0/24 is configured with 192.168.99.1 being on the server side.
Furthermore, I assume that you can succesfully establish a connection to your iodine server and ping it over the tunnel.

With this setup, you do not necessarily need to have IP-forwarding enabled, because the traffic will go through a SOCKS5-proxy.

A SOCKS5 proxy

Back in the past (probably a couple of years ago), I decided to use ssocks as a SOCKS5 proxy. Not sure if that's still a good choice, but it runs fine for me.
Start ssocks with the following arguments to bind it to 192.168.99.1:8080 (don't ask why I used this port...):

/usr/local/bin/ssocksd -b 192.168.99.1 --port 8080

A transparent proxy redirector

Most of the magic happens on the client side with a tool called redsocks. Most linux distributions ship a package for it, but compiling it manually should work as well. The source code is on Github.

After establishing a connection with your DNS tunnel, we will use this tool to transparently redirect all outgoing connections through the SOCKS5 proxy on the server.

But first we need to adjust the configuration file /etc/redsocks.conf:

base {
	log_debug = off;
	log_info = off;
	log = "syslog:daemon";

	daemon = on;

	user = redsocks;
	group = redsocks;

	redirector = iptables;
}

redsocks {
	local_ip = 127.0.0.1;
	local_port = 31338;

	ip = 192.168.99.1; // change here
	port = 8080; // change here

	type = socks5;
}

redudp {
	local_ip = 127.0.0.1;
	local_port = 10053;

	ip = 192.168.99.1; // change here
	port = 8080; // change here

	dest_ip = 8.8.8.8;
	dest_port = 53;

	udp_timeout = 30;
	udp_timeout_stream = 180;
}

dnstc {
	local_ip = 127.0.0.1;
	local_port = 5300;
}

I only changed the fields ip and port in the redtcp and redudp sections.

You can see that the redirector setting indicates the use of iptables. We therefor have to create a special ruleset that will redirect the outgoing traffic into redsocks, which in turn sends the traffic to the SOCKS proxy.

Create a file called /etc/iptables/redsocks.rules with:

# Transparent SOCKS proxy
# See: http://darkk.net.ru/redsocks/

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:REDSOCKS - [0:0]

# Redirect all output through redsocks
-A OUTPUT -d XXX.XXX.XXX.XXX -j ACCEPT
-A OUTPUT -d 192.168.99.1 -j ACCEPT
-A OUTPUT -p tcp -j REDSOCKS

# Whitelist LANs and some other reserved addresses.
# https://en.wikipedia.org/wiki/Reserved_IP_addresses#Reserved_IPv4_addresses
-A REDSOCKS -d 0.0.0.0/8 -j RETURN
-A REDSOCKS -d 10.0.0.0/8 -j RETURN
-A REDSOCKS -d 127.0.0.0/8 -j RETURN
-A REDSOCKS -d 169.254.0.0/16 -j RETURN
-A REDSOCKS -d 172.16.0.0/12 -j RETURN
#-A REDSOCKS -d 192.168.0.0/16 -j RETURN
-A REDSOCKS -d 224.0.0.0/4 -j RETURN
-A REDSOCKS -d 240.0.0.0/4 -j RETURN

# Redirect everything else to redsocks port
-A REDSOCKS -p tcp -j REDIRECT --to-ports 31338

COMMIT

We have to adjust the example iptables rules a bit:

  • Depending on the subnet you use, you have to comment out the matching -A REDSOCKS -d ... -j RETURN line.
  • We have to accept traffic to our socks5 proxy by adding -A OUTPUT -d 192.168.99.1 -j ACCEPT before the -A OUTPUT -p tcp -j REDSOCKS line.
  • We have to accept traffic to our DNS tunnel server by replacing the XXX.XXX.XXX.XXX with the real ip address of the server running iodined.

After starting redsocks, e.g. with sudo systemctl start redsocks, we have to activate the iptables rules with sudo iptables-restore < /etc/iptables/redsocks.rules.

To test if the setup works correctly, you can run curl ifconfig.me and hope that it returns the server's IP address.

Troubleshoot: DNS not resolving

In case you have problems resolving DNS queries using this setup, the root cause might be that the nameserver YYY.YYY.YYY.YYY entry in your /etc/resolv.conf does not match the dest_ip address in the redudp section. Try adjusting either one so that it's the same.

But why?

If you ask why you should use this (or a similar) DNS-tunnel based approach: It can become helpful if you're in a location where WiFi hotspots require a login and/or a payment before giving internet access. Sometimes, DNS queries are not blocked ;-)

-=-