qubes-firewall: add anti-spoofing rules for connected machines
qubes-firewall will now blacklist IP addresses from all connected machines on non-vif* interfaces. This prevents spoofing source or target address on packets going over an upstream link, even if a VM in question is powered off at the moment. Depends on QubesOS/qubes-core-admin#303 which makes admin maintain the list of IPs in qubesdb. Fixes QubesOS/qubes-issues#5540.
This commit is contained in:
parent
cc68f165bc
commit
bfe31cfec8
@ -1,4 +1,10 @@
|
|||||||
# Generated by ip6tables-save v1.4.14 on Tue Sep 25 16:00:20 2012
|
# Generated by ip6tables-save v1.4.14 on Tue Sep 25 16:00:20 2012
|
||||||
|
*mangle
|
||||||
|
:QBS-PREROUTING - [0:0]
|
||||||
|
:QBS-POSTROUTING - [0:0]
|
||||||
|
-A PREROUTING -j QBS-PREROUTING
|
||||||
|
-A POSTROUTING -j QBS-POSTROUTING
|
||||||
|
COMMIT
|
||||||
*filter
|
*filter
|
||||||
:INPUT DROP [0:0]
|
:INPUT DROP [0:0]
|
||||||
:FORWARD DROP [0:0]
|
:FORWARD DROP [0:0]
|
||||||
|
@ -10,6 +10,12 @@
|
|||||||
-A POSTROUTING -o lo -j ACCEPT
|
-A POSTROUTING -o lo -j ACCEPT
|
||||||
-A POSTROUTING -j MASQUERADE
|
-A POSTROUTING -j MASQUERADE
|
||||||
COMMIT
|
COMMIT
|
||||||
|
*mangle
|
||||||
|
:QBS-PREROUTING - [0:0]
|
||||||
|
:QBS-POSTROUTING - [0:0
|
||||||
|
-A PREROUTING -j QBS-PREROUTING
|
||||||
|
-A POSTROUTING -j QBS-POSTROUTING
|
||||||
|
COMMIT
|
||||||
*filter
|
*filter
|
||||||
:INPUT DROP [0:0]
|
:INPUT DROP [0:0]
|
||||||
:FORWARD DROP [0:0]
|
:FORWARD DROP [0:0]
|
||||||
|
@ -11,6 +11,12 @@
|
|||||||
-A POSTROUTING -o lo -j ACCEPT
|
-A POSTROUTING -o lo -j ACCEPT
|
||||||
-A POSTROUTING -j MASQUERADE
|
-A POSTROUTING -j MASQUERADE
|
||||||
COMMIT
|
COMMIT
|
||||||
|
*mangle
|
||||||
|
:QBS-PREROUTING - [0:0]
|
||||||
|
:QBS-POSTROUTING - [0:0
|
||||||
|
-A PREROUTING -j QBS-PREROUTING
|
||||||
|
-A POSTROUTING -j QBS-POSTROUTING
|
||||||
|
COMMIT
|
||||||
# Completed on Mon Sep 6 08:57:46 2010
|
# Completed on Mon Sep 6 08:57:46 2010
|
||||||
# Generated by iptables-save v1.4.5 on Mon Sep 6 08:57:46 2010
|
# Generated by iptables-save v1.4.5 on Mon Sep 6 08:57:46 2010
|
||||||
*filter
|
*filter
|
||||||
|
@ -77,6 +77,15 @@ class FirewallWorker(object):
|
|||||||
"""Apply rules in given source address"""
|
"""Apply rules in given source address"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def update_connected_ips(self, family):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_connected_ips(self, family):
|
||||||
|
if family == 6:
|
||||||
|
return self.qdb.read('/connected-ips6').split()
|
||||||
|
else:
|
||||||
|
return self.qdb.read('/connected-ips').split()
|
||||||
|
|
||||||
def run_firewall_dir(self):
|
def run_firewall_dir(self):
|
||||||
"""Run scripts dir contents, before user script"""
|
"""Run scripts dir contents, before user script"""
|
||||||
script_dir_paths = ['/etc/qubes/qubes-firewall.d',
|
script_dir_paths = ['/etc/qubes/qubes-firewall.d',
|
||||||
@ -175,15 +184,25 @@ class FirewallWorker(object):
|
|||||||
# initial load
|
# initial load
|
||||||
for source_addr in self.list_targets():
|
for source_addr in self.list_targets():
|
||||||
self.handle_addr(source_addr)
|
self.handle_addr(source_addr)
|
||||||
|
self.update_connected_ips(4)
|
||||||
|
self.update_connected_ips(6)
|
||||||
self.qdb.watch('/qubes-firewall/')
|
self.qdb.watch('/qubes-firewall/')
|
||||||
|
self.qdb.watch('/connected-ips')
|
||||||
|
self.qdb.watch('/connected-ips6')
|
||||||
try:
|
try:
|
||||||
for watch_path in iter(self.qdb.read_watch, None):
|
for watch_path in iter(self.qdb.read_watch, None):
|
||||||
|
if watch_path == '/connected-ips':
|
||||||
|
self.update_connected_ips(4)
|
||||||
|
|
||||||
|
if watch_path == '/connected-ips6':
|
||||||
|
self.update_connected_ips(6)
|
||||||
|
|
||||||
# ignore writing rules itself - wait for final write at
|
# ignore writing rules itself - wait for final write at
|
||||||
# source_addr level empty write (/qubes-firewall/SOURCE_ADDR)
|
# source_addr level empty write (/qubes-firewall/SOURCE_ADDR)
|
||||||
if watch_path.count('/') > 2:
|
if watch_path.startswith('/qubes-firewall/') and watch_path.count('/') == 2:
|
||||||
continue
|
source_addr = watch_path.split('/')[2]
|
||||||
source_addr = watch_path.split('/')[2]
|
self.handle_addr(source_addr)
|
||||||
self.handle_addr(source_addr)
|
|
||||||
except OSError: # EINTR
|
except OSError: # EINTR
|
||||||
# signal received, don't continue the loop
|
# signal received, don't continue the loop
|
||||||
pass
|
pass
|
||||||
@ -391,25 +410,49 @@ class IptablesWorker(FirewallWorker):
|
|||||||
else:
|
else:
|
||||||
self.apply_rules_family(source, rules, 4)
|
self.apply_rules_family(source, rules, 4)
|
||||||
|
|
||||||
|
def update_connected_ips(self, family):
|
||||||
|
self.run_ipt(family, ['-t', 'mangle', '-F', 'QBS-PREROUTING'])
|
||||||
|
self.run_ipt(family, ['-t', 'mangle', '-F', 'QBS-POSTROUTING'])
|
||||||
|
|
||||||
|
for ip in self.get_connected_ips(family):
|
||||||
|
self.run_ipt(family, [
|
||||||
|
'-t', 'mangle', '-A', 'QBS-PREROUTING',
|
||||||
|
'!', '-i', 'vif+', '-s', ip, '-j', 'DROP'])
|
||||||
|
self.run_ipt(family, [
|
||||||
|
'-t', 'mangle', '-A', 'QBS-POSTROUTING',
|
||||||
|
'!', '-o', 'vif+', '-d', ip, '-j', 'DROP'])
|
||||||
|
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
# make sure 'QBS_FORWARD' chain exists - should be created before
|
# Chains QBS-FORWARD, QBS-PREROUTING, QBS-POSTROUTING
|
||||||
# starting qubes-firewall
|
# need to be created before running this.
|
||||||
try:
|
try:
|
||||||
self.run_ipt(4, ['-F', 'QBS-FORWARD'])
|
self.run_ipt(4, ['-F', 'QBS-FORWARD'])
|
||||||
self.run_ipt(4,
|
self.run_ipt(4,
|
||||||
['-A', 'QBS-FORWARD', '!', '-i', 'vif+', '-j', 'RETURN'])
|
['-A', 'QBS-FORWARD', '!', '-i', 'vif+', '-j', 'RETURN'])
|
||||||
self.run_ipt(4, ['-A', 'QBS-FORWARD', '-j', 'DROP'])
|
self.run_ipt(4, ['-A', 'QBS-FORWARD', '-j', 'DROP'])
|
||||||
|
self.run_ipt(4, ['-t', 'mangle', '-F', 'QBS-PREROUTING'])
|
||||||
|
self.run_ipt(4, ['-t', 'mangle', '-F', 'QBS-POSTROUTING'])
|
||||||
|
|
||||||
self.run_ipt(6, ['-F', 'QBS-FORWARD'])
|
self.run_ipt(6, ['-F', 'QBS-FORWARD'])
|
||||||
self.run_ipt(6,
|
self.run_ipt(6,
|
||||||
['-A', 'QBS-FORWARD', '!', '-i', 'vif+', '-j', 'RETURN'])
|
['-A', 'QBS-FORWARD', '!', '-i', 'vif+', '-j', 'RETURN'])
|
||||||
self.run_ipt(6, ['-A', 'QBS-FORWARD', '-j', 'DROP'])
|
self.run_ipt(6, ['-A', 'QBS-FORWARD', '-j', 'DROP'])
|
||||||
|
self.run_ipt(6, ['-t', 'mangle', '-F', 'QBS-PREROUTING'])
|
||||||
|
self.run_ipt(6, ['-t', 'mangle', '-F', 'QBS-POSTROUTING'])
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
self.log_error('\'QBS-FORWARD\' chain not found, create it first')
|
self.log_error(
|
||||||
|
'Error initializing iptables. '
|
||||||
|
'You probably need to create QBS-FORWARD, QBS-PREROUTING and '
|
||||||
|
'QBS-POSTROUTING chains first.'
|
||||||
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
for family in (4, 6):
|
for family in (4, 6):
|
||||||
self.run_ipt(family, ['-F', 'QBS-FORWARD'])
|
self.run_ipt(family, ['-F', 'QBS-FORWARD'])
|
||||||
|
self.run_ipt(family, ['-t', 'mangle', '-F', 'QBS-PREROUTING'])
|
||||||
|
self.run_ipt(family, ['-t', 'mangle', '-F', 'QBS-POSTROUTING'])
|
||||||
for chain in self.chains[family]:
|
for chain in self.chains[family]:
|
||||||
self.run_ipt(family, ['-F', chain])
|
self.run_ipt(family, ['-F', chain])
|
||||||
self.run_ipt(family, ['-X', chain])
|
self.run_ipt(family, ['-X', chain])
|
||||||
@ -468,6 +511,27 @@ class NftablesWorker(FirewallWorker):
|
|||||||
self.run_nft(nft_input)
|
self.run_nft(nft_input)
|
||||||
self.chains[family].add(chain)
|
self.chains[family].add(chain)
|
||||||
|
|
||||||
|
def update_connected_ips(self, family):
|
||||||
|
addr = '{' + ', '.join(self.get_connected_ips(family)) + '}'
|
||||||
|
|
||||||
|
nft_input = (
|
||||||
|
'flush chain {family} {table} prerouting\n'
|
||||||
|
'flush chain {family} {table} postrouting\n'
|
||||||
|
'table {family} {table} {{\n'
|
||||||
|
' chain prerouting {{\n'
|
||||||
|
' iifname != "vif*" {family} saddr {addr} drop\n'
|
||||||
|
' }}\n'
|
||||||
|
' chain postrouting {{\n'
|
||||||
|
' oifname != "vif*" {family} daddr {addr} drop\n'
|
||||||
|
' }}\n'
|
||||||
|
'}}\n'
|
||||||
|
).format(
|
||||||
|
family=('ip6' if family == 6 else 'ip'),
|
||||||
|
table='qubes-firewall',
|
||||||
|
addr=addr,
|
||||||
|
)
|
||||||
|
self.run_nft(nft_input)
|
||||||
|
|
||||||
def prepare_rules(self, chain, rules, family):
|
def prepare_rules(self, chain, rules, family):
|
||||||
"""
|
"""
|
||||||
Helper function to translate rules list into input for iptables-restore
|
Helper function to translate rules list into input for iptables-restore
|
||||||
@ -609,8 +673,6 @@ class NftablesWorker(FirewallWorker):
|
|||||||
self.apply_rules_family(source, rules, 4)
|
self.apply_rules_family(source, rules, 4)
|
||||||
|
|
||||||
def init(self):
|
def init(self):
|
||||||
# make sure 'QBS_FORWARD' chain exists - should be created before
|
|
||||||
# starting qubes-firewall
|
|
||||||
nft_init = (
|
nft_init = (
|
||||||
'table {family} qubes-firewall {{\n'
|
'table {family} qubes-firewall {{\n'
|
||||||
' chain forward {{\n'
|
' chain forward {{\n'
|
||||||
@ -619,6 +681,14 @@ class NftablesWorker(FirewallWorker):
|
|||||||
' ct state established,related accept\n'
|
' ct state established,related accept\n'
|
||||||
' meta iifname != "vif*" accept\n'
|
' meta iifname != "vif*" accept\n'
|
||||||
' }}\n'
|
' }}\n'
|
||||||
|
' chain prerouting {{\n'
|
||||||
|
' type filter hook prerouting priority 0;\n'
|
||||||
|
' policy accept;\n'
|
||||||
|
' }}\n'
|
||||||
|
' chain postrouting {{\n'
|
||||||
|
' type filter hook postrouting priority 0;\n'
|
||||||
|
' policy accept;\n'
|
||||||
|
' }}\n'
|
||||||
'}}\n'
|
'}}\n'
|
||||||
)
|
)
|
||||||
nft_init = ''.join(
|
nft_init = ''.join(
|
||||||
|
Loading…
Reference in New Issue
Block a user