container: Add support for retrieving GPG keys using wget

- Check for errors when running the provisioning script.

- Check for errors when installing missing packages.

Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
fliu 2023-07-13 01:33:37 +00:00 committed by Sunil Mohan Adapa
parent de856b0d9c
commit da1e4ed505
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2

View File

@ -151,9 +151,11 @@ URLS = URLS_AMD64
TRUSTED_KEYS = ['D4B069124FCF43AA1FCD7FBC2ACFC1E15AF82D8C']
KEY_SERVER = 'keyserver.ubuntu.com'
KEY_SERVER_HTTPS_API = \
'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x{key_id}'
PROVISION_SCRIPT = '''
set -x
set -xe pipefail
cd /freedombox/
@ -185,8 +187,8 @@ sudo chmod a+rw geckodriver.log
sudo mkdir -p .pytest_cache/
sudo chmod --recursive a+rw .pytest_cache/
sudo chmod a+w /freedombox
sudo chmod --recursive --silent a+w htmlcov
sudo chmod --silent a+w .coverage
sudo chmod --recursive --silent a+w htmlcov || true
sudo chmod --silent a+w .coverage || true
exit 0
''' # noqa
@ -290,6 +292,8 @@ def parse_arguments():
help='Distribution of the image to download and setup')
subparser.add_argument('--image-size', default='16G',
help='Disk image size to resize to after download')
subparser.add_argument('--hkp-client', choices=('gpg', 'wget'),
default='gpg', help='Client for key retrieval')
# Print IP address
subparser = subparsers.add_parser(
@ -339,6 +343,8 @@ def parse_arguments():
subparser.add_argument('--distribution', choices=distributions,
default=default_distribution,
help='Distribution of the image to update')
subparser.add_argument('--hkp-client', choices=('gpg', 'wget'),
default='gpg', help='Client for key retrieval')
# Display help message when no args are passed
if len(sys.argv) == 1:
@ -383,6 +389,7 @@ def _verify_dependencies():
'sgdisk': 'gdisk',
'btrfs': 'btrfs-progs',
'nmcli': 'network-manager',
'dnsmasq': 'dnsmasq',
'ssh': 'openssh-client',
'ssh-keygen': 'openssh-client',
}
@ -405,8 +412,8 @@ def _verify_dependencies():
sys.exit(1)
logger.info('Running apt for missing packages: %s',
' '.join(missing_commands))
subprocess.run(['sudo', 'apt', 'install'] + missing_packages, check=False)
' '.join(missing_packages))
subprocess.run(['sudo', 'apt', 'install'] + missing_packages, check=True)
def _get_systemd_nspawn_version():
@ -440,7 +447,30 @@ def _download_file(url, target_file, force=False):
partial_file.rename(target_file)
def _verify_signature(data_file, signature_file):
def _receive_keys_with_gpg(gpg_home, key_ids):
"""Use gpg to retrieve and import a list of keys."""
subprocess.run([
'gpg', '--quiet', '--homedir',
str(gpg_home), '--keyserver', KEY_SERVER, '--recv-keys'
] + key_ids, check=True)
def _receive_keys_with_wget(gpg_home, key_ids):
"""Use wget to retrieve a list of keys and import them."""
for key_id in key_ids:
# Download public key
logger.info('Getting public key %s', key_id)
url = KEY_SERVER_HTTPS_API.format(key_id=key_id)
wget_result = subprocess.run(
['wget', '--quiet', '--output-document=-', url], check=True,
capture_output=True)
public_key = wget_result.stdout
subprocess.run(
['gpg', '--quiet', '--homedir',
str(gpg_home), '--import=-'], input=public_key, check=True)
def _verify_signature(hkp_client, data_file, signature_file):
"""Verify the detached signature on a file using GPG."""
verified_file = signature_file.with_suffix(signature_file.suffix +
'.verified')
@ -452,10 +482,11 @@ def _verify_signature(data_file, signature_file):
gpg_home.chmod(0o700)
logger.info('Receiving GPG keys')
subprocess.run([
'gpg', '--quiet', '--homedir',
str(gpg_home), '--keyserver', KEY_SERVER, '--recv-keys'
] + TRUSTED_KEYS, check=True)
if hkp_client == 'wget':
_receive_keys_with_wget(gpg_home, TRUSTED_KEYS)
else:
_receive_keys_with_gpg(gpg_home, TRUSTED_KEYS)
process = subprocess.run(
['gpg', '--quiet', '--homedir',
str(gpg_home), '--armor', '--export'] + TRUSTED_KEYS, check=True,
@ -518,7 +549,7 @@ def _get_overlay_folder(distribution):
return folder.resolve()
def _download_disk_image(distribution, force=False):
def _download_disk_image(distribution, hkp_client, force=False):
"""Download and unpack FreedomBox disk image."""
work_directory.mkdir(exist_ok=True)
@ -530,7 +561,7 @@ def _download_disk_image(distribution, force=False):
signature_file = target_file.with_suffix(target_file.suffix + '.sig')
_download_file(url + '.sig', signature_file, force=force)
_verify_signature(target_file, signature_file)
_verify_signature(hkp_client, target_file, signature_file)
return _extract_image(target_file)
@ -547,6 +578,7 @@ def _get_partition_info(image_file):
if line.startswith('Partition Table:'):
partition_table_type = line.partition(': ')[2]
logger.info('Main partition: %s', last_partition_number)
return partition_table_type, last_partition_number
@ -750,6 +782,7 @@ def _setup(image_file, distribution):
_setup_nm_connection(distribution)
setup_file.touch()
logger.info('Setup completed')
def _create_nspawn_machine(image_file, distribution):
@ -820,9 +853,10 @@ def _launch(image_file, distribution):
logger.info('Bringing up host network connection: %s',
f'fbx-{distribution}-shared')
# This command requires dnsmasq
subprocess.run(
['sudo', 'nmcli', 'connection', 'up', f'fbx-{distribution}-shared'],
stdout=subprocess.DEVNULL, check=False)
stdout=subprocess.DEVNULL, check=True)
def _stop(distribution):
@ -907,6 +941,7 @@ def _provision(image_file, distribution):
input=PROVISION_SCRIPT.encode())
provision_file.touch()
logger.info('Provision completed')
def _print_banner(distribution):
@ -1023,7 +1058,8 @@ def subcommand_up(arguments):
_verify_dependencies()
_get_systemd_nspawn_version()
image_file = _download_disk_image(arguments.distribution)
image_file = _download_disk_image(arguments.distribution,
arguments.hkp_client)
_resize_disk_image(image_file, arguments.image_size)
_setup(image_file, arguments.distribution)
_launch(image_file, arguments.distribution)
@ -1077,7 +1113,8 @@ def subcommand_update(arguments):
"""Update the disk image."""
if _is_update_required(arguments.distribution):
logger.info("Updating...")
_download_disk_image(arguments.distribution, force=True)
_download_disk_image(arguments.distribution, arguments.hkp_client,
force=True)
else:
logger.info("Already using the latest image")