backup: add firewall and appmenus list handling
This commit is contained in:
parent
04ad224a9d
commit
f1036c27a7
@ -442,6 +442,9 @@ class ExtractWorker3(Process):
|
|||||||
for fname, (data_func, size_func) in self.handlers.items():
|
for fname, (data_func, size_func) in self.handlers.items():
|
||||||
if not fname.startswith(dirname + '/'):
|
if not fname.startswith(dirname + '/'):
|
||||||
continue
|
continue
|
||||||
|
if not os.path.exists(fname):
|
||||||
|
# for example firewall.xml
|
||||||
|
continue
|
||||||
if size_func is not None:
|
if size_func is not None:
|
||||||
size_func(os.path.getsize(fname))
|
size_func(os.path.getsize(fname))
|
||||||
with open(fname, 'rb') as input_file:
|
with open(fname, 'rb') as input_file:
|
||||||
@ -741,6 +744,10 @@ class BackupVM(object):
|
|||||||
'''Report whether a VM is included in the backup'''
|
'''Report whether a VM is included in the backup'''
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def handle_firewall_xml(self, vm, stream):
|
||||||
|
'''Import appropriate format of firewall.xml'''
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
class BackupRestoreOptions(object):
|
class BackupRestoreOptions(object):
|
||||||
'''Options for restore operation'''
|
'''Options for restore operation'''
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
@ -1717,6 +1724,15 @@ class BackupRestore(object):
|
|||||||
if retcode != 0:
|
if retcode != 0:
|
||||||
self.log.error("*** Error while setting home directory owner")
|
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):
|
def restore_do(self, restore_info):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@ -1753,8 +1769,11 @@ class BackupRestore(object):
|
|||||||
size_func = volume.resize
|
size_func = volume.resize
|
||||||
handlers[os.path.join(vm_info.subdir, name + '.img')] = \
|
handlers[os.path.join(vm_info.subdir, name + '.img')] = \
|
||||||
(data_func, size_func)
|
(data_func, size_func)
|
||||||
# TODO applications whitelist
|
handlers[os.path.join(vm_info.subdir, 'firewall.xml')] = (
|
||||||
# TODO firewall
|
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 \
|
if 'dom0' in restore_info.keys() and \
|
||||||
restore_info['dom0'].good_to_go:
|
restore_info['dom0'].good_to_go:
|
||||||
|
@ -25,6 +25,7 @@ import xml.parsers
|
|||||||
import logging
|
import logging
|
||||||
import lxml.etree
|
import lxml.etree
|
||||||
|
|
||||||
|
from qubesadmin.firewall import Rule, Action, Proto, DstHost, SpecialTarget
|
||||||
import qubesadmin.backup
|
import qubesadmin.backup
|
||||||
|
|
||||||
service_to_feature = {
|
service_to_feature = {
|
||||||
@ -44,6 +45,102 @@ class Core2VM(qubesadmin.backup.BackupVM):
|
|||||||
def included_in_backup(self):
|
def included_in_backup(self):
|
||||||
return self.backup_content
|
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):
|
class Core2Qubes(qubesadmin.backup.BackupApp):
|
||||||
'''Parsed qubes.xml'''
|
'''Parsed qubes.xml'''
|
||||||
def __init__(self, store=None):
|
def __init__(self, store=None):
|
||||||
|
@ -25,6 +25,7 @@ import logging
|
|||||||
import lxml.etree
|
import lxml.etree
|
||||||
|
|
||||||
import qubesadmin.backup
|
import qubesadmin.backup
|
||||||
|
import qubesadmin.firewall
|
||||||
|
|
||||||
class Core3VM(qubesadmin.backup.BackupVM):
|
class Core3VM(qubesadmin.backup.BackupVM):
|
||||||
'''VM object'''
|
'''VM object'''
|
||||||
@ -33,6 +34,22 @@ class Core3VM(qubesadmin.backup.BackupVM):
|
|||||||
def included_in_backup(self):
|
def included_in_backup(self):
|
||||||
return self.backup_path is not None
|
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):
|
class Core3Qubes(qubesadmin.backup.BackupApp):
|
||||||
'''Parsed qubes.xml'''
|
'''Parsed qubes.xml'''
|
||||||
def __init__(self, store=None):
|
def __init__(self, store=None):
|
||||||
|
Loading…
Reference in New Issue
Block a user