From d54bf635cd928e2d2e224995806fcc3c5ea5dbce Mon Sep 17 00:00:00 2001 From: David Runge Date: Thu, 6 May 2021 00:56:02 +0200 Subject: [PATCH] mkarchiso: Add buildmode to export netboot artifacts archiso/mkarchiso: Implement a buildmode to export artifacts required for netboot with IPXE. When providing the buildmode 'netboot' via profiledef.sh or the `-m` option, all targets necessary to create an ISO medium are built, but the components required for netboot are exported to the output dir. Optionally, it is possible to provide a set of certificates for codsigning using the `-c` option, where the first file is considered as the signer certificate and the second as the key. Add `_export_netboot_artifacts()` to copy build artifacts to the output directory. Add `_sign_netboot_artifacts()` to codesign the netboot artifacts in the work directory. Add `_validate_requirements_buildmode_netboot()` to check for openssl. Add `_build_iso_base()` to implement common function calls between the 'iso' and the 'netboot' buildmodes. Add `_build_buildmode_netboot()` to make use of `_build_iso_base()`, (optionally) `_sign_netboot_artifacts()` and `_export_netboot_artifacts()`. Change `_build_buildmode_iso()` to make use of `_build_iso_base()`. Add `-c` as an option to mkarchiso to read in a list of file names. Unify the output of `_usage()` by using the same definition style for lists of strings provided to options that accept them (e.g. `-c`, `-m`, `-p`). Closes #128 --- archiso/mkarchiso | 148 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 115 insertions(+), 33 deletions(-) diff --git a/archiso/mkarchiso b/archiso/mkarchiso index 683f90e..ff6aec5 100755 --- a/archiso/mkarchiso +++ b/archiso/mkarchiso @@ -34,6 +34,8 @@ buildmodes=() bootmodes=() airootfs_image_type="" airootfs_image_tool_options=() +cert_list=() +sign_netboot_artifacts="" declare -A file_permissions=() @@ -96,13 +98,17 @@ usage: ${app_name} [options] Default: '${iso_label}' -P Set the ISO publisher Default: '${iso_publisher}' + -c [cert ..] Provide certificates for codesigning of netboot artifacts + Multiple files are provided as quoted, space delimited list. + The first file is considered as the signing certificate, + the second as the key. -g Set the PGP key ID to be used for signing the rootfs image -h This message - -m [build modes] Build modes to use (valid modes are: 'bootstrap' and 'iso'). + -m [mode ..] Build mode(s) to use (valid modes are: 'bootstrap', 'iso' and 'netboot'). Multiple build modes are provided as quoted, space delimited list. -o Set the output directory Default: '${out_dir}' - -p PACKAGE(S) Package(s) to install. + -p [package ..] Package(s) to install. Multiple packages are provided as quoted, space delimited list. -v Enable verbose output -w Set the working directory @@ -127,6 +133,7 @@ _show_config() { _msg_info " Current build mode: ${buildmode}" _msg_info " Build modes: ${buildmodes[*]}" _msg_info " GPG key: ${gpg_key:-None}" + _msg_info "Code signing certificates: ${cert_list[*]}" _msg_info " Profile: ${profile}" _msg_info "Pacman configuration file: ${pacman_conf}" _msg_info " Image file name: ${image_name:-None}" @@ -650,6 +657,34 @@ _prepare_airootfs_image() { fi } +# export build artifacts for netboot +_export_netboot_artifacts() { + _msg_info "Exporting netboot artifacts..." + install -d -m 0755 "${out_dir}" + cp -a -- "${isofs_dir}/${install_dir}/" "${out_dir}/" + _msg_info "Done!" + du -h -- "${out_dir}/${install_dir}" +} + +# sign build artifacts for netboot +_sign_netboot_artifacts() { + local _file _dir + _msg_info "Signing netboot artifacts..." + _dir="${isofs_dir}/${install_dir}/" + for _file in "${_dir}/boot/"*ucode.img "${_dir}/boot/${arch}/vmlinuz-"* "${_dir}/boot/${arch}/initramfs-"*.img; do + openssl cms \ + -sign \ + -binary \ + -noattr \ + -in "${_file}" \ + -signer "${cert_list[0]}" \ + -inkey "${cert_list[1]}" \ + -outform DER \ + -out "${_file}".ipxe.sig + done + _msg_info "Done!" +} + _validate_requirements_airootfs_image_type_squashfs() { if ! command -v mksquashfs &> /dev/null; then (( validation_error=validation_error+1 )) @@ -703,6 +738,14 @@ _validate_requirements_buildmode_iso() { fi } +_validate_requirements_buildmode_netboot() { + _validate_requirements_buildmode_all + if ! command -v openssl &> /dev/null; then + (( validation_error=validation_error+1 )) + _msg_error "Validating build mode '${_buildmode}': openssl is not available on this host. Install 'openssl'!" 0 + fi +} + # SYSLINUX El Torito _add_xorrisofs_options_bios.syslinux.eltorito() { xorrisofs_options+=( @@ -884,9 +927,10 @@ _read_profile() { # Validate set options _validate_options() { - local validation_error=0 bootmode _buildmode + local validation_error=0 bootmode _cert _buildmode local pkg_list_from_file=() local bootstrap_pkg_list_from_file=() + local _override_cert_list=() _msg_info "Validating options..." # Check if the package list file exists and read packages from it @@ -917,7 +961,23 @@ _validate_options() { _msg_error "Bootstrap packages file '${bootstrap_packages}' does not exist." 0 fi fi - + if [[ "${sign_netboot_artifacts}" == "y" ]]; then + # Check if the certificate files exist + for _cert in "${cert_list[@]}"; do + if [[ -e "${_cert}" ]]; then + _override_cert_list+=("$(realpath -- "${_cert}")") + else + (( validation_error=validation_error+1 )) + _msg_error "File '${_cert}' does not exist." 0 + fi + done + cert_list=("${_override_cert_list[@]}") + # Check if there are at least two certificate files + if (( "${#cert_list[@]}" < 2 )); then + (( validation_error=validation_error+1 )) + _msg_error "Two certificates are required for codesigning, but '${cert_list[*]}' is provided." 0 + fi + fi # Check if pacman configuration file exists if [[ ! -e "${pacman_conf}" ]]; then (( validation_error=validation_error+1 )) @@ -1017,6 +1077,10 @@ _set_overrides() { install_dir="${app_name}" fi [[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key" + if [[ -v override_cert_list ]]; then + sign_netboot_artifacts="y" + fi + [[ ! -v override_cert_list ]] || cert_list+=("${override_cert_list[@]}") if [[ -v override_quiet ]]; then quiet="$override_quiet" elif [[ -z "$quiet" ]]; then @@ -1079,38 +1143,15 @@ _make_pkglist() { _msg_info "Done!" } -_build_buildmode_bootstrap() { - local image_name="${iso_name}-bootstrap-${iso_version}-${arch}.tar.gz" - local run_once_mode="${buildmode}" - local buildmode_packages="${bootstrap_packages}" - local buildmode_pkg_list=() - - # Set the package list to use - buildmode_pkg_list=("${bootstrap_pkg_list[@]}") - # Set up essential directory paths - pacstrap_dir="${work_dir}/${arch}/bootstrap/root.${arch}" - [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" - install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}" - - [[ "${quiet}" == "y" ]] || _show_config - _run_once _make_pacman_conf - _run_once _make_packages - _run_once _make_version - _run_once _make_pkglist - _run_once _cleanup_pacstrap_dir - _run_once _build_bootstrap_image -} - -_build_buildmode_iso() { - local image_name="${iso_name}-${iso_version}-${arch}.iso" - local run_once_mode="${buildmode}" +# build the base for an ISO and/or a netboot target +_build_iso_base() { + local run_once_mode="base" local buildmode_packages="${packages}" - local buildmode_pkg_list=() + # Set the package list to use + local buildmode_pkg_list=("${pkg_list[@]}") # Set up essential directory paths pacstrap_dir="${work_dir}/${arch}/airootfs" isofs_dir="${work_dir}/iso" - # Set the package list to use - buildmode_pkg_list=("${pkg_list[@]}") # Create working directory [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" @@ -1132,6 +1173,46 @@ _build_buildmode_iso() { _make_bootmodes _run_once _cleanup_pacstrap_dir _run_once _prepare_airootfs_image +} + +# Build the bootstrap buildmode +_build_buildmode_bootstrap() { + local image_name="${iso_name}-bootstrap-${iso_version}-${arch}.tar.gz" + local run_once_mode="${buildmode}" + local buildmode_packages="${bootstrap_packages}" + # Set the package list to use + local buildmode_pkg_list=("${bootstrap_pkg_list[@]}") + + # Set up essential directory paths + pacstrap_dir="${work_dir}/${arch}/bootstrap/root.${arch}" + [[ -d "${work_dir}" ]] || install -d -- "${work_dir}" + install -d -m 0755 -o 0 -g 0 -- "${pacstrap_dir}" + + [[ "${quiet}" == "y" ]] || _show_config + _run_once _make_pacman_conf + _run_once _make_packages + _run_once _make_version + _run_once _make_pkglist + _run_once _cleanup_pacstrap_dir + _run_once _build_bootstrap_image +} + +# Build the netboot buildmode +_build_buildmode_netboot() { + local run_once_mode="${buildmode}" + + _run_once _build_iso_base + if [[ -v cert_list ]]; then + _run_once _sign_netboot_artifacts + fi + _run_once _export_netboot_artifacts +} + +# Build the ISO buildmode +_build_buildmode_iso() { + local image_name="${iso_name}-${iso_version}-${arch}.iso" + local run_once_mode="${buildmode}" + _run_once _build_iso_base _run_once _build_iso_image } @@ -1145,7 +1226,7 @@ _build() { done } -while getopts 'p:C:L:P:A:D:w:m:o:g:vh?' arg; do +while getopts 'c:p:C:L:P:A:D:w:m:o:g:vh?' arg; do case "${arg}" in p) read -r -a override_pkg_list <<< "${OPTARG}" ;; C) override_pacman_conf="${OPTARG}" ;; @@ -1153,6 +1234,7 @@ while getopts 'p:C:L:P:A:D:w:m:o:g:vh?' arg; do P) override_iso_publisher="${OPTARG}" ;; A) override_iso_application="${OPTARG}" ;; D) override_install_dir="${OPTARG}" ;; + c) read -r -a override_cert_list <<< "${OPTARG}" ;; w) override_work_dir="${OPTARG}" ;; m) read -r -a override_buildmodes <<< "${OPTARG}" ;; o) override_out_dir="${OPTARG}" ;;