backup: add firewall and appmenus list handling

This commit is contained in:
Marek Marczykowski-Górecki 2017-07-17 02:33:25 +02:00
parent 04ad224a9d
commit f1036c27a7
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
3 changed files with 135 additions and 2 deletions

View File

@ -442,6 +442,9 @@ class ExtractWorker3(Process):
for fname, (data_func, size_func) in self.handlers.items():
if not fname.startswith(dirname + '/'):
continue
if not os.path.exists(fname):
# for example firewall.xml
continue
if size_func is not None:
size_func(os.path.getsize(fname))
with open(fname, 'rb') as input_file:
@ -741,6 +744,10 @@ class BackupVM(object):
'''Report whether a VM is included in the backup'''
return False
def handle_firewall_xml(self, vm, stream):
'''Import appropriate format of firewall.xml'''
raise NotImplementedError
class BackupRestoreOptions(object):
'''Options for restore operation'''
# pylint: disable=too-few-public-methods
@ -1717,6 +1724,15 @@ class BackupRestore(object):
if retcode != 0:
self.log.error("*** Error while setting home directory owner")
def _handle_appmenus_list(self, vm, stream):
'''Handle whitelisted-appmenus.list file'''
try:
subprocess.check_call(
['qvm-appmenus', '--set-whitelist=-', vm.name],
stdin=stream)
except subprocess.CalledProcessError:
self.log.exception('Failed to set application list for %s', vm.name)
def restore_do(self, restore_info):
'''
@ -1753,8 +1769,11 @@ class BackupRestore(object):
size_func = volume.resize
handlers[os.path.join(vm_info.subdir, name + '.img')] = \
(data_func, size_func)
# TODO applications whitelist
# TODO firewall
handlers[os.path.join(vm_info.subdir, 'firewall.xml')] = (
functools.partial(vm_info.vm.handle_firewall_xml, vm), None)
handlers[os.path.join(vm_info.subdir,
'whitelisted-appmenus.list')] = (
functools.partial(self._handle_appmenus_list, vm), None)
if 'dom0' in restore_info.keys() and \
restore_info['dom0'].good_to_go:

View File

@ -25,6 +25,7 @@ import xml.parsers
import logging
import lxml.etree
from qubesadmin.firewall import Rule, Action, Proto, DstHost, SpecialTarget
import qubesadmin.backup
service_to_feature = {
@ -44,6 +45,102 @@ class Core2VM(qubesadmin.backup.BackupVM):
def included_in_backup(self):
return self.backup_content
@staticmethod
def rule_from_xml_v1(node, action):
'''Parse single rule in old XML format (pre Qubes 4.0)
:param node: XML node for the rule
:param action: action to apply (in old format it wasn't part of the
rule itself)
'''
netmask = node.get('netmask')
if netmask is None:
netmask = 32
else:
netmask = int(netmask)
address = node.get('address')
if address:
dsthost = DstHost(address, netmask)
else:
dsthost = None
proto = node.get('proto')
port = node.get('port')
toport = node.get('toport')
if port and toport:
dstports = port + '-' + toport
elif port:
dstports = port
else:
dstports = None
# backward compatibility: protocol defaults to TCP if port is specified
if dstports and not proto:
proto = 'tcp'
if proto == 'any':
proto = None
expire = node.get('expire')
kwargs = {
'action': action,
}
if dsthost:
kwargs['dsthost'] = dsthost
if dstports:
kwargs['dstports'] = dstports
if proto:
kwargs['proto'] = proto
if expire:
kwargs['expire'] = expire
return Rule(None, **kwargs)
def handle_firewall_xml(self, vm, stream):
'''Load old (Qubes < 4.0) firewall XML format'''
try:
tree = lxml.etree.parse(stream) # pylint: disable=no-member
xml_root = tree.getroot()
policy_v1 = xml_root.get('policy')
assert policy_v1 in ('allow', 'deny')
default_policy_is_accept = (policy_v1 == 'allow')
rules = []
def _translate_action(key):
'''Translate action name'''
if xml_root.get(key, policy_v1) == 'allow':
return Action.accept
return Action.drop
rules.append(Rule(None,
action=_translate_action('dns'),
specialtarget=SpecialTarget('dns')))
rules.append(Rule(None,
action=_translate_action('icmp'),
proto=Proto.icmp))
if default_policy_is_accept:
rule_action = Action.drop
else:
rule_action = Action.accept
for element in xml_root:
rule = self.rule_from_xml_v1(element, rule_action)
rules.append(rule)
if default_policy_is_accept:
rules.append(Rule(None, action='accept'))
else:
rules.append(Rule(None, action='drop'))
vm.firewall.rules = rules
except: # pylint: disable=bare-except
vm.log.exception('Failed to set firewall')
class Core2Qubes(qubesadmin.backup.BackupApp):
'''Parsed qubes.xml'''
def __init__(self, store=None):

View File

@ -25,6 +25,7 @@ import logging
import lxml.etree
import qubesadmin.backup
import qubesadmin.firewall
class Core3VM(qubesadmin.backup.BackupVM):
'''VM object'''
@ -33,6 +34,22 @@ class Core3VM(qubesadmin.backup.BackupVM):
def included_in_backup(self):
return self.backup_path is not None
def handle_firewall_xml(self, vm, stream):
'''Load new (Qubes >= 4.0) firewall XML format'''
try:
tree = lxml.etree.parse(stream) # pylint: disable=no-member
xml_root = tree.getroot()
rules = []
for rule_node in xml_root.findall('./rules/rule'):
rule_opts = {}
for rule_opt in rule_node.findall('./properties/property'):
rule_opts[rule_opt.get('name')] = rule_opt.text
rules.append(qubesadmin.firewall.Rule(None, **rule_opts))
vm.firewall.rules = rules
except: # pylint: disable=bare-except
vm.log.exception('Failed to set firewall')
class Core3Qubes(qubesadmin.backup.BackupApp):
'''Parsed qubes.xml'''
def __init__(self, store=None):