NLnetLabs / unbound

Unbound is a validating, recursive, and caching DNS resolver.
https://nlnetlabs.nl/unbound
BSD 3-Clause "New" or "Revised" License
3.03k stars 347 forks source link

outgoing-interface randomization needs sysctl ip_nonlocal_bind=1 on Linux or TCP queries fail which breaks DNSSEC #933

Open DanielG opened 1 year ago

DanielG commented 1 year ago

Describe the bug

When using an ip6 netblock with outgoing-interface as documented in the manpage queries needing TCP fail with error: outgoing tcp: bind: Cannot assign requested address. I noticed this since it causes DNSSEC enabled domains to be completely broken.

To reproduce

(Prerequisite: a prefix of some size should be routed to the host running unbound, I use 2001:db8::/64 as an example here)

ip addr add dev lo 2001:db8::1/64
ip route add local 2001:db8::/80

Add to unbound.conf:

server:
  interface: lo
  outgoing-interface: 2001:db8::1/80

Make sure to test with domains that have IPv6 enabled nameservers, I use tracker.debian.org here as that's what I initially saw the problem with.

$ dig @::1 tracker.debian.org
; <<>> DiG 9.18.16-1~deb12u1-Debian <<>> @::1 tracker.debian.org
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 4035
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 1232
;; QUESTION SECTION:
;tracker.debian.org.        IN  A

;; Query time: 4020 msec
;; SERVER: ::1#53(::1) (UDP)
;; WHEN: Sun Sep 10 00:59:48 CEST 2023
;; MSG SIZE  rcvd: 47

unbound (verbosity 4) debug logs showing the bind error: https://gist.github.com/DanielG/34bac7d94983c0065bf12e0de313d986

Documentation fix

I've found that setting sysctl -w net.ipv6.ip_nonlocal_bind=1 will fix this, but I'm not sure if it's intended for this to be needed. If so the documentation should probably be updated.

System:

Configure line: --build=x86_64-linux-gnu --prefix=/usr --includedir=${prefix}/include --mandir=${prefix}/share/man --infodir=${prefix}/share/info --sysconfdir=/etc --localstatedir=/var --disable-option-checking --disable-silent-rules --libdir=${prefix}/lib/x86_64-linux-gnu --runstatedir=/run --disable-maintainer-mode --disable-dependency-tracking --with-pythonmodule --with-pyunbound --enable-subnet --enable-dnstap --enable-systemd --enable-cachedb --with-libhiredis --with-libnghttp2 --with-chroot-dir= --with-dnstap-socket-path=/run/dnstap.sock --disable-rpath --with-pidfile=/run/unbound.pid --with-libevent --enable-tfo-client --with-rootkey-file=/usr/share/dns/root.key --enable-tfo-server Linked libs: libevent 2.1.12-stable (it uses epoll), OpenSSL 3.0.9 30 May 2023 Linked modules: dns64 python cachedb subnetcache respip validator iterator TCP Fastopen feature available

DanielG commented 1 year ago

After a quick look at the code I figured it out, you're supposed to set ip-freebind: yes.

While the manpage does mention "freebind" it doesn't make it explicit that this option needs to be turned on:

              On Linux you need these two commands
              to be able to use the freebind socket option to receive  traffic
              for  the ip6 netblock: ip -6 addr add mynetblock/64 dev lo && ip
              -6 route add local mynetblock/64 dev lo