mirror of
https://github.com/vyos/vyos-build.git
synced 2025-10-01 20:28:40 +02:00
config: T4919: Add emulated TPM encryption test
This commit is contained in:
parent
3920af2500
commit
63a8f9d3c6
8
Jenkinsfile
vendored
8
Jenkinsfile
vendored
@ -119,6 +119,14 @@ pipeline {
|
|||||||
sh "sudo make testraid"
|
sh "sudo make testraid"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
stage('Smoketests for TPM config encryption') {
|
||||||
|
when {
|
||||||
|
expression { fileExists 'build/live-image-amd64.hybrid.iso' }
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh "sudo make testtpm"
|
||||||
|
}
|
||||||
|
}
|
||||||
stage('Smoketests') {
|
stage('Smoketests') {
|
||||||
when {
|
when {
|
||||||
expression { return params.TEST_SMOKETESTS }
|
expression { return params.TEST_SMOKETESTS }
|
||||||
|
|||||||
5
Makefile
5
Makefile
@ -64,6 +64,11 @@ qemu-live: checkiso
|
|||||||
oci: checkiso
|
oci: checkiso
|
||||||
scripts/iso-to-oci build/live-image-amd64.hybrid.iso
|
scripts/iso-to-oci build/live-image-amd64.hybrid.iso
|
||||||
|
|
||||||
|
.PHONY: testtpm
|
||||||
|
.ONESHELL:
|
||||||
|
testtpm: checkiso
|
||||||
|
scripts/check-qemu-install --debug --tpmtest build/live-image-amd64.hybrid.iso
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
.ONESHELL:
|
.ONESHELL:
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@ -96,6 +96,9 @@ RUN apt-get update && apt-get install -y \
|
|||||||
debootstrap \
|
debootstrap \
|
||||||
live-build
|
live-build
|
||||||
|
|
||||||
|
# Packages for TPM test
|
||||||
|
RUN apt-get update && apt-get install -y swtpm
|
||||||
|
|
||||||
# Syslinux and Grub2 is only supported on x86 and x64 systems
|
# Syslinux and Grub2 is only supported on x86 and x64 systems
|
||||||
RUN if dpkg-architecture -ii386 || dpkg-architecture -iamd64; then \
|
RUN if dpkg-architecture -ii386 || dpkg-architecture -iamd64; then \
|
||||||
apt-get update && apt-get install -y \
|
apt-get update && apt-get install -y \
|
||||||
|
|||||||
@ -50,6 +50,7 @@ from datetime import datetime
|
|||||||
|
|
||||||
EXCEPTION = 0
|
EXCEPTION = 0
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
|
tpm_folder = '/tmp/vyos_tpm_test'
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Install and start a test VyOS vm.')
|
parser = argparse.ArgumentParser(description='Install and start a test VyOS vm.')
|
||||||
parser.add_argument('iso', help='ISO file to install')
|
parser.add_argument('iso', help='ISO file to install')
|
||||||
@ -73,6 +74,8 @@ parser.add_argument('--no-interfaces', help='Execute testsuite without interface
|
|||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
parser.add_argument('--configtest', help='Execute load/commit config tests',
|
parser.add_argument('--configtest', help='Execute load/commit config tests',
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
|
parser.add_argument('--tpmtest', help='Execute TPM encrypted config tests',
|
||||||
|
action='store_true', default=False)
|
||||||
parser.add_argument('--qemu-cmd', help='Only generate QEMU launch command',
|
parser.add_argument('--qemu-cmd', help='Only generate QEMU launch command',
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
|
|
||||||
@ -113,7 +116,7 @@ def get_half_cpus():
|
|||||||
cpu /= 2
|
cpu /= 2
|
||||||
return int(cpu)
|
return int(cpu)
|
||||||
|
|
||||||
def get_qemu_cmd(name, enable_kvm, enable_uefi, disk_img, raid=None, iso_img=None):
|
def get_qemu_cmd(name, enable_kvm, enable_uefi, disk_img, raid=None, iso_img=None, tpm=False):
|
||||||
kvm = "-enable-kvm"
|
kvm = "-enable-kvm"
|
||||||
cpu = "-cpu host"
|
cpu = "-cpu host"
|
||||||
if not enable_kvm:
|
if not enable_kvm:
|
||||||
@ -173,6 +176,11 @@ def get_qemu_cmd(name, enable_kvm, enable_uefi, disk_img, raid=None, iso_img=Non
|
|||||||
cmd += f' -drive format=raw,file={raid},if=none,media=disk,id=drive-hd2,readonly=off' \
|
cmd += f' -drive format=raw,file={raid},if=none,media=disk,id=drive-hd2,readonly=off' \
|
||||||
f' -device scsi-hd,bus=scsi0.0,drive=drive-hd2,id=hd2,bootindex={bootindex}'
|
f' -device scsi-hd,bus=scsi0.0,drive=drive-hd2,id=hd2,bootindex={bootindex}'
|
||||||
|
|
||||||
|
if tpm:
|
||||||
|
cmd += f' -chardev socket,id=chrtpm,path={tpm_folder}/swtpm-sock' \
|
||||||
|
' -tpmdev emulator,id=tpm0,chardev=chrtpm' \
|
||||||
|
' -device tpm-tis,tpmdev=tpm0'
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
def shutdownVM(c, log, message=''):
|
def shutdownVM(c, log, message=''):
|
||||||
@ -270,6 +278,22 @@ if args.raid:
|
|||||||
# must be called after the raid disk as args.disk name is altered in the RAID path
|
# must be called after the raid disk as args.disk name is altered in the RAID path
|
||||||
gen_disk(args.disk)
|
gen_disk(args.disk)
|
||||||
|
|
||||||
|
# Create software emulated TPM
|
||||||
|
def start_swtpm():
|
||||||
|
if not os.path.exists(tpm_folder):
|
||||||
|
os.mkdir(tpm_folder)
|
||||||
|
|
||||||
|
def swtpm_thread():
|
||||||
|
c = subprocess.check_output([
|
||||||
|
'swtpm', 'socket', '--tpmstate', f'dir={tpm_folder}',
|
||||||
|
'--ctrl', f'type=unixio,path={tpm_folder}/swtpm-sock', '--tpm2', '--log', 'level=1'
|
||||||
|
])
|
||||||
|
|
||||||
|
from multiprocessing import Process
|
||||||
|
tpm_process = Process(target=swtpm_thread)
|
||||||
|
tpm_process.start()
|
||||||
|
return tpm_process
|
||||||
|
|
||||||
if args.qemu_cmd:
|
if args.qemu_cmd:
|
||||||
tmp = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid, args.iso)
|
tmp = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid, args.iso)
|
||||||
os.system(tmp)
|
os.system(tmp)
|
||||||
@ -338,11 +362,14 @@ try:
|
|||||||
shutdownVM(c, log, 'Shutting down installation system')
|
shutdownVM(c, log, 'Shutting down installation system')
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
|
if args.tpmtest:
|
||||||
|
tpm_process = start_swtpm()
|
||||||
|
|
||||||
#################################################
|
#################################################
|
||||||
# Booting installed system
|
# Booting installed system
|
||||||
#################################################
|
#################################################
|
||||||
log.info('Booting installed system')
|
log.info('Booting installed system')
|
||||||
cmd = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid)
|
cmd = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid, tpm=args.tpmtest)
|
||||||
log.debug(f'Executing command: {cmd}')
|
log.debug(f'Executing command: {cmd}')
|
||||||
c = pexpect.spawn(cmd, logfile=stl)
|
c = pexpect.spawn(cmd, logfile=stl)
|
||||||
|
|
||||||
@ -396,7 +423,147 @@ try:
|
|||||||
#################################################
|
#################################################
|
||||||
# Executing test-suite
|
# Executing test-suite
|
||||||
#################################################
|
#################################################
|
||||||
if args.raid:
|
if args.tpmtest:
|
||||||
|
def verify_mount():
|
||||||
|
# Verify encrypted and mounted
|
||||||
|
c.sendline('mount | grep vyos_config')
|
||||||
|
c.expect('/dev/mapper/vyos_config on /config.*')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
def verify_config():
|
||||||
|
# Verify encrypted config is loaded
|
||||||
|
c.sendline('show config commands | cat')
|
||||||
|
c.expect('set system option performance \'latency\'')
|
||||||
|
c.expect('set system option reboot-on-panic')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
log.info('Running TPM encrypted config tests')
|
||||||
|
|
||||||
|
tpm_timeout = 600 # Give it 10 mins to encrypt
|
||||||
|
|
||||||
|
# Verify TPM is loaded
|
||||||
|
c.sendline('ls /dev/tpm0')
|
||||||
|
c.expect('/dev/tpm0')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
# Create recovery key
|
||||||
|
import string
|
||||||
|
from random import choices
|
||||||
|
test_recovery_key = ''.join(choices(string.ascii_uppercase + string.digits, k=32))
|
||||||
|
|
||||||
|
log.info('Encrypting config to TPM')
|
||||||
|
c.sendline('encryption enable')
|
||||||
|
c.expect('Automatically generate a recovery key\?.*')
|
||||||
|
c.sendline('n')
|
||||||
|
c.expect('Enter recovery key: ')
|
||||||
|
c.sendline(test_recovery_key)
|
||||||
|
c.expect('Enter size of encrypted config partition.*', timeout=30)
|
||||||
|
c.sendline('32')
|
||||||
|
c.expect('Encrypted config volume has been enabled', timeout=tpm_timeout)
|
||||||
|
c.expect('Backup the recovery key in a safe place!')
|
||||||
|
c.expect(f'Recovery key: {test_recovery_key}')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
verify_mount()
|
||||||
|
|
||||||
|
# Add some non-default config nodes this encrypted config
|
||||||
|
log.info('Adding nodes for encrypted config test')
|
||||||
|
c.sendline('configure')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('set system option performance latency')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('set system option reboot-on-panic')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('commit')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('save')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('exit')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
# Shutdown VM
|
||||||
|
shutdownVM(c, log, 'Shutdown VM after TPM encryption')
|
||||||
|
|
||||||
|
# Shutdown kills swtpm
|
||||||
|
tpm_process.join()
|
||||||
|
tpm_process.close()
|
||||||
|
|
||||||
|
# Start emulator again
|
||||||
|
tpm_process = start_swtpm()
|
||||||
|
|
||||||
|
# Booting back into VM
|
||||||
|
log.info('Booting TPM-backed system')
|
||||||
|
cmd = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid, tpm=args.tpmtest)
|
||||||
|
log.debug(f'Executing command: {cmd}')
|
||||||
|
c = pexpect.spawn(cmd, logfile=stl)
|
||||||
|
|
||||||
|
try:
|
||||||
|
c.expect('The highlighted entry will be executed automatically in', timeout=10)
|
||||||
|
c.sendline('')
|
||||||
|
except pexpect.TIMEOUT:
|
||||||
|
log.warning('Did not find GRUB countdown window, ignoring')
|
||||||
|
|
||||||
|
# Check for vyos-router message
|
||||||
|
c.expect('.*Mounted encrypted config volume', timeout=120)
|
||||||
|
|
||||||
|
loginVM(c, log)
|
||||||
|
|
||||||
|
verify_mount()
|
||||||
|
verify_config()
|
||||||
|
|
||||||
|
# Shutdown VM
|
||||||
|
shutdownVM(c, log, 'Shutdown VM for TPM fail test')
|
||||||
|
|
||||||
|
# Clear swtpm
|
||||||
|
from glob import glob
|
||||||
|
for f in glob(f'{tpm_folder}/*'):
|
||||||
|
os.remove(f)
|
||||||
|
|
||||||
|
# Shutdown kills swtpm
|
||||||
|
tpm_process.join()
|
||||||
|
tpm_process.close()
|
||||||
|
|
||||||
|
# Start emulator again
|
||||||
|
tpm_process = start_swtpm()
|
||||||
|
|
||||||
|
# Booting back into VM
|
||||||
|
log.info('Booting system with cleared TPM')
|
||||||
|
cmd = get_qemu_cmd('TESTVM', kvm, args.uefi, args.disk, diskname_raid, tpm=args.tpmtest)
|
||||||
|
log.debug(f'Executing command: {cmd}')
|
||||||
|
c = pexpect.spawn(cmd, logfile=stl)
|
||||||
|
|
||||||
|
try:
|
||||||
|
c.expect('The highlighted entry will be executed automatically in', timeout=10)
|
||||||
|
c.sendline('')
|
||||||
|
except pexpect.TIMEOUT:
|
||||||
|
log.warning('Did not find GRUB countdown window, ignoring')
|
||||||
|
|
||||||
|
c.expect('.*Encrypted config volume has not been mounted', timeout=120)
|
||||||
|
|
||||||
|
loginVM(c, log)
|
||||||
|
|
||||||
|
# Test loading config with recovery key
|
||||||
|
c.sendline('encryption load')
|
||||||
|
c.expect('Enter recovery key: ')
|
||||||
|
c.sendline(test_recovery_key)
|
||||||
|
c.expect('Encrypted config volume has been mounted', timeout=120)
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
verify_mount()
|
||||||
|
|
||||||
|
log.info('Loading encrypted config.boot')
|
||||||
|
c.sendline('configure')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('load /config/config.boot')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('commit')
|
||||||
|
c.expect(cfg_mode_prompt)
|
||||||
|
c.sendline('exit')
|
||||||
|
c.expect(op_mode_prompt)
|
||||||
|
|
||||||
|
verify_config()
|
||||||
|
|
||||||
|
elif args.raid:
|
||||||
# Verify RAID subsystem - by deleting a disk and re-create the array
|
# Verify RAID subsystem - by deleting a disk and re-create the array
|
||||||
# from scratch
|
# from scratch
|
||||||
c.sendline('cat /proc/mdstat')
|
c.sendline('cat /proc/mdstat')
|
||||||
@ -571,6 +738,10 @@ except Exception:
|
|||||||
#################################################
|
#################################################
|
||||||
log.info("Cleaning up")
|
log.info("Cleaning up")
|
||||||
|
|
||||||
|
if tpm_process:
|
||||||
|
tpm_process.terminate()
|
||||||
|
tpm_process.join()
|
||||||
|
|
||||||
if not args.keep:
|
if not args.keep:
|
||||||
log.info(f'Removing disk file: {args.disk}')
|
log.info(f'Removing disk file: {args.disk}')
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user