I'd like to maintain a DNS tunnel on my self-hosted server at example.com. I also have a DNS server on it, which serves everything for example.com. I'm currently using dns2tcp for DNS tunneling, on the domain tunnel.example.com. NSD3 is used for serving authoritative zones, because it is both simple and secure.

However, I have only one public IPv4 address, which means that NSD and dns2tcp can't listen on the same IP/port.

So I'm currently using PowerDNS Recursor using the forward-zones parameter like this:


This enables request for authoritative zone to be asked to the correct server, as well as for tunnel requests. NSD is listening on port 5353 and dns2tcp on port 5354.

However, this is bad, because the recursor needs to be open. And it actually answers to any recursive query.

Do you have any solution for that? I really prefer a solution that doesn't involve setting up BIND, but if you are in the mood to convince me, don't hesitate to do so ;)

EDIT: I change the title to be clearer.


Two servers can not listen on the same IP+Port combination at the same time.
Anything you do to enable such a beast with two backs is Hackery - it is Bad and Wrong and should not be done. Pain, Sorrow, Madness and Death lie down this path - turn back now or you will be eaten by a grue.

The Right Solution

Get another IP. Ideally get another server entirely, and don't run your recursive DNS on the same box as your authoritative.

The easy solution

Get over your fear/distrust/hatred of BIND (acquire a copy of DNS and BIND and make nice), and use the allow-recursion{…}; directive to limit who can make recursive queries.

This easy solution may still leave you open to cache poisoning and other nastiness. If you really want security for your authoritative DNS server you need to run a separate nameserver process listening on a separate IP address, ideally on a separate host (or jail, VM, whatever).

  • I just want to split answers based on the query. Like a DNS server could be able to query different backends for example. And anyway, the allow-recursion will clear the AA bit, which is needed for some configuration. – Adrien Clerc Jun 15 '12 at 14:30
  • @AdrienClerc BIND's allow-recursion does not clear the AA bit for an authoritative answer originated by the server you're talking to (If you don't believe me, set up BIND with allow-recursion enabled and at least one zone it's authoritative for and try it). It DOES PROPERLY clear it for an answer it found by recursion, as it is NOT authoritative for that answer - it's just passing along information. – voretaq7 Jun 15 '12 at 14:45


The solution with the string module from iptables (see below) is not translatable with nftables, so I had to find another solution.

I'm now using dnsdist which is a DNS load balancer. It uses more CPU and RAM, but it's clearly more powerful. And I like it for being dedicated to this task, without rewriting DNS answers, except when requested. Here is an excerpt of my configuration file:

-- dnsdist configuration file, an example can be found in /usr/share/doc/dnsdist/examples/

-- disable security status polling via DNS

setLocal('<PUBLIC_IP4>:53', {reusePort=true, interface="eth_adsl"})
addLocal('[<PUBLIC_IP6>]:53', {reusePort=true, interface="eth_adsl"})
setACL({'', '::/0'})


-- Declare NSD authoritative server. It could also listen to localhost, but I let it answer on public addresses, using port 5300
newServer({address="[<PUBLIC_IP6>]:5300", checkInterval=60, pool="root"})
newServer({address="<PUBLIC_IP4>:5300", checkInterval=60, pool="root"})

-- Declare DNS Tunnel (iodine), on port 5354
newServer({address="", pool="tunnel"})
-- This is using socket activation, so disable dnsdist checks to avoid waking up the tunnel

-- Forward to tunnel, skipping cache
tunnelRule = makeRule("t.antipoul.fr")
addAction(tunnelRule, SkipCacheAction())
addAction(tunnelRule, PoolAction("tunnel"))

-- Allow Zone transfer from secondary only
-- See https://dnsdist.org/advanced/axfr.html
addAction(AndRule({OrRule({QTypeRule(DNSQType.AXFR), QTypeRule(DNSQType.IXFR)}), NotRule(makeRule("<SECONDARY_IP4>/32"))}), RCodeAction(DNSRCode.REFUSED))

-- All remaining traffic goes to NSD
addAction(AllRule(), PoolAction("root"))

Original 2012 solution

I've found a solution to my problem. However, it is not a generic solution for the question.

In my case, I rely on the protocol used by dns2tcp, which communicates using subdomain requests on tunnel.example.com. So the idea was to match packet containing such requests, and build a netfilter rule:

iptables -t nat -I PREROUTING -p udp --dport 53 -m string --algo bm --from 42 --hex-string "|0674756e6e656c076578616d706c6503636f6d00|" -j REDIRECT -to-ports 5354
  • The hex-string parameter is the string corresponding to tunnel.example.com as seen in DNS packets.
  • The from 42 ensures that only subrequests are redirected to dns2tcp, and not regular requests for the domain itself such as SOA or NS requests.
  • NSD now listens on port 53, which is much better than before.
  • There are no more public recursive server, which is even better.

This is not perfect, but it does use much less software than before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.