It's now located in an actual repo:
-
-
Save vt0r/2b5702844530aeddb64a3d1232dfea76 to your computer and use it in GitHub Desktop.
#!/bin/sh | |
# --- | |
# This file goes in /data/on_boot.d/10-nextdns-catchall.sh (mode a+x) | |
SERVICE_FILE="nextdns-catchall.service" | |
SOURCE_FILE_PATH="/data/${SERVICE_FILE}" | |
SYSTEMD_FILE_PATH="/etc/systemd/system/${SERVICE_FILE}" | |
# Exit right away if the source file is not present | |
if [ ! -f $SOURCE_FILE_PATH ]; then | |
echo "Can't find service file ${SOURCE_FILE_PATH}" | |
exit 1 | |
fi | |
sha256() { | |
sha256sum $1 | cut -d' ' -f1 | |
} | |
INSTALLED_SUM="$(sha256 $SYSTEMD_FILE_PATH)" | |
SOURCE_SUM="$(sha256 $SOURCE_FILE_PATH)" | |
if [ "$INSTALLED_SUM" = "$SOURCE_SUM" ]; then | |
echo "Already installed and up to date. Doing nothing." | |
exit 0 | |
fi | |
cp $SOURCE_FILE_PATH $SYSTEMD_FILE_PATH | |
systemctl daemon-reload | |
systemctl enable $SERVICE_FILE | |
systemctl start $SERVICE_FILE |
# This file goes in /data/nextdns-catchall.service (mode a+r) | |
[Unit] | |
Description=NextDNS Catch all traffic | |
After=nextdns.service | |
Requires=nextdns.service | |
[Install] | |
WantedBy=multi-user.target nextdns.service | |
[Service] | |
Type=oneshot | |
RemainAfterExit=yes | |
# Add one of these for each bridge interface that has a LAN IP | |
# and hosts a LAN subnet (one for each VLAN). Examples: | |
ExecStart=sysctl -w net.ipv4.conf.br0.route_localnet=1 | |
# ExecStart=sysctl -w net.ipv4.conf.br100.route_localnet=1 | |
# ExecStart=sysctl -w net.ipv4.conf.br200.route_localnet=1 | |
# ... | |
# Add one of these for each VPN interface (WireGuard, OpenVPN, Teleport) | |
# ExecStart=sysctl -w net.ipv4.conf.wgsrv1.route_localnet=1 | |
# ExecStart=sysctl -w net.ipv4.conf.tun0.route_localnet=1 | |
# ... | |
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -N NEXTDNS_CATCHALL || iptables -w 30 -t nat -F NEXTDNS_CATCHALL" | |
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL >/dev/null 2>&1 || iptables -w 30 -t nat -A PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL" | |
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL >/dev/null 2>&1 || iptables -w 30 -t nat -A PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL" | |
# Have some internal (or external) DNS servers you want to allow unencrypted queries to? | |
# The following two examples show how you can add exceptions to the catch all for UDP and TCP | |
# NOTE: this will allow UNENCRYPTED queries to hit any IP(s) you add here as exceptions, so be careful! | |
# Allowing any external IPs here defeats the purpose of this script if you need to leave your internal network to reach them! | |
# You should specify each IP as a CIDR (just append /32), and you can add multiple, comma-separated, right after "-d" (destination) | |
# -- | |
# ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p udp -m udp -d 172.27.72.1/32,192.168.0.1/32 --dport 53 -j RETURN | |
# ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p tcp -m tcp -d 172.27.72.1/32,192.168.0.1/32 --dport 53 -j RETURN | |
ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.1:5342 | |
ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.1:5342 | |
ExecStart=/usr/bin/env bash -c "ip6tables -w 30 -t nat -N NEXTDNS_CATCHALL || ip6tables -w 30 -t nat -F NEXTDNS_CATCHALL" | |
ExecStart=/usr/bin/env bash -c "ip6tables -w 30 -t nat -C PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL >/dev/null 2>&1 || ip6tables -w 30 -t nat -A PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL" | |
ExecStart=/usr/bin/env bash -c "ip6tables -w 30 -t nat -C PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL >/dev/null 2>&1 || ip6tables -w 30 -t nat -A PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL" | |
ExecStart=ip6tables -w 30 -t nat -A NEXTDNS_CATCHALL -p udp -m udp --dport 53 -j DNAT --to-destination ::1:5342 | |
ExecStart=ip6tables -w 30 -t nat -A NEXTDNS_CATCHALL -p tcp -m tcp --dport 53 -j DNAT --to-destination ::1:5342 | |
ExecStop=iptables -w 30 -t nat -D PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=iptables -w 30 -t nat -D PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=-iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=-iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=iptables -w 30 -t nat -F NEXTDNS_CATCHALL | |
ExecStop=iptables -w 30 -t nat -X NEXTDNS_CATCHALL | |
ExecStop=ip6tables -w 30 -t nat -D PREROUTING -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=ip6tables -w 30 -t nat -D PREROUTING -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=-ip6tables -w 30 -t nat -D PREROUTING ! -d ::1 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=-ip6tables -w 30 -t nat -D PREROUTING ! -d ::1 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL | |
ExecStop=ip6tables -w 30 -t nat -F NEXTDNS_CATCHALL | |
ExecStop=ip6tables -w 30 -t nat -X NEXTDNS_CATCHALL |
@jasonwaters - I don’t know what the interface name is for the teleport server, but you’ll just need to add another route_localnet
for that particular VPN tunnel interface. Just edited the comments of the unit file with a couple examples. For my own setup, the first commented example has the default interface name (wgsrv1
) for the non-Teleport WireGuard VPN server.
You can list all interfaces with a command like ip a
on the UDM, looking for the interface name that holds the “gateway” IP of your VPN client subnet. Another option, if it’s WireGuard, is to run wg show | grep -E '^interface: '
. There are other ways to enumerate the device name, depending on which tunnel type it is.
Let me know what interface name you come up with, please, and I can update the examples.
@vt0r thanks for your help. I found two relevant interface names: tlprt0
and tlprt1
, however i was not able get it to work even with these two entries:
# VPN - Teleport
ExecStart=sysctl -w net.ipv4.conf.tlprt0.route_localnet=1
ExecStart=sysctl -w net.ipv4.conf.tlprt1.route_localnet=1
I went ahead and set up a wireguard vpn on my UDM Pro and configured it the same as you and it works great. I will just use that. No reason to stick with teleport TBH.
Hello,
I get a few errors from iptables / iptables6 when the scripts run on two different UDM-SEs.
Jan 30 10:31:02 systemd[1]: Starting NextDNS Catch all traffic...
Jan 30 10:31:02 sysctl[3946140]: net.ipv4.conf.br0.route_localnet = 1
Jan 30 10:31:02 env[3946144]: iptables: No chain/target/match by that name.
Jan 30 10:31:02 env[3946146]: iptables: No chain/target/match by that name.
Jan 30 10:31:02 env[3946152]: ip6tables: No chain/target/match by that name.
Jan 30 10:31:02 env[3946154]: ip6tables: No chain/target/match by that name.
Jan 30 10:31:02 systemd[1]: Finished NextDNS Catch all traffic.
What would I need to fix to correct these errors?
Thanks,
Braeden
@bslatyer - those get output when the iptables -C
command fails to detect a rule. The ||
(OR) will then run the second command on that line - to add the missing rule, which succeeds and outputs nothing. There are two instances of this for v4 and two for v6, but it makes it to the end, judging by the finished message at the bottom. You can safely ignore this if you see it looks good in systemctl status nextdns-catchall
@vt0r thanks legend!
Hey @vt0r,
One more question: when these rules are in effect - they block all Spilt Horizon Traffic between routers.
NextDNS is doing the Spilt Horizon in the configuration.
forwarder example1.com.=172.27.72.1
forwarder example2.com.=192.168.0.1
I have two routers connected by UniFi's Site Magic like this.
Router 1
LAN - 172.27.72.1/32
WG - 192.168.1.0/32
Router 2
LAN - 192.168.0.1/32
WG - 192.168.1.1/32
Can the rules be modified to allow DNS traffic to flow between these two routers while simultaneously catching the other traffic?
I've already had a few attempts at this and failed.
Thanks
Braeden
@bslatyer - In that case, I think you could change four lines (25, 26 + 34, 35) in the above nextdns-catchall.service
file to append the LAN and WG IPs as other destinations to ignore when deciding which ones to redirect, as shown in this diff:
--- nextdns-catchall.service.orig 2024-02-15 22:53:29.439936283 -0500
+++ nextdns-catchall.service 2024-02-15 22:56:35.445589293 -0500
@@ -1,5 +1,5 @@
-ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL"
-ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL"
+ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL"
+ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL"
...
-ExecStop=iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL
-ExecStop=iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL
+ExecStop=iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL
+ExecStop=iptables -w 30 -t nat -D PREROUTING ! -d 127.0.0.0/8,172.27.72.1/32,192.168.0.1/32 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL
This basically means editing any occurrence in those four rules of 127.0.0.1/8
to append ,172.27.72.1/32,192.168.0.1/32
to it, and so on and so forth, for each legitimate DNS server IP you need to allow direct access to. Only those four lines, though. No others. NOTE that this edit happens twice on lines 25/26, since those are each basically the same rule twice - first one checks (-C
) for its existence, and if not present, second adds (-A
) the rule to that chain.
I think doing just that should make it work for your use case, since that pair of rules we edit (and the matching deletions for the stop) do a negative (!
) destination (-d
) match, and they will only jump (-j
) to the NEXTDNS_CATCHALL
chain when the destination does not match the CIDRs given.
The best way to handle this in a "production" way is using ipset
and creating a set (usually type hash:net
), which you can fill with all CIDRs of valid destinations for DNS (127.0.0.1/8
, 172.27.72.1/32
, etc). You'd then point to that set by name, rather than giving the whole list of CIDRs in all those rules, and that would work using -m set --match-set <the name of the set you created>
, but for this small use case, editing four lines is probably ok. 😅
Hey @vt0r,
Thanks for that 😄
Doesn't seem to work...
Feb 16 18:15:26 Redwood systemd[1]: Starting NextDNS Catch all traffic...
Feb 16 18:15:26 Redwood sysctl[2689076]: net.ipv4.conf.br0.route_localnet = 1
Feb 16 18:15:26 Redwood env[2689080]: iptables v1.8.7 (legacy): ! not allowed with multiple source or destination IP addresses
Feb 16 18:15:26 Redwood env[2689080]: Try `iptables -h' or 'iptables --help' for more information.
Feb 16 18:15:26 Redwood env[2689079]: iptables v1.8.7 (legacy): ! not allowed with multiple source or destination IP addresses
Feb 16 18:15:26 Redwood env[2689079]: Try `iptables -h' or 'iptables --help' for more information.
Feb 16 18:15:26 Redwood systemd[1]: nextdns-catchall.service: Main process exited, code=exited, status=2/INVALIDARGUMENT
Feb 16 18:15:26 Redwood systemd[1]: nextdns-catchall.service: Failed with result 'exit-code'.
Feb 16 18:15:26 Redwood systemd[1]: Failed to start NextDNS Catch all traffic.
Ok, I tried to get fancy with the multi-destination thing. Guess it doesn't work with a negative match. Anyway, the fix for that should be easy. Just add some rules into the NEXTDNS_CATCHALL
chain before the redirect ones that will "short-circuit" the rest of the rules in the chain, by returning (RETURN
) back to the calling chain (PREROUTING
in this case), which skips evaluation of any further rules in the chain. Assuming you've completely reverted the nextdns-catchall.service
file to look like the one above, here's the diff for that:
--- nextdns-catchall.service.orig 2024-02-16 10:57:36.065678559 -0500
+++ nextdns-catchall.service 2024-02-16 11:01:56.416036146 -0500
@@ -24,6 +24,8 @@
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -N NEXTDNS_CATCHALL || iptables -w 30 -t nat -F NEXTDNS_CATCHALL"
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8 -m udp -p udp --dport 53 -j NEXTDNS_CATCHALL"
ExecStart=/usr/bin/env bash -c "iptables -w 30 -t nat -C PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL || iptables -w 30 -t nat -A PREROUTING ! -d 127.0.0.0/8 -m tcp -p tcp --dport 53 -j NEXTDNS_CATCHALL"
+ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p udp -m udp -d 172.27.72.1/32,192.168.0.1/32 --dport 53 -j RETURN
+ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p tcp -m tcp -d 172.27.72.1/32,192.168.0.1/32 --dport 53 -j RETURN
ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p udp -m udp --dport 53 -j DNAT --to-destination 127.0.0.1:53
ExecStart=iptables -w 30 -t nat -A NEXTDNS_CATCHALL -p tcp -m tcp --dport 53 -j DNAT --to-destination 127.0.0.1:53
ExecStart=/usr/bin/env bash -c "ip6tables -w 30 -t nat -N NEXTDNS_CATCHALL || ip6tables -w 30 -t nat -F NEXTDNS_CATCHALL"
It should work this time, since multi-destination stuff should work when not combined with the negative match. If not, we can always add two rules for each destination (one UDP, one TCP).
Oh, forgot to ping there. cc @bslatyer ^
If that works out for you, with or without the multi-dest stuff, I'll add commented examples to the original gist above, just in case anyone else wants to allow certain non-localhost DNS servers, as I'm sure you're not the only one with this use case. Might be time to convert this into a real git repo lol...
Made some changes to nextdns-catchall.service
again, and they should be fully backwards compatible. Here's the list:
- First, ensure it injects the symlinks (
WantedBy
) fornextdns.service
. This ensures systemd will try to start this after nextdns starts - Next, replace rules that ignore DNS traffic destined for
127.0.0.1/8
(like dnsmasq) with less specific ones that just match UDP/TCP on port 53. This goes back to circumventing the built-in dnsmasq server, as the nextdns-injected config can potentially get ignored. This has the added benefit of also redirecting some queries sent by the UDM itself - though not all - Also, we added
>/dev/null 2>&1
to silence error output from the firstiptables -C
commands that check for existence of a specific rule. It doesn't need to be printing a failure every time, as that's misleading. The command we run after the OR (||
) does succeed and outputs nothing, so we stop that here. - Finally, clean up the old rules that ignore
127.0.0.1/8
in PREROUTING tables, since the rule format changed a bit in the latest edit. These ExecStop commands are prefixed with-
, as they're expected to fail after the first time, and we don't want those expected failures to impact the stop of our service.
I'd recommend anyone using this to update. Before applying the updates, you can run systemctl stop nextdns-catchall.service
if you want to manually clean up rules first. If you do not do this stop step first, then after updating /data/nextdns-catchall.service
and running /data/on_boot.d/10-nextdns-catchall.sh
, you may want to run a systemctl restart nextdns-catchall.service
(once) just for good measure to make sure that extra cleanup happens, if necessary. This will only need to happen once on this change, due to the way the rules added to (and removed from on stop) the PREROUTING
tables changed slightly.
Also, I'm going to move this whole thing into a real git repo and will share the link here when done. This ensures we can have real issue tracking, pull requests, etc.
EDIT - done. Added a README file to the top of the list that points to the new repository. You can find it here:
@vt0r Thanks for this, works very well. I noticed DNS is not resolving when I use wifiman teleport to VPN into my network. Any ideas how it could be supported?