I converted @qubist’s iptables rules to support Qubes 4.2 nftables. I did some quick tests and it seems to work, tell me if something is not working properly.
sys-dns
#!/bin/sh
# This script will be executed at every VM startup, you can place your own
# custom commands here. This includes overriding some configuration in /etc,
# starting services etc.
nft='/usr/sbin/nft'
# allow redirects to localhost
/usr/sbin/sysctl -w net.ipv4.conf.all.route_localnet=1
"${nft}" add rule ip qubes custom-input meta l4proto { tcp, udp } iifgroup 2 ip daddr 127.0.0.1 th dport 53 accept
# block connections to other DNS servers
"${nft}" add rule ip qubes custom-forward meta l4proto { tcp, udp } iifgroup 2 ip daddr != 127.0.0.1 th dport 53 drop
"${nft}" flush chain ip qubes dnat-dns
"${nft}" add rule ip qubes dnat-dns meta l4proto { tcp, udp } th dport 53 dnat to 127.0.0.1
echo 'nameserver 127.0.0.1' > /etc/resolv.conf
# https://github.com/DNSCrypt/dnscrypt-proxy/wiki/Installation-linux
# https://wiki.archlinux.org/title/Dnscrypt-proxy#Enable_EDNS0
echo 'options edns0' >> /etc/resolv.conf
ln -s /rw/dnscrypt-proxy /etc/dnscrypt-proxy
/usr/bin/systemctl start dnscrypt-proxy.service
sys-wall
#!/bin/sh
# This script will be executed at every VM startup, you can place your own
# custom commands here. This includes overriding some configuration in /etc,
# starting services etc.
nft='/usr/sbin/nft'
# redirect all dns-requests to sys-dns
"${nft}" flush chain ip qubes dnat-dns
"${nft}" add rule ip qubes dnat-dns meta l4proto { tcp, udp } ip daddr { 10.139.1.1, 10.139.1.2 } th dport 53 dnat to $(/usr/bin/qubesdb-read /qubes-gateway)
# block connections to other DNS servers
"${nft}" add rule ip qubes dnat-dns meta l4proto { tcp, udp } th dport 53 drop