0e033645ad
Make use of better security of Qubes 4.x by using HVM by default. If some VMs are incompatible with it (like MirageOS based), user can always switch it to PV manually later.
1721 lines
72 KiB
Python
1721 lines
72 KiB
Python
# -*- encoding: utf8 -*-
|
|
#
|
|
# The Qubes OS Project, http://www.qubes-os.org
|
|
#
|
|
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public License
|
|
# as published by the Free Software Foundation; either version 2.1
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
import functools
|
|
import tempfile
|
|
import unittest
|
|
from distutils import spawn
|
|
from multiprocessing import Queue
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
import logging
|
|
|
|
try:
|
|
import unittest.mock as mock
|
|
except ImportError:
|
|
import mock
|
|
import re
|
|
|
|
import multiprocessing
|
|
import pkg_resources
|
|
import sys
|
|
|
|
import qubesadmin.backup.core2
|
|
import qubesadmin.backup.core3
|
|
import qubesadmin.firewall
|
|
import qubesadmin.storage
|
|
import qubesadmin.tests
|
|
import qubesadmin.tests.backup
|
|
|
|
|
|
MANGLED_SUBDIRS_R2 = {
|
|
"test-work": "vm5",
|
|
"test-template-clone": "vm9",
|
|
"test-custom-template-appvm": "vm10",
|
|
"test-standalonevm": "vm11",
|
|
"test-testproxy": "vm12",
|
|
"test-testhvm": "vm14",
|
|
"test-net": "vm16",
|
|
}
|
|
MANGLED_SUBDIRS_R4 = {
|
|
"test-work": "vm3",
|
|
"test-fedora-25-clone": "vm7",
|
|
"test-custom-template-appvm": "vm31",
|
|
"test-standalonevm": "vm4",
|
|
"test-proxy": "vm30",
|
|
"test-hvm": "vm9",
|
|
"test-net": "vm6",
|
|
"test-d8test": "vm20",
|
|
}
|
|
|
|
APPTEMPLATE_R2B2 = '''
|
|
[Desktop Entry]
|
|
Name=%VMNAME%: {name}
|
|
GenericName=%VMNAME%: {name}
|
|
GenericName[ca]=%VMNAME%: Navegador web
|
|
GenericName[cs]=%VMNAME%: Webový prohlížeč
|
|
GenericName[es]=%VMNAME%: Navegador web
|
|
GenericName[fa]=%VMNAME%: مرورر اینترنتی
|
|
GenericName[fi]=%VMNAME%: WWW-selain
|
|
GenericName[fr]=%VMNAME%: Navigateur Web
|
|
GenericName[hu]=%VMNAME%: Webböngésző
|
|
GenericName[it]=%VMNAME%: Browser Web
|
|
GenericName[ja]=%VMNAME%: ウェブ・ブラウザ
|
|
GenericName[ko]=%VMNAME%: 웹 브라우저
|
|
GenericName[nb]=%VMNAME%: Nettleser
|
|
GenericName[nl]=%VMNAME%: Webbrowser
|
|
GenericName[nn]=%VMNAME%: Nettlesar
|
|
GenericName[no]=%VMNAME%: Nettleser
|
|
GenericName[pl]=%VMNAME%: Przeglądarka WWW
|
|
GenericName[pt]=%VMNAME%: Navegador Web
|
|
GenericName[pt_BR]=%VMNAME%: Navegador Web
|
|
GenericName[sk]=%VMNAME%: Internetový prehliadač
|
|
GenericName[sv]=%VMNAME%: Webbläsare
|
|
Comment={comment}
|
|
Comment[ca]=Navegueu per el web
|
|
Comment[cs]=Prohlížení stránek World Wide Webu
|
|
Comment[de]=Im Internet surfen
|
|
Comment[es]=Navegue por la web
|
|
Comment[fa]=صفحات شبه جهانی اینترنت را مرور نمایید
|
|
Comment[fi]=Selaa Internetin WWW-sivuja
|
|
Comment[fr]=Navigue sur Internet
|
|
Comment[hu]=A világháló böngészése
|
|
Comment[it]=Esplora il web
|
|
Comment[ja]=ウェブを閲覧します
|
|
Comment[ko]=웹을 돌아 다닙니다
|
|
Comment[nb]=Surf på nettet
|
|
Comment[nl]=Verken het internet
|
|
Comment[nn]=Surf på nettet
|
|
Comment[no]=Surf på nettet
|
|
Comment[pl]=Przeglądanie stron WWW
|
|
Comment[pt]=Navegue na Internet
|
|
Comment[pt_BR]=Navegue na Internet
|
|
Comment[sk]=Prehliadanie internetu
|
|
Comment[sv]=Surfa på webben
|
|
Exec=qvm-run -q --tray -a %VMNAME% '{command} %u'
|
|
Categories=Network;WebBrowser;
|
|
X-Qubes-VmName=%VMNAME%
|
|
Icon=%VMDIR%/icon.png
|
|
'''
|
|
|
|
QUBESXML_R1 = '''<?xml version='1.0' encoding='UTF-8'?>
|
|
<QubesVmCollection clockvm="2" default_fw_netvm="2" default_kernel="3.2.7-10" default_netvm="3" default_template="1" updatevm="3"><QubesTemplateVm conf_file="fedora-17-x64.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/vm-templates/fedora-17-x64" include_in_backups="True" installed_by_rpm="True" internal="False" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="fedora-17-x64" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="1" root_img="root.img" services="{'meminfo-writer': True}" template_qid="none" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesNetVm conf_file="netvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/netvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="iommu=soft swiotlb=2048" label="red" maxmem="4063" memory="200" name="netvm" netid="1" pcidevs="['00:19.0', '03:00.0']" private_img="private.img" qid="2" root_img="root.img" services="{'ntpd': False, 'meminfo-writer': False}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /><QubesProxyVm conf_file="firewallvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/firewallvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="200" name="firewallvm" netid="2" netvm_qid="2" pcidevs="[]" private_img="private.img" qid="3" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="fedora-17-x64-dvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/fedora-17-x64-dvm" include_in_backups="True" installed_by_rpm="False" internal="True" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="fedora-17-x64-dvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="4" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="1" volatile_img="volatile.img" /><QubesAppVm conf_file="test-work.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-work" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="400" name="test-work" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="5" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="personal.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/personal" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="400" name="personal" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="6" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="banking.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/banking" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="400" name="banking" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="7" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="untrusted.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/untrusted" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="red" maxmem="4063" memory="400" name="untrusted" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="8" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-standalonevm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-standalonevm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="None" kernelopts="" label="red" maxmem="4063" memory="400" name="test-standalonevm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="9" root_img="root.img" services="{'meminfo-writer': True}" template_qid="none" uses_default_kernel="False" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-testvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-testvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="red" mac="00:16:3E:5E:6C:55" maxmem="4063" memory="400" name="test-testvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="10" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesTemplateVm conf_file="test-template-clone.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/vm-templates/test-template-clone" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="test-template-clone" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="11" root_img="root.img" services="{'meminfo-writer': True}" template_qid="none" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-custom-template-appvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-custom-template-appvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="400" name="test-custom-template-appvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="12" root_img="root.img" services="{'meminfo-writer': True}" template_qid="11" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesProxyVm conf_file="test-testproxy.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/test-testproxy" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="200" name="test-testproxy" netid="3" netvm_qid="2" pcidevs="[]" private_img="private.img" qid="13" root_img="root.img" services="{'meminfo-writer': True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /></QubesVmCollection>
|
|
'''
|
|
|
|
BACKUP_HEADER_R2 = '''version=3
|
|
hmac-algorithm=SHA512
|
|
crypto-algorithm=aes-256-cbc
|
|
encrypted={encrypted}
|
|
compressed={compressed}
|
|
compression-filter=gzip
|
|
'''
|
|
|
|
BACKUP_HEADER_R4 = '''version=4
|
|
hmac-algorithm=scrypt
|
|
encrypted=True
|
|
compressed={compressed}
|
|
compression-filter=gzip
|
|
backup-id=20161020T123455-1234
|
|
'''
|
|
|
|
parsed_qubes_xml_r2 = {
|
|
'domains': {
|
|
'dom0': {
|
|
'klass': 'AdminVM',
|
|
'label': 'black',
|
|
'properties': {},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'fedora-20-x64': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'black',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.meminfo-writer': True,
|
|
'qrexec': True,
|
|
'gui': True,
|
|
},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'netvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'memory': '200',
|
|
'netvm': None,
|
|
'default_dispvm': 'disp-no-netvm',
|
|
'provides_network': True},
|
|
'devices': {
|
|
'pci': {
|
|
('dom0', '02_00.0'): {},
|
|
('dom0', '03_00.0'): {},
|
|
}
|
|
},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.ntpd': False,
|
|
'service.meminfo-writer': False
|
|
},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'firewallvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'green',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'memory': '200',
|
|
'provides_network': True
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'fedora-20-x64-dvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'gray',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'vcpus': '1'
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'internal': True, 'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'banking': {
|
|
'klass': 'AppVM',
|
|
'label': 'green',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'personal': {
|
|
'klass': 'AppVM',
|
|
'label': 'yellow',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'untrusted': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'netvm': 'test-testproxy',
|
|
'default_dispvm': 'disp-test-testproxy',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'testproxy2': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'memory': '200',
|
|
'provides_network': True},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'test-template-clone',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'test-testproxy': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'memory': '200',
|
|
'provides_network': True},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': 'servicevms/test-testproxy',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-testhvm': {
|
|
'klass': 'StandaloneVM',
|
|
'label': 'purple',
|
|
'properties': {'kernel': '', 'virt_mode': 'hvm', 'memory': '512'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.meminfo-writer': False,
|
|
'linux-stubdom': False},
|
|
'template': None,
|
|
'backup_path': 'appvms/test-testhvm',
|
|
'included_in_backup': True,
|
|
'root_size': 2097664,
|
|
},
|
|
'test-work': {
|
|
'klass': 'AppVM',
|
|
'label': 'green',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': 'appvms/test-work',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-template-clone': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'green',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.meminfo-writer': True,
|
|
'qrexec': True,
|
|
'gui': True,
|
|
},
|
|
'template': None,
|
|
'backup_path': 'vm-templates/test-template-clone',
|
|
'included_in_backup': True,
|
|
'root_size': 209715712,
|
|
},
|
|
'test-custom-template-appvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'yellow',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': True},
|
|
'template': 'test-template-clone',
|
|
'backup_path': 'appvms/test-custom-template-appvm',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-standalonevm': {
|
|
'klass': 'StandaloneVM',
|
|
'label': 'blue',
|
|
'properties': {'maxmem': '1535'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.meminfo-writer': True,
|
|
'qrexec': True,
|
|
'gui': True,
|
|
},
|
|
'template': None,
|
|
'backup_path': 'appvms/test-standalonevm',
|
|
'included_in_backup': True,
|
|
'root_size': 2097664,
|
|
},
|
|
'test-net': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '1535',
|
|
'memory': '200',
|
|
'netvm': None,
|
|
'default_dispvm': 'disp-no-netvm',
|
|
'provides_network': True},
|
|
'devices': {
|
|
'pci': {
|
|
('dom0', '02_00.0'): {},
|
|
('dom0', '03_00.0'): {},
|
|
}
|
|
},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.ntpd': False,
|
|
'service.meminfo-writer': False
|
|
},
|
|
'template': 'fedora-20-x64',
|
|
'backup_path': 'servicevms/test-net',
|
|
'included_in_backup': True,
|
|
},
|
|
'disp-no-netvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'netvm': None,
|
|
'template_for_dispvms': True,
|
|
},
|
|
'devices': {},
|
|
'features': {},
|
|
'tags': set(),
|
|
'template': None, # default
|
|
'backup_path': None,
|
|
'included_in_backup': True,
|
|
},
|
|
'disp-test-testproxy': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'netvm': 'test-testproxy',
|
|
'template_for_dispvms': True,
|
|
},
|
|
'devices': {},
|
|
'features': {},
|
|
'tags': set(),
|
|
'template': None, # default
|
|
'backup_path': None,
|
|
'included_in_backup': True,
|
|
},
|
|
},
|
|
'globals': {
|
|
'default_template': 'fedora-20-x64',
|
|
'default_kernel': '3.7.6-2',
|
|
'default_netvm': 'firewallvm',
|
|
'clockvm': 'netvm',
|
|
'updatevm': 'firewallvm'
|
|
},
|
|
}
|
|
|
|
parsed_qubes_xml_v4 = {
|
|
'domains': {
|
|
'dom0': {
|
|
'klass': 'AdminVM',
|
|
'label': 'black',
|
|
'properties': {},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'fedora-25': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'black',
|
|
'properties': {},
|
|
'devices': {},
|
|
'tags': {'created-by-test-work'},
|
|
'features': {
|
|
'gui': '1',
|
|
'qrexec': 'True',
|
|
'updates-available': False
|
|
},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'fedora-25-lvm': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'black',
|
|
'properties': {
|
|
'maxmem': '4000',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'debian-8': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'black',
|
|
'properties': {},
|
|
'devices': {},
|
|
'tags': {'created-by-dom0'},
|
|
'features': {
|
|
'gui': '1',
|
|
'qrexec': 'True',
|
|
'updates-available': False},
|
|
'template': None,
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'sys-net': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'virt_mode': 'hvm',
|
|
'kernelopts': 'nopat i8042.nokbd i8042.noaux',
|
|
'maxmem': '300',
|
|
'memory': '300',
|
|
'netvm': None,
|
|
'default_user': 'user',
|
|
'provides_network': 'True'},
|
|
'devices': {
|
|
'pci': {
|
|
('dom0', '02_00.0'): {},
|
|
}
|
|
},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.clocksync': '1',
|
|
'service.meminfo-writer': False
|
|
},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'sys-firewall': {
|
|
'klass': 'AppVM',
|
|
'label': 'green',
|
|
'properties': {
|
|
'autostart': 'True',
|
|
'memory': '500',
|
|
'provides_network': 'True'
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'test-d8test': {
|
|
'klass': 'AppVM',
|
|
'label': 'gray',
|
|
'properties': {'debug': 'True', 'kernel': None},
|
|
'devices': {},
|
|
'tags': {'created-by-dom0'},
|
|
'features': {},
|
|
'template': 'debian-8',
|
|
'backup_path': 'appvms/test-d8test',
|
|
'included_in_backup': True,
|
|
},
|
|
'fedora-25-dvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'template_for_dispvms': 'True',
|
|
'vcpus': '1',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'internal': '1', 'service.meminfo-writer': '1'},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'fedora-25-clone-dvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'vcpus': '1',
|
|
'template_for_dispvms': 'True',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'internal': '1', 'service.meminfo-writer': '1'},
|
|
'template': 'test-fedora-25-clone',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'vault': {
|
|
'klass': 'AppVM',
|
|
'label': 'black',
|
|
'properties': {'virt_mode': 'pv', 'maxmem': '1536', 'netvm': None},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'personal': {
|
|
'klass': 'AppVM',
|
|
'label': 'yellow',
|
|
'properties': {'netvm': 'sys-firewall'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'feat1': '1',
|
|
'feat2': False,
|
|
'feat32': '1',
|
|
'featdis': False,
|
|
'xxx': '1'
|
|
},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'untrusted': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'netvm': None,
|
|
'backup_timestamp': '1474318497',
|
|
'default_dispvm': 'fedora-25-clone-dvm',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': '1'},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'sys-usb': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'autostart': 'True',
|
|
'maxmem': '400',
|
|
'provides_network': 'True',
|
|
},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.meminfo-writer': False,
|
|
'service.network-manager': False,
|
|
},
|
|
'template': 'fedora-25',
|
|
'backup_path': None,
|
|
'included_in_backup': False,
|
|
},
|
|
'test-proxy': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {'netvm': 'sys-net', 'provides_network': 'True'},
|
|
'devices': {},
|
|
'tags': {'created-by-dom0'},
|
|
'features': {},
|
|
'template': 'debian-8',
|
|
'backup_path': 'appvms/test-proxy',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-hvm': {
|
|
'klass': 'StandaloneVM',
|
|
'label': 'purple',
|
|
'properties': {
|
|
'kernel': None,
|
|
'virt_mode': 'hvm',
|
|
'maxmem': '4000'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': False},
|
|
'template': None,
|
|
'backup_path': 'appvms/test-hvm',
|
|
'included_in_backup': True,
|
|
'root_size': 2097664,
|
|
},
|
|
'test-work': {
|
|
'klass': 'AppVM',
|
|
'label': 'green',
|
|
'properties': {
|
|
'ip': '192.168.0.1',
|
|
'maxmem': '4000',
|
|
'memory': '400'},
|
|
'devices': {},
|
|
'tags': {'tag1', 'tag2'},
|
|
'features': {'service.meminfo-writer': '1'},
|
|
'template': 'fedora-25',
|
|
'backup_path': 'appvms/test-work',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-fedora-25-clone': {
|
|
'klass': 'TemplateVM',
|
|
'label': 'black',
|
|
'properties': {'maxmem': '4000'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {'service.meminfo-writer': '1'},
|
|
'template': None,
|
|
'backup_path': 'vm-templates/test-fedora-25-clone',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-custom-template-appvm': {
|
|
'klass': 'AppVM',
|
|
'label': 'yellow',
|
|
'properties': {'debug': 'True', 'kernel': None},
|
|
'devices': {},
|
|
'tags': {'created-by-dom0'},
|
|
'features': {},
|
|
'template': 'test-fedora-25-clone',
|
|
'backup_path': 'appvms/test-custom-template-appvm',
|
|
'included_in_backup': True,
|
|
},
|
|
'test-standalonevm': {
|
|
'klass': 'StandaloneVM',
|
|
'label': 'blue',
|
|
'properties': {'maxmem': '4000'},
|
|
'devices': {},
|
|
'tags': set(),
|
|
'features': {},
|
|
'template': None,
|
|
'backup_path': 'appvms/test-standalonevm',
|
|
'included_in_backup': True,
|
|
'root_size': 2097664,
|
|
},
|
|
'test-net': {
|
|
'klass': 'AppVM',
|
|
'label': 'red',
|
|
'properties': {
|
|
'maxmem': '300',
|
|
'memory': '300',
|
|
'netvm': None,
|
|
'provides_network': 'True'
|
|
},
|
|
'devices': {
|
|
'pci': {
|
|
('dom0', '03_00.0'): {},
|
|
}
|
|
},
|
|
'tags': set(),
|
|
'features': {
|
|
'service.ntpd': False,
|
|
'service.meminfo-writer': False
|
|
},
|
|
'template': 'fedora-25',
|
|
'backup_path': 'appvms/test-net',
|
|
'included_in_backup': True,
|
|
},
|
|
},
|
|
'globals': {
|
|
'default_template': 'fedora-25',
|
|
'default_kernel': '4.9.31-17',
|
|
'default_netvm': 'sys-firewall',
|
|
'default_dispvm': 'fedora-25-dvm',
|
|
#'default_fw_netvm': 'sys-net',
|
|
'clockvm': 'sys-net',
|
|
'updatevm': 'sys-firewall'
|
|
},
|
|
}
|
|
|
|
|
|
class TC_00_QubesXML(qubesadmin.tests.QubesTestCase):
|
|
|
|
def assertCorrectlyConverted(self, backup_app, expected_data):
|
|
self.assertCountEqual(backup_app.domains.keys(),
|
|
expected_data['domains'].keys())
|
|
for vm in expected_data['domains']:
|
|
self.assertEqual(backup_app.domains[vm].name, vm)
|
|
self.assertEqual(backup_app.domains[vm].properties,
|
|
expected_data['domains'][vm]['properties'], vm)
|
|
for devtype in expected_data['domains'][vm]['devices']:
|
|
self.assertEqual(backup_app.domains[vm].devices[devtype],
|
|
expected_data['domains'][vm]['devices'][devtype], vm)
|
|
self.assertEqual(backup_app.domains[vm].tags,
|
|
expected_data['domains'][vm]['tags'], vm)
|
|
self.assertEqual(backup_app.domains[vm].features,
|
|
expected_data['domains'][vm]['features'], vm)
|
|
self.assertEqual(backup_app.domains[vm].template,
|
|
expected_data['domains'][vm]['template'], vm)
|
|
self.assertEqual(backup_app.domains[vm].klass,
|
|
expected_data['domains'][vm]['klass'], vm)
|
|
self.assertEqual(backup_app.domains[vm].label,
|
|
expected_data['domains'][vm]['label'], vm)
|
|
self.assertEqual(backup_app.domains[vm].backup_path,
|
|
expected_data['domains'][vm]['backup_path'], vm)
|
|
self.assertEqual(backup_app.domains[vm].included_in_backup,
|
|
expected_data['domains'][vm]['included_in_backup'], vm)
|
|
|
|
self.assertEqual(backup_app.globals, expected_data['globals'])
|
|
|
|
def test_000_qubes_xml_r2(self):
|
|
xml_data = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
|
|
with tempfile.NamedTemporaryFile() as qubes_xml:
|
|
qubes_xml.file.write(xml_data)
|
|
backup_app = qubesadmin.backup.core2.Core2Qubes(qubes_xml.name)
|
|
self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_r2)
|
|
|
|
def test_010_qubes_xml_r4(self):
|
|
self.maxDiff = None
|
|
xml_data = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
|
|
with tempfile.NamedTemporaryFile() as qubes_xml:
|
|
qubes_xml.file.write(xml_data)
|
|
backup_app = qubesadmin.backup.core3.Core3Qubes(qubes_xml.name)
|
|
self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_v4)
|
|
|
|
# backup code use multiprocessing, synchronize with main process
|
|
class AppProxy(object):
|
|
def __init__(self, app, sync_queue):
|
|
self._app = app
|
|
self._sync_queue = sync_queue
|
|
|
|
def qubesd_call(self, dest, method, arg=None, payload=None,
|
|
payload_stream=None):
|
|
if payload_stream:
|
|
signature_bin = payload_stream.read(512)
|
|
payload = signature_bin.split(b'\0', 1)[0]
|
|
subprocess.call(['cat'], stdin=payload_stream,
|
|
stdout=subprocess.DEVNULL)
|
|
payload_stream.close()
|
|
self._sync_queue.put((dest, method, arg, payload))
|
|
return self._app.qubesd_call(dest, method, arg, payload)
|
|
|
|
|
|
class MockVolume(qubesadmin.storage.Volume):
|
|
def __init__(self, import_data_queue, *args, **kwargs):
|
|
super(MockVolume, self).__init__(*args, **kwargs)
|
|
self.app = AppProxy(self.app, import_data_queue)
|
|
|
|
class MockFirewall(qubesadmin.firewall.Firewall):
|
|
def __init__(self, import_data_queue, *args, **kwargs):
|
|
super(MockFirewall, self).__init__(*args, **kwargs)
|
|
self.vm.app = AppProxy(self.vm.app, import_data_queue)
|
|
|
|
|
|
@unittest.skipUnless(os.environ.get('ENABLE_SLOW_TESTS', False),
|
|
'Set ENABLE_SLOW_TESTS=1 environment variable')
|
|
class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
|
|
|
|
storage_pool = None
|
|
|
|
def tearDown(self):
|
|
super(TC_10_BackupCompatibility, self).tearDown()
|
|
|
|
def create_whitelisted_appmenus(self, filename):
|
|
with open(filename, "w") as f:
|
|
f.write("gnome-terminal.desktop\n")
|
|
f.write("nautilus.desktop\n")
|
|
f.write("firefox.desktop\n")
|
|
f.write("mozilla-thunderbird.desktop\n")
|
|
f.write("libreoffice-startcenter.desktop\n")
|
|
|
|
def create_appmenus(self, dir, template, list):
|
|
for name in list:
|
|
with open(os.path.join(dir, name + ".desktop"), "w") as f:
|
|
f.write(template.format(name=name, comment=name, command=name))
|
|
|
|
def create_private_img(self, filename):
|
|
signature = '/'.join(os.path.splitext(filename)[0].split('/')[-2:])
|
|
self.create_sparse(filename, 2*2**20, signature=signature.encode())
|
|
#subprocess.check_call(["/usr/sbin/mkfs.ext4", "-q", "-F", filename])
|
|
|
|
def create_volatile_img(self, filename):
|
|
self.create_sparse(filename, 11.5*2**20)
|
|
# here used to be sfdisk call with "0,1024,S\n,10240,L\n" input,
|
|
# but since sfdisk folks like to change command arguments in
|
|
# incompatible way, have an partition table verbatim here
|
|
ptable = (
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\xab\x39\xd5\xd4\x00\x00\x20\x00'
|
|
'\x00\x21\xaa\x82\x82\x28\x08\x00\x00\x00\x00\x00\x00\x20\xaa\x00'
|
|
'\x82\x29\x15\x83\x9c\x79\x08\x00\x00\x20\x00\x00\x01\x40\x00\x00'
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x55'
|
|
)
|
|
with open(filename, 'r+') as f:
|
|
f.seek(0x1b0)
|
|
f.write(ptable)
|
|
|
|
# TODO: mkswap
|
|
|
|
def fullpath(self, name):
|
|
return os.path.join(self.backupdir, name)
|
|
|
|
def create_v1_files(self, r2b2=False):
|
|
appmenus_list = [
|
|
"firefox", "gnome-terminal", "evince", "evolution",
|
|
"mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
|
|
"gedit", "gpk-update-viewer", "gpk-application"
|
|
]
|
|
|
|
os.mkdir(self.fullpath("appvms"))
|
|
os.mkdir(self.fullpath("servicevms"))
|
|
os.mkdir(self.fullpath("vm-templates"))
|
|
|
|
# normal AppVM, with firewall
|
|
os.mkdir(self.fullpath("appvms/test-work"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"appvms/test-work/whitelisted-appmenus.list"))
|
|
os.symlink("/usr/share/qubes/icons/green.png",
|
|
self.fullpath("appvms/test-work/icon.png"))
|
|
self.create_private_img(self.fullpath("appvms/test-work/private.img"))
|
|
with open(self.fullpath("appvms/test-work/firewall.xml"), "wb") as \
|
|
f_firewall:
|
|
f_firewall.write(
|
|
pkg_resources.resource_string(__name__, 'v3-firewall.xml'))
|
|
|
|
# StandaloneVM
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"appvms/test-standalonevm/whitelisted-appmenus.list"))
|
|
os.symlink("/usr/share/qubes/icons/blue.png",
|
|
self.fullpath("appvms/test-standalonevm/icon.png"))
|
|
self.create_private_img(self.fullpath(
|
|
"appvms/test-standalonevm/private.img"))
|
|
self.create_sparse(
|
|
self.fullpath("appvms/test-standalonevm/root.img"), 10*2**30)
|
|
self.fill_image(self.fullpath("appvms/test-standalonevm/root.img"),
|
|
1024*1024, True,
|
|
signature=b'test-standalonevm/root')
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm/apps.templates"))
|
|
self.create_appmenus(self.fullpath("appvms/test-standalonevm/apps"
|
|
".templates"),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm/kernels"))
|
|
for k_file in ["initramfs", "vmlinuz", "modules.img"]:
|
|
self.fill_image(self.fullpath("appvms/test-standalonevm/kernels/"
|
|
+ k_file), 1024*1024)
|
|
|
|
# VM based on custom template
|
|
subprocess.check_call(
|
|
["/bin/cp", "-a", self.fullpath("appvms/test-work"),
|
|
self.fullpath("appvms/test-custom-template-appvm")])
|
|
# override for correct signature
|
|
self.create_private_img(
|
|
self.fullpath("appvms/test-custom-template-appvm/private.img"))
|
|
|
|
# HVM
|
|
if r2b2:
|
|
subprocess.check_call(
|
|
["/bin/cp", "-a", self.fullpath("appvms/test-standalonevm"),
|
|
self.fullpath("appvms/test-testhvm")])
|
|
# override for correct signature
|
|
self.create_private_img(
|
|
self.fullpath("appvms/test-testhvm/private.img"))
|
|
self.fill_image(self.fullpath("appvms/test-testhvm/root.img"),
|
|
1024*1024, True,
|
|
signature=b'test-testhvm/root')
|
|
|
|
# ProxyVM
|
|
os.mkdir(self.fullpath("servicevms/test-testproxy"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"servicevms/test-testproxy/whitelisted-appmenus.list"))
|
|
self.create_private_img(
|
|
self.fullpath("servicevms/test-testproxy/private.img"))
|
|
|
|
# NetVM
|
|
os.mkdir(self.fullpath("servicevms/test-net"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"servicevms/test-net/whitelisted-appmenus.list"))
|
|
self.create_private_img(
|
|
self.fullpath("servicevms/test-net/private.img"))
|
|
|
|
# Custom template
|
|
os.mkdir(self.fullpath("vm-templates/test-template-clone"))
|
|
self.create_private_img(
|
|
self.fullpath("vm-templates/test-template-clone/private.img"))
|
|
self.create_sparse(self.fullpath(
|
|
"vm-templates/test-template-clone/root-cow.img"), 10*2**20)
|
|
self.create_sparse(self.fullpath(
|
|
"vm-templates/test-template-clone/root.img"), 10*2**20)
|
|
self.fill_image(self.fullpath(
|
|
"vm-templates/test-template-clone/root.img"), 100*2**20, True,
|
|
signature=b'test-template-clone/root')
|
|
self.create_volatile_img(self.fullpath(
|
|
"vm-templates/test-template-clone/volatile.img"))
|
|
subprocess.check_call([
|
|
"/bin/tar", "cS",
|
|
"-f", self.fullpath(
|
|
"vm-templates/test-template-clone/clean-volatile.img.tar"),
|
|
"-C", self.fullpath("vm-templates/test-template-clone"),
|
|
"volatile.img"])
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/whitelisted-appmenus.list"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/vm-whitelisted-appmenus.list"))
|
|
if r2b2:
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/netvm-whitelisted-appmenus"
|
|
".list"))
|
|
os.symlink("/usr/share/qubes/icons/green.png",
|
|
self.fullpath("vm-templates/test-template-clone/icon.png"))
|
|
os.mkdir(
|
|
self.fullpath("vm-templates/test-template-clone/apps.templates"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-template-clone/apps.templates"),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
os.mkdir(self.fullpath("vm-templates/test-template-clone/apps"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-template-clone/apps"),
|
|
APPTEMPLATE_R2B2.replace("%VMNAME%", "test-template-clone")
|
|
.replace("%VMDIR%", self.fullpath(
|
|
"vm-templates/test-template-clone")),
|
|
appmenus_list)
|
|
|
|
def create_v4_files(self):
|
|
appmenus_list = [
|
|
"firefox", "gnome-terminal", "evince", "evolution",
|
|
"mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
|
|
"gedit", "gpk-update-viewer", "gpk-application"
|
|
]
|
|
|
|
os.mkdir(self.fullpath("appvms"))
|
|
os.mkdir(self.fullpath("vm-templates"))
|
|
|
|
# normal AppVMs
|
|
for vm in ('test-work', 'test-d8test', 'test-proxy',
|
|
'test-custom-template-appvm', 'test-net'):
|
|
os.mkdir(self.fullpath('appvms/{}'.format(vm)))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
'appvms/{}/whitelisted-appmenus.list'.format(vm)))
|
|
self.create_private_img(self.fullpath('appvms/{}/private.img'.format(
|
|
vm)))
|
|
|
|
# setup firewall only on one VM
|
|
with open(self.fullpath("appvms/test-work/firewall.xml"), "wb") as \
|
|
f_firewall:
|
|
f_firewall.write(
|
|
pkg_resources.resource_string(__name__, 'v4-firewall.xml'))
|
|
|
|
# StandaloneVMs
|
|
for vm in ('test-standalonevm', 'test-hvm'):
|
|
os.mkdir(self.fullpath('appvms/{}'.format(vm)))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
'appvms/{}/whitelisted-appmenus.list'.format(vm)))
|
|
self.create_private_img(self.fullpath(
|
|
'appvms/{}/private.img'.format(vm)))
|
|
self.create_sparse(
|
|
self.fullpath('appvms/{}/root.img'.format(vm)), 10*2**30)
|
|
self.fill_image(self.fullpath('appvms/{}/root.img'.format(vm)),
|
|
1024*1024, True,
|
|
signature='{}/root'.format(vm).encode())
|
|
|
|
# only for Linux one
|
|
os.mkdir(self.fullpath('appvms/test-standalonevm/apps.templates'))
|
|
self.create_appmenus(
|
|
self.fullpath('appvms/test-standalonevm/apps.templates'),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
|
|
# Custom template
|
|
os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone"))
|
|
self.create_private_img(
|
|
self.fullpath("vm-templates/test-fedora-25-clone/private.img"))
|
|
self.create_sparse(self.fullpath(
|
|
"vm-templates/test-fedora-25-clone/root.img"), 10*2**20)
|
|
self.fill_image(self.fullpath(
|
|
"vm-templates/test-fedora-25-clone/root.img"), 1*2**20, True,
|
|
signature=b'test-fedora-25-clone/root')
|
|
self.create_volatile_img(self.fullpath(
|
|
"vm-templates/test-fedora-25-clone/volatile.img"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-fedora-25-clone/whitelisted-appmenus.list"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-fedora-25-clone/vm-whitelisted-appmenus.list"))
|
|
os.mkdir(
|
|
self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone/apps"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-fedora-25-clone/apps"),
|
|
APPTEMPLATE_R2B2.replace("%VMNAME%", "test-fedora-25-clone")
|
|
.replace("%VMDIR%", self.fullpath(
|
|
"vm-templates/test-fedora-25-clone")),
|
|
appmenus_list)
|
|
|
|
def scrypt_encrypt(self, f_name, output_name=None, password='qubes',
|
|
basedir=None):
|
|
if basedir is None:
|
|
basedir = self.backupdir
|
|
if output_name is None:
|
|
output_name = f_name + '.enc'
|
|
if f_name == 'backup-header':
|
|
scrypt_pass = 'backup-header!' + password
|
|
else:
|
|
scrypt_pass = '20161020T123455-1234!{}!{}'.format(f_name, password)
|
|
p = subprocess.Popen(['scrypt', 'enc', '-P', '-t', '0.1',
|
|
os.path.join(basedir, f_name), os.path.join(basedir, output_name)],
|
|
stdin=subprocess.PIPE)
|
|
p.communicate(scrypt_pass.encode())
|
|
assert p.wait() == 0
|
|
return output_name
|
|
|
|
def calculate_hmac(self, f_name, algorithm="sha512", password="qubes"):
|
|
with open(self.fullpath(f_name), "r") as f_data:
|
|
with open(self.fullpath(f_name+".hmac"), "w") as f_hmac:
|
|
subprocess.check_call(
|
|
["openssl", "dgst", "-"+algorithm, "-hmac", password],
|
|
stdin=f_data, stdout=f_hmac)
|
|
|
|
def append_backup_stream(self, f_name, stream, basedir=None):
|
|
if not basedir:
|
|
basedir = self.backupdir
|
|
subprocess.check_call(["tar", "-cO", "--posix", "-C", basedir,
|
|
f_name],
|
|
stdout=stream)
|
|
|
|
def handle_v3_file(self, f_name, subdir, stream, compressed=True,
|
|
encrypted=True):
|
|
# create inner archive
|
|
tar_cmdline = ["tar", "-Pc", '--sparse',
|
|
'-C', self.fullpath(os.path.dirname(f_name)),
|
|
'--xform', 's:^%s:%s\\0:' % (
|
|
os.path.basename(f_name),
|
|
subdir),
|
|
os.path.basename(f_name)
|
|
]
|
|
if compressed:
|
|
tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
|
|
tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
|
|
if encrypted:
|
|
encryptor = subprocess.Popen(
|
|
["openssl", "enc", "-e", "-aes-256-cbc", "-pass", "pass:qubes"],
|
|
stdin=tar.stdout,
|
|
stdout=subprocess.PIPE)
|
|
tar.stdout.close()
|
|
data = encryptor.stdout
|
|
else:
|
|
data = tar.stdout
|
|
|
|
stage1_dir = self.fullpath(os.path.join("stage1", subdir))
|
|
if not os.path.exists(stage1_dir):
|
|
os.makedirs(stage1_dir)
|
|
subprocess.check_call(["split", "--numeric-suffixes",
|
|
"--suffix-length=3",
|
|
"--bytes="+str(100*1024*1024), "-",
|
|
os.path.join(stage1_dir,
|
|
os.path.basename(f_name+"."))],
|
|
stdin=data)
|
|
data.close()
|
|
|
|
for part in sorted(os.listdir(stage1_dir)):
|
|
if not re.match(
|
|
r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
|
|
part):
|
|
continue
|
|
part_with_dir = os.path.join(subdir, part)
|
|
self.calculate_hmac(os.path.join("stage1", part_with_dir))
|
|
self.append_backup_stream(part_with_dir, stream,
|
|
basedir=self.fullpath("stage1"))
|
|
self.append_backup_stream(part_with_dir+".hmac", stream,
|
|
basedir=self.fullpath("stage1"))
|
|
|
|
def handle_v4_file(self, f_name, subdir, stream, compressed=True):
|
|
# create inner archive
|
|
tar_cmdline = ["tar", "-Pc", '--sparse',
|
|
'-C', self.fullpath(os.path.dirname(f_name)),
|
|
'--xform', 's:^%s:%s\\0:' % (
|
|
os.path.basename(f_name),
|
|
subdir),
|
|
os.path.basename(f_name)
|
|
]
|
|
if compressed:
|
|
tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
|
|
tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
|
|
data = tar.stdout
|
|
|
|
stage1_dir = self.fullpath(os.path.join("stage1", subdir))
|
|
if not os.path.exists(stage1_dir):
|
|
os.makedirs(stage1_dir)
|
|
subprocess.check_call(["split", "--numeric-suffixes",
|
|
"--suffix-length=3",
|
|
"--bytes="+str(100*1024*1024), "-",
|
|
os.path.join(stage1_dir,
|
|
os.path.basename(f_name+"."))],
|
|
stdin=data)
|
|
data.close()
|
|
|
|
for part in sorted(os.listdir(stage1_dir)):
|
|
if not re.match(
|
|
r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
|
|
part):
|
|
continue
|
|
part_with_dir = os.path.join(subdir, part)
|
|
f_name = self.scrypt_encrypt(part_with_dir,
|
|
basedir=self.fullpath('stage1'))
|
|
self.append_backup_stream(f_name, stream,
|
|
basedir=self.fullpath("stage1"))
|
|
|
|
def create_v3_backup(self, encrypted=True, compressed=True):
|
|
"""
|
|
Create "backup format 3" backup - used in R2 and R3.0
|
|
|
|
:param encrypted: Should the backup be encrypted
|
|
:param compressed: Should the backup be compressed
|
|
:return:
|
|
"""
|
|
output = open(self.fullpath("backup.bin"), "w")
|
|
with open(self.fullpath("backup-header"), "w") as f:
|
|
f.write(BACKUP_HEADER_R2.format(
|
|
encrypted=str(encrypted),
|
|
compressed=str(compressed)
|
|
))
|
|
self.calculate_hmac("backup-header")
|
|
self.append_backup_stream("backup-header", output)
|
|
self.append_backup_stream("backup-header.hmac", output)
|
|
with open(self.fullpath("qubes.xml"), "wb") as f:
|
|
qubesxml = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
|
|
if encrypted:
|
|
for vmname, subdir in MANGLED_SUBDIRS_R2.items():
|
|
qubesxml = re.sub(r"[a-z-]*/{}".format(vmname).encode(),
|
|
subdir.encode(), qubesxml)
|
|
f.write(qubesxml)
|
|
else:
|
|
f.write(qubesxml)
|
|
|
|
self.handle_v3_file("qubes.xml", "", output, encrypted=encrypted,
|
|
compressed=compressed)
|
|
|
|
self.create_v1_files(r2b2=True)
|
|
for vm_type in ["appvms", "servicevms"]:
|
|
for vm_name in os.listdir(self.fullpath(vm_type)):
|
|
vm_dir = os.path.join(vm_type, vm_name)
|
|
for f_name in os.listdir(self.fullpath(vm_dir)):
|
|
if encrypted:
|
|
subdir = MANGLED_SUBDIRS_R2[vm_name]
|
|
else:
|
|
subdir = vm_dir
|
|
self.handle_v3_file(
|
|
os.path.join(vm_dir, f_name),
|
|
subdir+'/', output,
|
|
compressed=compressed,
|
|
encrypted=encrypted)
|
|
|
|
for vm_name in os.listdir(self.fullpath("vm-templates")):
|
|
vm_dir = os.path.join("vm-templates", vm_name)
|
|
if encrypted:
|
|
subdir = MANGLED_SUBDIRS_R2[vm_name]
|
|
else:
|
|
subdir = vm_dir
|
|
self.handle_v3_file(
|
|
os.path.join(vm_dir, "."),
|
|
subdir+'/', output,
|
|
compressed=compressed,
|
|
encrypted=encrypted)
|
|
|
|
output.close()
|
|
|
|
def create_v4_backup(self, compressed=True):
|
|
"""
|
|
Create "backup format 4" backup - used in R4.0
|
|
|
|
:param compressed: Should the backup be compressed
|
|
:return:
|
|
"""
|
|
output = open(self.fullpath("backup.bin"), "w")
|
|
with open(self.fullpath("backup-header"), "w") as f:
|
|
f.write(BACKUP_HEADER_R4.format(
|
|
compressed=str(compressed)
|
|
))
|
|
self.scrypt_encrypt("backup-header", output_name='backup-header.hmac')
|
|
self.append_backup_stream("backup-header", output)
|
|
self.append_backup_stream("backup-header.hmac", output)
|
|
with open(self.fullpath("qubes.xml"), "wb") as f:
|
|
qubesxml = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
|
|
for vmname, subdir in MANGLED_SUBDIRS_R4.items():
|
|
qubesxml = re.sub(
|
|
r'backup-path">[a-z-]*/{}'.format(vmname).encode(),
|
|
('backup-path">' + subdir).encode(),
|
|
qubesxml)
|
|
f.write(qubesxml)
|
|
|
|
self.handle_v4_file("qubes.xml", "", output, compressed=compressed)
|
|
|
|
self.create_v4_files()
|
|
for vm_type in ["appvms", "vm-templates"]:
|
|
for vm_name in os.listdir(self.fullpath(vm_type)):
|
|
vm_dir = os.path.join(vm_type, vm_name)
|
|
for f_name in os.listdir(self.fullpath(vm_dir)):
|
|
subdir = MANGLED_SUBDIRS_R4[vm_name]
|
|
self.handle_v4_file(
|
|
os.path.join(vm_dir, f_name),
|
|
subdir+'/', output, compressed=compressed)
|
|
|
|
output.close()
|
|
|
|
def setup_expected_calls(self, parsed_qubes_xml, templates_map=None):
|
|
if templates_map is None:
|
|
templates_map = {}
|
|
|
|
extra_vm_list_lines = []
|
|
for name, vm in parsed_qubes_xml['domains'].items():
|
|
if not vm['included_in_backup']:
|
|
continue
|
|
|
|
if self.storage_pool:
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.vm.CreateInPool.' + vm['klass'],
|
|
templates_map.get(vm['template'], vm['template']),
|
|
'name={} label={} pool={}'.format(
|
|
name, vm['label'], self.storage_pool).encode())] = \
|
|
b'0\0'
|
|
else:
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.vm.Create.' + vm['klass'],
|
|
templates_map.get(vm['template'], vm['template']),
|
|
'name={} label={}'.format(name, vm['label']).encode())] =\
|
|
b'0\0'
|
|
extra_vm_list_lines.append(
|
|
'{} class={} state=Halted\n'.format(name, vm['klass']).encode())
|
|
if vm['backup_path']:
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.List', None, None)] = \
|
|
b'0\0root\nprivate\nvolatile\n'
|
|
if vm['klass'] == 'AppVM':
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Info', 'root', None)] = \
|
|
b'0\0' \
|
|
b'pool=default\n' \
|
|
b'vid=' + name.encode() + b'/root\n' \
|
|
b'size=1024\n' \
|
|
b'usage=512\n' \
|
|
b'rw=False\n' \
|
|
b'snap_on_start=True\n' \
|
|
b'save_on_stop=False\n' \
|
|
b'source=\n' \
|
|
b'internal=True\n' \
|
|
b'revisions_to_keep=3\n'
|
|
else:
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Info', 'root', None)] = \
|
|
b'0\0' \
|
|
b'pool=default\n' \
|
|
b'vid=' + name.encode() + b'/root\n' \
|
|
b'size=1024\n' \
|
|
b'usage=512\n' \
|
|
b'rw=True\n' \
|
|
b'snap_on_start=False\n' \
|
|
b'save_on_stop=True\n' \
|
|
b'internal=True\n' \
|
|
b'revisions_to_keep=3\n'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Resize', 'root',
|
|
str(vm.get('root_size', 2097664)).encode())] = \
|
|
b'0\0'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Import', 'root',
|
|
name.encode() + b'/root')] = \
|
|
b'0\0'
|
|
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Info', 'private', None)] = \
|
|
b'0\0' \
|
|
b'pool=default\n' \
|
|
b'vid=' + name.encode() + b'/private\n' \
|
|
b'size=1024\n' \
|
|
b'usage=512\n' \
|
|
b'rw=True\n' \
|
|
b'snap_on_start=False\n' \
|
|
b'save_on_stop=True\n' \
|
|
b'revisions_to_keep=3\n'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Resize', 'private', b'2097152')] = \
|
|
b'0\0'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Import', 'private',
|
|
name.encode() + b'/private')] = \
|
|
b'0\0'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.volume.Info', 'volatile', None)] = \
|
|
b'0\0' \
|
|
b'pool=default\n' \
|
|
b'vid=' + name.encode() + b'/root\n' \
|
|
b'size=1024\n' \
|
|
b'usage=512\n' \
|
|
b'rw=True\n' \
|
|
b'snap_on_start=False\n' \
|
|
b'save_on_stop=False\n' \
|
|
b'revisions_to_keep=3\n'
|
|
|
|
for prop, value in vm['properties'].items():
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.property.Set', prop,
|
|
str(value).encode() if value is not None else b'')] = b'0\0'
|
|
|
|
for bus, devices in vm['devices'].items():
|
|
for (backend_domain, ident), options in devices.items():
|
|
all_options = options.copy()
|
|
all_options['persistent'] = True
|
|
encoded_options = ' '.join('{}={}'.format(key, value) for
|
|
key, value in all_options.items()).encode()
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.device.{}.Attach'.format(bus),
|
|
'{}+{}'.format(backend_domain, ident),
|
|
encoded_options)] = b'0\0'
|
|
|
|
for feature, value in vm['features'].items():
|
|
if value is False:
|
|
value = ''
|
|
elif value is True:
|
|
value = '1'
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.feature.Set', feature,
|
|
str(value).encode())] = b'0\0'
|
|
|
|
for tag in vm['tags']:
|
|
self.app.expected_calls[
|
|
(name, 'admin.vm.tag.Set', tag, None)] = b'0\0'
|
|
|
|
if vm['backup_path']:
|
|
appmenus = (
|
|
b'gnome-terminal.desktop\n'
|
|
b'nautilus.desktop\n'
|
|
b'firefox.desktop\n'
|
|
b'mozilla-thunderbird.desktop\n'
|
|
b'libreoffice-startcenter.desktop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
(name, 'appmenus', None, appmenus)] = b'0\0'
|
|
|
|
orig_admin_vm_list = self.app.expected_calls[
|
|
('dom0', 'admin.vm.List', None, None)]
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
|
|
[orig_admin_vm_list] + \
|
|
[orig_admin_vm_list + b''.join(extra_vm_list_lines)] * \
|
|
len(extra_vm_list_lines)
|
|
|
|
def mock_appmenus(self, queue, vm, stream):
|
|
queue.put((vm.name, 'appmenus', None, stream.read()))
|
|
|
|
def test_210_r2(self):
|
|
self.create_v3_backup(False)
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
|
|
b'0\0dom0 class=AdminVM state=Running\n'
|
|
b'fedora-25 class=TemplateVM state=Halted\n'
|
|
b'testvm class=AppVM state=Running\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.property.Get', 'default_template', None)] = \
|
|
b'0\0default=no type=vm fedora-25'
|
|
self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
|
|
'fedora-20-x64': 'fedora-25'
|
|
})
|
|
firewall_data = (
|
|
'action=accept specialtarget=dns\n'
|
|
'action=accept proto=icmp\n'
|
|
'action=accept proto=tcp dstports=22-22\n'
|
|
'action=accept proto=tcp dstports=9418-9418\n'
|
|
'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
|
|
'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
|
|
'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
|
|
'action=drop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('test-work', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
self.app.expected_calls[
|
|
('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
qubesd_calls_queue = multiprocessing.Queue()
|
|
|
|
patches = [
|
|
mock.patch('qubesadmin.storage.Volume',
|
|
functools.partial(MockVolume, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
|
|
functools.partial(self.mock_appmenus, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.firewall.Firewall',
|
|
functools.partial(MockFirewall, qubesd_calls_queue)),
|
|
]
|
|
for patch in patches:
|
|
patch.start()
|
|
try:
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
finally:
|
|
for patch in patches:
|
|
patch.stop()
|
|
|
|
# retrieve calls from other multiprocess.Process instances
|
|
while not qubesd_calls_queue.empty():
|
|
call_args = qubesd_calls_queue.get()
|
|
self.app.qubesd_call(*call_args)
|
|
qubesd_calls_queue.close()
|
|
|
|
self.assertAllCalled()
|
|
|
|
def test_220_r2_encrypted(self):
|
|
self.create_v3_backup(True)
|
|
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
|
|
b'0\0dom0 class=AdminVM state=Running\n'
|
|
b'fedora-25 class=TemplateVM state=Halted\n'
|
|
b'testvm class=AppVM state=Running\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.property.Get', 'default_template', None)] = \
|
|
b'0\0default=no type=vm fedora-25'
|
|
self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
|
|
'fedora-20-x64': 'fedora-25'
|
|
})
|
|
firewall_data = (
|
|
'action=accept specialtarget=dns\n'
|
|
'action=accept proto=icmp\n'
|
|
'action=accept proto=tcp dstports=22-22\n'
|
|
'action=accept proto=tcp dstports=9418-9418\n'
|
|
'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
|
|
'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
|
|
'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
|
|
'action=drop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('test-work', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
self.app.expected_calls[
|
|
('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
|
|
qubesd_calls_queue = multiprocessing.Queue()
|
|
|
|
patches = [
|
|
mock.patch('qubesadmin.storage.Volume',
|
|
functools.partial(MockVolume, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
|
|
functools.partial(self.mock_appmenus, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.firewall.Firewall',
|
|
functools.partial(MockFirewall, qubesd_calls_queue)),
|
|
]
|
|
for patch in patches:
|
|
patch.start()
|
|
try:
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
finally:
|
|
for patch in patches:
|
|
patch.stop()
|
|
|
|
# retrieve calls from other multiprocess.Process instances
|
|
while not qubesd_calls_queue.empty():
|
|
call_args = qubesd_calls_queue.get()
|
|
self.app.qubesd_call(*call_args)
|
|
qubesd_calls_queue.close()
|
|
|
|
self.assertAllCalled()
|
|
|
|
def test_230_r2_uncompressed(self):
|
|
self.create_v3_backup(False, False)
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
|
|
b'0\0dom0 class=AdminVM state=Running\n'
|
|
b'fedora-25 class=TemplateVM state=Halted\n'
|
|
b'testvm class=AppVM state=Running\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.property.Get', 'default_template', None)] = \
|
|
b'0\0default=no type=vm fedora-25'
|
|
self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
|
|
'fedora-20-x64': 'fedora-25'
|
|
})
|
|
firewall_data = (
|
|
'action=accept specialtarget=dns\n'
|
|
'action=accept proto=icmp\n'
|
|
'action=accept proto=tcp dstports=22-22\n'
|
|
'action=accept proto=tcp dstports=9418-9418\n'
|
|
'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
|
|
'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
|
|
'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
|
|
'action=drop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('test-work', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
self.app.expected_calls[
|
|
('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
|
|
qubesd_calls_queue = multiprocessing.Queue()
|
|
|
|
patches = [
|
|
mock.patch('qubesadmin.storage.Volume',
|
|
functools.partial(MockVolume, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
|
|
functools.partial(self.mock_appmenus, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.firewall.Firewall',
|
|
functools.partial(MockFirewall, qubesd_calls_queue)),
|
|
]
|
|
for patch in patches:
|
|
patch.start()
|
|
try:
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
finally:
|
|
for patch in patches:
|
|
patch.stop()
|
|
|
|
# retrieve calls from other multiprocess.Process instances
|
|
while not qubesd_calls_queue.empty():
|
|
call_args = qubesd_calls_queue.get()
|
|
self.app.qubesd_call(*call_args)
|
|
qubesd_calls_queue.close()
|
|
|
|
self.assertAllCalled()
|
|
|
|
@unittest.skipUnless(spawn.find_executable('scrypt'),
|
|
"scrypt not installed")
|
|
def test_230_r4(self):
|
|
self.create_v4_backup(False)
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
|
|
b'0\0dom0 class=AdminVM state=Running\n'
|
|
b'fedora-25 class=TemplateVM state=Halted\n'
|
|
b'testvm class=AppVM state=Running\n'
|
|
b'sys-net class=AppVM state=Running\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.property.Get', 'default_template', None)] = \
|
|
b'0\0default=no type=vm fedora-25'
|
|
self.app.expected_calls[
|
|
('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
|
|
b'0\0default=no type=bool True'
|
|
self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
|
|
'debian-8': 'fedora-25'
|
|
})
|
|
firewall_data = (
|
|
'action=accept specialtarget=dns\n'
|
|
'action=accept proto=icmp\n'
|
|
'action=accept proto=tcp dstports=22-22\n'
|
|
'action=accept proto=tcp dsthost=www.qubes-os.org '
|
|
'dstports=443-443\n'
|
|
'action=accept proto=tcp dst4=192.168.0.0/24\n'
|
|
'action=drop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('test-work', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
|
|
qubesd_calls_queue = multiprocessing.Queue()
|
|
|
|
patches = [
|
|
mock.patch('qubesadmin.storage.Volume',
|
|
functools.partial(MockVolume, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
|
|
functools.partial(self.mock_appmenus, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.firewall.Firewall',
|
|
functools.partial(MockFirewall, qubesd_calls_queue)),
|
|
]
|
|
for patch in patches:
|
|
patch.start()
|
|
try:
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
finally:
|
|
for patch in patches:
|
|
patch.stop()
|
|
|
|
# retrieve calls from other multiprocess.Process instances
|
|
while not qubesd_calls_queue.empty():
|
|
call_args = qubesd_calls_queue.get()
|
|
self.app.qubesd_call(*call_args)
|
|
qubesd_calls_queue.close()
|
|
|
|
self.assertAllCalled()
|
|
|
|
@unittest.skipUnless(spawn.find_executable('scrypt'),
|
|
"scrypt not installed")
|
|
def test_230_r4_compressed(self):
|
|
self.create_v4_backup(True)
|
|
|
|
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
|
|
b'0\0dom0 class=AdminVM state=Running\n'
|
|
b'fedora-25 class=TemplateVM state=Halted\n'
|
|
b'testvm class=AppVM state=Running\n'
|
|
b'sys-net class=AppVM state=Running\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('dom0', 'admin.property.Get', 'default_template', None)] = \
|
|
b'0\0default=no type=vm fedora-25'
|
|
self.app.expected_calls[
|
|
('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
|
|
b'0\0default=no type=bool True'
|
|
self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
|
|
'debian-8': 'fedora-25'
|
|
})
|
|
firewall_data = (
|
|
'action=accept specialtarget=dns\n'
|
|
'action=accept proto=icmp\n'
|
|
'action=accept proto=tcp dstports=22-22\n'
|
|
'action=accept proto=tcp dsthost=www.qubes-os.org '
|
|
'dstports=443-443\n'
|
|
'action=accept proto=tcp dst4=192.168.0.0/24\n'
|
|
'action=drop\n'
|
|
)
|
|
self.app.expected_calls[
|
|
('test-work', 'admin.vm.firewall.Set', None,
|
|
firewall_data.encode())] = b'0\0'
|
|
|
|
qubesd_calls_queue = multiprocessing.Queue()
|
|
|
|
patches = [
|
|
mock.patch('qubesadmin.storage.Volume',
|
|
functools.partial(MockVolume, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
|
|
functools.partial(self.mock_appmenus, qubesd_calls_queue)),
|
|
mock.patch(
|
|
'qubesadmin.firewall.Firewall',
|
|
functools.partial(MockFirewall, qubesd_calls_queue)),
|
|
]
|
|
for patch in patches:
|
|
patch.start()
|
|
try:
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
finally:
|
|
for patch in patches:
|
|
patch.stop()
|
|
|
|
# retrieve calls from other multiprocess.Process instances
|
|
while not qubesd_calls_queue.empty():
|
|
call_args = qubesd_calls_queue.get()
|
|
self.app.qubesd_call(*call_args)
|
|
qubesd_calls_queue.close()
|
|
|
|
self.assertAllCalled()
|
|
|
|
|
|
class TC_11_BackupCompatibilityIntoLVM(TC_10_BackupCompatibility):
|
|
storage_pool = 'some-pool'
|
|
|
|
|
|
def restore_backup(self, source=None, appvm=None, options=None,
|
|
expect_errors=None, manipulate_restore_info=None,
|
|
passphrase='qubes'):
|
|
if options is None:
|
|
options = {}
|
|
options['override_pool'] = self.storage_pool
|
|
super(TC_11_BackupCompatibilityIntoLVM, self).restore_backup(source,
|
|
appvm, options, expect_errors, manipulate_restore_info)
|