74f5fb5ac7
Currently there is just one anti-spoofing firewall rule ensuring packets coming through vif+ interfaces have the right source address. Add another rule ensuring that addresses that belongs to VMs behind those vif+ interface do not appear on other interfaces (specifically eth0, but also physical ones). Normally it wouldn't be an issue because of rp_filter (doing the same based on route table), default DROP in FORWARD chain and also conntrack (the need to guess exact port numbers and sequence numbers). But it appears all three mechanisms are ineffective in some cases: - rp_filter in many distributions (including Fedora and Debian) was switched to Loose Mode, which doesn't verify exact interface - there is a rule in FORWARD table allowing established connections and conntrack does not keep track of input/output interfaces - CVE-2019-14899 allows to guess all the data needed to inject packets Reported-by: Demi M. Obenour <demiobenour@gmail.com>
176 lines
5.6 KiB
Bash
Executable File
176 lines
5.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#============================================================================
|
|
# /etc/xen/vif-route-qubes
|
|
#
|
|
# Script for configuring a vif in routed mode.
|
|
# The hotplugging system will call this script if it is specified either in
|
|
# the device configuration given to Xend, or the default Xend configuration
|
|
# in /etc/xen/xend-config.sxp. If the script is specified in neither of those
|
|
# places, then vif-bridge is the default.
|
|
#
|
|
# Usage:
|
|
# vif-route (add|remove|online|offline)
|
|
#
|
|
# Environment vars:
|
|
# vif vif interface name (required).
|
|
# XENBUS_PATH path to this device's details in the XenStore (required).
|
|
#
|
|
# Read from the store:
|
|
# ip list of IP networks for the vif, space-separated (default given in
|
|
# this script).
|
|
#============================================================================
|
|
|
|
dir=$(dirname "$0")
|
|
# shellcheck disable=SC1091,SC1090
|
|
. "$dir/vif-common.sh"
|
|
|
|
set -o pipefail
|
|
#main_ip=$(dom0_ip)
|
|
|
|
# Network Hooks for triggering supplementary actions on AppVM connect
|
|
network_hooks() {
|
|
local command="$1"
|
|
local vif="$2"
|
|
local addr="$3"
|
|
|
|
local vif_type
|
|
vif_type="$(xenstore-read "${XENBUS_PATH}/type")"
|
|
|
|
if [ -d /rw/config/network-hooks.d ]; then
|
|
for hook in /rw/config/network-hooks.d/*
|
|
do
|
|
if [ -x "$hook" ]; then
|
|
log debug "Executing network-hook $(basename "$hook")..."
|
|
do_without_error "${hook}" "${command}" "${vif}" "${vif_type}" "${addr}"
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
ipt_arg=
|
|
if "iptables-restore" --help 2>&1 | grep -q wait=; then
|
|
# 'wait' must be last on command line if secs not specified
|
|
ipt_arg=--wait
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
if [ "${ip}" ]; then
|
|
# get first IPv4 and first IPv6
|
|
for addr in ${ip}; do
|
|
if [ -z "$ip4" ] && [[ "$addr" = *.* ]]; then
|
|
ip4="$addr"
|
|
elif [ -z "$ip6" ] && [[ "$addr" = *:* ]]; then
|
|
ip6="$addr"
|
|
fi
|
|
done
|
|
# IPs as seen by this VM
|
|
netvm_ip="$ip4"
|
|
netvm_gw_ip=$(qubesdb-read /qubes-netvm-gateway)
|
|
netvm_gw_ip6=$(qubesdb-read /qubes-netvm-gateway6 || :)
|
|
netvm_dns1_ip=$(qubesdb-read /qubes-netvm-primary-dns)
|
|
netvm_dns2_ip=$(qubesdb-read /qubes-netvm-secondary-dns)
|
|
|
|
back_ip="$netvm_gw_ip"
|
|
back_ip6="$netvm_gw_ip6"
|
|
|
|
# IPs as seen by the VM - if other than $netvm_ip
|
|
appvm_gw_ip="$(qubesdb-read "/mapped-ip/$ip4/visible-gateway" 2>/dev/null || :)"
|
|
appvm_ip="$(qubesdb-read "/mapped-ip/$ip4/visible-ip" 2>/dev/null || :)"
|
|
fi
|
|
|
|
# Apply NAT if IP visible from the VM is different than the "real" one
|
|
# See vif-qubes-nat.sh for details
|
|
# XXX: supported only for the first IPv4 address, IPv6 is dropped if this
|
|
# feature is enabled
|
|
if [ -n "$appvm_ip" ] && [ -n "$appvm_gw_ip" ] && [ "$appvm_ip" != "$netvm_ip" ]; then
|
|
# shellcheck disable=SC2154
|
|
if test "$command" == online; then
|
|
# shellcheck disable=SC2154
|
|
echo 1 >"/proc/sys/net/ipv4/conf/${vif}/proxy_arp"
|
|
fi
|
|
|
|
# shellcheck source=network/vif-qubes-nat.sh
|
|
. "$dir/vif-qubes-nat.sh"
|
|
fi
|
|
|
|
# shellcheck disable=SC2154
|
|
case "$command" in
|
|
online)
|
|
echo 1 >"/proc/sys/net/ipv4/conf/${vif}/proxy_arp"
|
|
ipcmd='add'
|
|
iptables_cmd='-I PREROUTING 1'
|
|
cmdprefix=''
|
|
ipv6_disabled=$(cat /proc/sys/net/ipv6/conf/"${vif}"/disable_ipv6 || echo 1)
|
|
;;
|
|
offline)
|
|
do_without_error ifdown "${vif}"
|
|
ipcmd='del'
|
|
iptables_cmd='-D PREROUTING'
|
|
cmdprefix='do_without_error'
|
|
# cleanup IPv6 config even if _now_ it is disabled
|
|
ipv6_disabled=0
|
|
;;
|
|
esac
|
|
|
|
domid=${vif/vif/}
|
|
domid=${domid/.*/}
|
|
# metric must be possitive, but prefer later interface
|
|
# 32752 is max XID aka domid
|
|
metric=$(( 32752 - domid ))
|
|
|
|
# add anti-spoofing rules before enabling the interface
|
|
if [ "${ip}" ]; then
|
|
# If we've been given a list of IP addresses, then add routes from dom0 to
|
|
# the guest using those addresses.
|
|
for addr in ${ip};
|
|
do
|
|
if [[ "$addr" = *:* ]]; then
|
|
ipt=ip6tables-restore
|
|
else
|
|
ipt=iptables-restore
|
|
fi
|
|
printf '%s\n' "*raw" \
|
|
"$iptables_cmd -i ${vif} ! -s ${addr} -j DROP" \
|
|
"$iptables_cmd ! -i vif+ -s ${addr} -j DROP" \
|
|
"COMMIT" | \
|
|
${cmdprefix} $ipt --noflush $ipt_arg
|
|
done
|
|
# if no IPv6 is assigned, block all IPv6 traffic on that interface
|
|
if ! [[ "$ip" = *:* ]]; then
|
|
echo -e "*raw\\n$iptables_cmd -i ${vif} -j DROP\\nCOMMIT" | \
|
|
${cmdprefix} ip6tables-restore --noflush $ipt_arg
|
|
fi
|
|
fi
|
|
|
|
if [ "$command" = "online" ]; then
|
|
ifconfig "${vif}" up
|
|
fi
|
|
|
|
if [ "${ip}" ]; then
|
|
# If we've been given a list of IP addresses, then add routes from dom0 to
|
|
# the guest using those addresses.
|
|
for addr in ${ip};
|
|
do
|
|
if [[ "$addr" = *:* ]] && [[ "$ipv6_disabled" = 1 ]]; then
|
|
log error "Cannot set IPv6 route to ${addr}, IPv6 disabled in the kernel"
|
|
continue
|
|
fi
|
|
${cmdprefix} ip route "${ipcmd}" "${addr}" dev "${vif}" metric "$metric"
|
|
|
|
network_hooks "${command}" "${vif}" "${addr}"
|
|
done
|
|
${cmdprefix} ip addr "${ipcmd}" "${back_ip}/32" dev "${vif}"
|
|
if [ "${back_ip6}" ] && [[ "${back_ip6}" != "fe80:"* ]] && [[ "$ipv6_disabled" != 1 ]]; then
|
|
${cmdprefix} ip addr "${ipcmd}" "${back_ip6}/128" dev "${vif}"
|
|
fi
|
|
else
|
|
network_hooks "${command}" "${vif}"
|
|
fi
|
|
|
|
log debug "Successful vif-route-qubes $command for $vif."
|
|
if [ "$command" = "online" ]; then
|
|
# disable tx checksumming offload, apparently it doesn't work with our ancient qemu in stubdom
|
|
do_without_error ethtool -K "$vif" tx off
|
|
success
|
|
fi
|