Domain Based IPSet from DNS Response

Hi,

I was experimenting with ipset functionality of dnsmasq with VyOS, this dnsmasq 's IPSet feature adds the resolved IP addresses of queries for one or more domains in the specified Netfilter IP set.

I had success with such implementation on VyOS with few changes in an image. But its just PoC and not perfect solution with the existing VyOS configuration framework. I am sharing my implementation in this thread and would like to contribute to the community after complete rework as per the VyOS configuration format.

As VyOS uses pdns-recursor for DNS service which already has hook for Lua script that can be used for DNS response interception, Here is the reference PDNS Lua Doc

Implementation.

Note: Create IPSet

set firewall group address-group domain_routes

  1. Created an ISO with a lua package (lua5.2)
  2. Added an option in to existing dns configuration generator python script (/usr/libexec/vyos/conf_mode/dns_forwarding.py) and added a Lua hook (lua-dns-script=/usr/local/bin/pdns_postresolve.lua)
  3. Contents of /usr/local/bin/pdns_postresolve.lua

!#/usr/bin/lua

function postresolve(dq)
local records = dq:getRecords()
for k,v in pairs(records) do
if v.type == pdns.A then
os.execute("/usr/local/bin/pdns_dns_route.sh -q " … dq.qname:toString() … " -r " … v:getContent())
end
end
return true
end

  1. Contents of /usr/local/bin/pdns_dns_route.sh

#!/bin/sh

domain_routes=“/config/var/domainRoutes”

[ ! -f “$domain_routes” ] && exit 0

usage_help()
{
echo “$0: -q -r ”
}

if [ $# -eq 0 ]; then
usage_help
exit 1
fi

while getopts q:r:h opt; do
case “$opt” in
q) _domain=${OPTARG::-1} ;;
r) _address=$OPTARG ;;
h) usage_help; exit 0;;
*) usage_help; exit 1;;
esac
done

echo “$_domain” | grep -q -E -f “$domain_routes”
[ $? -ne 0 ] && exit 1

[ -z “$_address” ] && exit 1
ipv4calc --address “$_address” > /dev/null 2>&1 || exit 1

ipset add domain_routes “$_address”

  1. Here, I was facing issues adding resolved IP to IPSet because of the limited capabilities of pdns-recursor into systemd unit file. For that, I had to add the capability of CAP_NET_ADMIN

Content of /lib/systemd/system/pdns-recursor.service

[Unit]
Description=PowerDNS Recursor
Documentation=man:pdns_recursor(1) man:rec_control(1)
Documentation=https://doc.powerdns.com
Wants=network-online.target nss-lookup.target
Before=nss-lookup.target
After=network-online.target

[Service]
Type=notify
ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no
Restart=on-failure
StartLimitInterval=0
PrivateTmp=true
PrivateDevices=true
CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID CAP_CHOWN CAP_SYS_CHROOT CAP_NET_ADMIN
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
LimitNOFILE=4200

[Install]
WantedBy=multi-user.target

  1. Contents of /config/var/domainRoutes

*.google.com
www.microsoft.com
www.facebook.com
www.cisco.com

After these changes, I was able to get resolved IP Address in IPSet.