Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IPv6 Virtual DNS #462

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
74 changes: 64 additions & 10 deletions network/qubes-setup-dnat-to-ns
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ from __future__ import annotations
import dbus
import qubesdb
from typing import List
from ipaddress import IPv4Address
from itertools import zip_longest
from ipaddress import ip_address, IPv4Address, IPv6Address
import os

def get_dns_resolv_conf():
Expand All @@ -38,7 +39,7 @@ def get_dns_resolv_conf():
if len(tokens) < 2 or tokens[0] != "nameserver":
continue
try:
nameservers.append(IPv4Address(tokens[1]))
nameservers.append(ip_address(tokens[1]))
except ValueError:
pass
return nameservers
Expand Down Expand Up @@ -69,13 +70,13 @@ def get_dns_resolved():
raise
# Use global entries first
dns.sort(key=lambda x: x[0] != 0)
# Only keep IPv4 entries. systemd-resolved is trusted to return valid
# addresses.
return [IPv4Address(bytes(addr)) for _g, family, addr in dns if family == 2]
# systemd-resolved is trusted to return valid addresses.
return [ip_address(bytes(addr)) for _g, family, addr in dns]

def install_firewall_rules(dns):
qdb = qubesdb.QubesDB()
qubesdb_dns = []
qubesdb_dns6 = []
for i in ('/qubes-netvm-primary-dns', '/qubes-netvm-secondary-dns'):
ns_maybe = qdb.read(i)
if ns_maybe is None:
Expand All @@ -84,6 +85,14 @@ def install_firewall_rules(dns):
qubesdb_dns.append(IPv4Address(ns_maybe.decode("ascii", "strict")))
except (UnicodeDecodeError, ValueError):
pass
for i in ('/qubes-netvm-primary-dns6', '/qubes-netvm-secondary-dns6'):
ns_maybe = qdb.read(i)
if ns_maybe is None:
continue
try:
qubesdb_dns6.append(IPv6Address(ns_maybe.decode("ascii", "strict")))
except (UnicodeDecodeError, ValueError):
pass
res = [
'add table ip qubes',
# Add the chain so that the subsequent delete will work. If the chain already
Expand All @@ -95,15 +104,60 @@ def install_firewall_rules(dns):
# where both are present.
'delete chain ip qubes dnat-dns',
'table ip qubes {',
'chain custom-dnat-dns {}',
'chain dnat-dns {',
'type nat hook prerouting priority dstnat; policy accept;',
'jump custom-dnat-dns',
]
for i, (vm_nameserver, dest) in enumerate(zip_longest(qubesdb_dns,
[dns_ip for dns_ip in dns if type(dns_ip) is IPv4Address])):
if i >= len(qubesdb_dns):
break
dns_ = str(dest)
if dest is None or (vm_nameserver == dest and
qdb.read('/qubes-ip') is None):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
qdb.read('/qubes-ip') is None):
qdb.read('/qubes-primary-dns') is None):

for consistency?

res += [
f"ip daddr {vm_nameserver} tcp dport 53 reject with icmp type host-unreachable",
f"ip daddr {vm_nameserver} udp dport 53 reject with icmp type host-unreachable",
]
else:
res += [
f"ip daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
f"ip daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
]
res += ["}\n}\n"]
res += [
'add table ip6 qubes',
# Add the chain so that the subsequent delete will work. If the chain already
# exists this is a harmless no-op.
'add chain ip6 qubes dnat-dns',
# Delete the chain so that if the chain already exists, it will be removed.
# The removal of the old chain and addition of the new one happen as a single
# atomic operation, so there is no period where neither chain is present or
# where both are present.
'delete chain ip6 qubes dnat-dns',
'table ip6 qubes {',
'chain custom-dnat-dns {}',
'chain dnat-dns {',
'type nat hook prerouting priority dstnat; policy accept;',
'jump custom-dnat-dns',
]
for vm_nameserver, dest in zip(qubesdb_dns, get_dns_resolved()):
for i, (vm_nameserver, dest) in enumerate(zip_longest(qubesdb_dns6,
[dns_ip for dns_ip in dns if type(dns_ip) is IPv6Address])):
if i >= len(qubesdb_dns6):
break
dns_ = str(dest)
res += [
f"ip daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
f"ip daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
]
if dest is None or (vm_nameserver == dest and
qdb.read('/qubes-primary-dns6') is None):
res += [
f"ip6 daddr {vm_nameserver} tcp dport 53 reject with icmpv6 type addr-unreachable",
f"ip6 daddr {vm_nameserver} udp dport 53 reject with icmpv6 type addr-unreachable",
]
else:
res += [
f"ip6 daddr {vm_nameserver} udp dport 53 dnat to {dns_}",
f"ip6 daddr {vm_nameserver} tcp dport 53 dnat to {dns_}",
]
res += ["}\n}\n"]
os.execvp("nft", ("nft", "--", "\n".join(res)))

Expand Down
46 changes: 37 additions & 9 deletions network/setup-ip
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ configure_network () {
local gateway6="$8"
local primary_dns="$9"
local secondary_dns="${10}"
local custom="${11}"
local primary_dns6="${11}"
local secondary_dns6="${12}"
local custom="${13}"

/sbin/ip -- address replace "$ip/$netmask" dev "$INTERFACE"
if [[ "$custom" = false ]]; then
Expand Down Expand Up @@ -64,16 +66,21 @@ configure_network () {
if [ -h /etc/resolv.conf ]; then
rm -f /etc/resolv.conf
fi
echo > /etc/resolv.conf
echo -n > /etc/resolv.conf
if ! qsvc disable-dns-server ; then
echo "nameserver $primary_dns" > /etc/resolv.conf
if [ -n "$primary_dns6" ]; then
echo "nameserver $primary_dns6" >> /etc/resolv.conf
echo "nameserver $secondary_dns6" >> /etc/resolv.conf
fi
echo "nameserver $primary_dns" >> /etc/resolv.conf
echo "nameserver $secondary_dns" >> /etc/resolv.conf
fi
fi
if [ -x /usr/bin/resolvectl ] && \
systemctl is-enabled -q systemd-resolved.service && \
! qsvc disable-dns-server ; then
resolvectl dns "$INTERFACE" "$primary_dns" "$secondary_dns"
resolvectl dns "$INTERFACE" "$primary_dns6" "$secondary_dns6" \
"$primary_dns" "$secondary_dns"
fi
}

Expand All @@ -88,7 +95,9 @@ configure_network_nm () {
local gateway6="$8"
local primary_dns="$9"
local secondary_dns="${10}"
local custom="${11}"
local primary_dns6="${11}"
local secondary_dns6="${12}"
local custom="${13}"

local prefix
local prefix6
Expand Down Expand Up @@ -119,6 +128,10 @@ __EOF__
if ! qsvc disable-dns-server ; then
ip4_nm_config="${ip4_nm_config}
dns=${primary_dns};${secondary_dns}"
if [ -n "$primary_dns6" ]; then
ip6_nm_config="${ip6_nm_config}
dns=${primary_dns6};${secondary_dns6}"
fi
fi
if ! qsvc disable-default-route ; then
ip4_nm_config="${ip4_nm_config}
Expand Down Expand Up @@ -183,8 +196,21 @@ configure_qubes_ns() {
#netmask=$(qubesdb-read /qubes-netvm-netmask)
primary_dns=$(qubesdb-read /qubes-netvm-primary-dns 2>/dev/null || echo "$gateway")
secondary_dns=$(qubesdb-read /qubes-netvm-secondary-dns)
echo "NS1=$primary_dns" > /var/run/qubes/qubes-ns
echo "NS2=$secondary_dns" >> /var/run/qubes/qubes-ns
primary_dns6=$(qubesdb-read /qubes-netvm-primary-dns6 ||:)
secondary_dns6=$(qubesdb-read /qubes-netvm-secondary-dns6 ||:)
if [ -n "$primary_dns6" ]; then
cat > /var/run/qubes/qubes-ns<< EOF
NS1=$primary_dns6
NS1=$secondary_dns6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NS1=$secondary_dns6
NS2=$secondary_dns6

NS3=$primary_dns
NS4=$secondary_dns
EOF
else
cat > /var/run/qubes/qubes-ns<< EOF
NS1=$primary_dns
NS1=$secondary_dns
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NS1=$secondary_dns
NS2=$secondary_dns

EOF
fi
/usr/lib/qubes/qubes-setup-dnat-to-ns
}

Expand Down Expand Up @@ -244,6 +270,8 @@ if [ "$ACTION" == "add" ]; then

primary_dns=$(/usr/bin/qubesdb-read /qubes-primary-dns 2>/dev/null) || primary_dns=
secondary_dns=$(/usr/bin/qubesdb-read /qubes-secondary-dns 2>/dev/null) || secondary_dns=
primary_dns6=$(/usr/bin/qubesdb-read /qubes-primary-dns6 2>/dev/null ||:) || primary_dns6=
secondary_dns6=$(/usr/bin/qubesdb-read /qubes-secondary-dns6 2>/dev/null ||:) || secondary_dns6=
/usr/lib/systemd/systemd-sysctl \
"--prefix=/net/ipv4/conf/all" \
"--prefix=/net/ipv4/neigh/all" \
Expand All @@ -257,9 +285,9 @@ if [ "$ACTION" == "add" ]; then
if [ -n "$ip4" ]; then
# If NetworkManager is enabled, let it configure the network
if qsvc network-manager && [ -e /usr/bin/nmcli ]; then
configure_network_nm "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom"
configure_network_nm "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$primary_dns6" "$secondary_dns6" "$custom"
else
configure_network "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$custom"
configure_network "$MAC" "$INTERFACE" "$ip4" "$ip6" "$netmask" "$netmask6" "$gateway" "$gateway6" "$primary_dns" "$secondary_dns" "$primary_dns6" "$secondary_dns6" "$custom"
fi

network=$(qubesdb-read /qubes-netvm-network 2>/dev/null) || network=
Expand Down
2 changes: 2 additions & 0 deletions qubes-rpc/post-install.d/10-qubes-core-agent-features.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ advertise_systemd_service() {
done
}

qvm-features-request supported-feature.ipv6dns=1

advertise_systemd_service network-manager NetworkManager.service \
network-manager.service
advertise_systemd_service modem-manager ModemManager.service
Expand Down
17 changes: 15 additions & 2 deletions vm-systemd/network-proxy-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,22 @@ if [ "x$network" != "x" ]; then
#netmask=$(qubesdb-read /qubes-netvm-netmask)
primary_dns=$(qubesdb-read /qubes-netvm-primary-dns 2>/dev/null || echo "$gateway")
secondary_dns=$(qubesdb-read /qubes-netvm-secondary-dns)
primary_dns6=$(qubesdb-read /qubes-netvm-primary-dns6 ||:)
secondary_dns6=$(qubesdb-read /qubes-netvm-secondary-dns6 ||:)
modprobe netbk 2> /dev/null || modprobe xen-netback || "${modprobe_fail_cmd}"
echo "NS1=$primary_dns" > /var/run/qubes/qubes-ns
echo "NS2=$secondary_dns" >> /var/run/qubes/qubes-ns
if [ -n "$primary_dns6" ]; then
cat > /var/run/qubes/qubes-ns<< EOF
NS1=$primary_dns6
NS1=$secondary_dns6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NS1=$secondary_dns6
NS2=$secondary_dns6

NS3=$primary_dns
NS4=$secondary_dns
EOF
else
cat > /var/run/qubes/qubes-ns<< EOF
NS1=$primary_dns
NS1=$secondary_dns
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NS1=$secondary_dns
NS2=$secondary_dns

EOF
fi
/usr/lib/qubes/qubes-setup-dnat-to-ns
echo "1" > /proc/sys/net/ipv4/ip_forward
# enable also IPv6 forwarding, if IPv6 is enabled
Expand Down