Add the ability to generate rootfs signatures using openssl CMS module if `-c
` is given.
(gitlab ci) Added a CA structure to the codesigning certificates. This to test the functionality of optional CA being in the signing message. (mkarchiso) Removed the ``sign_netboot_artifacts`` variable and instead we'll now rely on ``if [[ -v cert_list ]]; then``. Added ``ARCHISO_TLS_FD`` and ``ARCHISO_TLSCA_FD`` environment variables to override the certificates used. This is so that third party CA's can be used during building in a meaningful way without distrupting the CA trust that is shipped by default. _cms_sign_artifact() was added which signs the rootfs using OpenSSL CMS. The files will be saved as "${artifact}.cms.sig". That would be for instance "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs.cms.sig".
This commit is contained in:
parent
5f135b4342
commit
326cfed7cc
@ -30,6 +30,8 @@ gnupg_homedir=""
|
|||||||
codesigning_dir=""
|
codesigning_dir=""
|
||||||
codesigning_cert=""
|
codesigning_cert=""
|
||||||
codesigning_key=""
|
codesigning_key=""
|
||||||
|
ca_cert=""
|
||||||
|
ca_key=""
|
||||||
pgp_key_id=""
|
pgp_key_id=""
|
||||||
|
|
||||||
print_section_start() {
|
print_section_start() {
|
||||||
@ -204,43 +206,103 @@ EOF
|
|||||||
print_section_end "ephemeral_pgp_key"
|
print_section_end "ephemeral_pgp_key"
|
||||||
}
|
}
|
||||||
|
|
||||||
create_ephemeral_codesigning_key() {
|
create_ephemeral_codesigning_keys() {
|
||||||
# create ephemeral certificates used for codesigning
|
# create ephemeral certificates used for codesigning
|
||||||
print_section_start "ephemeral_codesigning_key" "Creating ephemeral codesigning key"
|
print_section_start "ephemeral_codesigning_key" "Creating ephemeral codesigning keys"
|
||||||
|
|
||||||
|
# The exact steps in creating a CA with Codesigning being signed was taken from
|
||||||
|
# https://jamielinux.com/docs/openssl-certificate-authority/introduction.html
|
||||||
|
# (slight modifications to the process to not disturb default values of /etc/ssl/openssl.cnf)
|
||||||
|
|
||||||
codesigning_dir="${tmpdir}/.codesigning/"
|
codesigning_dir="${tmpdir}/.codesigning/"
|
||||||
local codesigning_conf="${codesigning_dir}/openssl.cnf"
|
local ca_dir="${codesigning_dir}/ca/"
|
||||||
|
|
||||||
|
local ca_conf="${ca_dir}/certificate_authority.cnf"
|
||||||
|
local ca_subj="/C=DE/ST=Berlin/L=Berlin/O=Arch Linux/OU=Release Engineering/CN=archlinux.org"
|
||||||
|
ca_cert="${ca_dir}/cacert.pem"
|
||||||
|
ca_key="${ca_dir}/private/cakey.pem"
|
||||||
|
|
||||||
|
local codesigning_conf="${codesigning_dir}/code_signing.cnf"
|
||||||
local codesigning_subj="/C=DE/ST=Berlin/L=Berlin/O=Arch Linux/OU=Release Engineering/CN=archlinux.org"
|
local codesigning_subj="/C=DE/ST=Berlin/L=Berlin/O=Arch Linux/OU=Release Engineering/CN=archlinux.org"
|
||||||
codesigning_cert="${codesigning_dir}/codesign.crt"
|
codesigning_cert="${codesigning_dir}/codesign.crt"
|
||||||
codesigning_key="${codesigning_dir}/codesign.key"
|
codesigning_key="${codesigning_dir}/codesign.key"
|
||||||
|
|
||||||
|
mkdir -p "${ca_dir}/"{private,newcerts,crl}
|
||||||
mkdir -p "${codesigning_dir}"
|
mkdir -p "${codesigning_dir}"
|
||||||
cp -- /etc/ssl/openssl.cnf "${codesigning_conf}"
|
cp -- /etc/ssl/openssl.cnf "${codesigning_conf}"
|
||||||
printf "\n[codesigning]\nkeyUsage=digitalSignature\nextendedKeyUsage=codeSigning\n" >> "${codesigning_conf}"
|
cp -- /etc/ssl/openssl.cnf "${ca_conf}"
|
||||||
|
touch "${ca_dir}/index.txt"
|
||||||
|
echo "1000" > "${ca_dir}/serial"
|
||||||
|
|
||||||
|
# Prepare the ca configuration for the change in directory
|
||||||
|
sed -i "s#/etc/ssl#${ca_dir}#g" "${ca_conf}"
|
||||||
|
|
||||||
|
# Create the Certificate Authority
|
||||||
|
openssl req \
|
||||||
|
-newkey rsa:4096 \
|
||||||
|
-sha256 \
|
||||||
|
-nodes \
|
||||||
|
-x509 \
|
||||||
|
-new \
|
||||||
|
-sha256 \
|
||||||
|
-keyout "${ca_key}" \
|
||||||
|
-config "${ca_conf}" \
|
||||||
|
-subj "${ca_subj}" \
|
||||||
|
-out "${ca_cert}"
|
||||||
|
|
||||||
|
cat << EOF >> "${ca_conf}"
|
||||||
|
|
||||||
|
[ v3_intermediate_ca ]
|
||||||
|
# Extensions for a typical intermediate CA ('man x509v3_config').
|
||||||
|
subjectKeyIdentifier = hash
|
||||||
|
authorityKeyIdentifier = keyid:always,issuer
|
||||||
|
basicConstraints = critical, CA:true, pathlen:0
|
||||||
|
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat << EOF >> "${codesigning_conf}"
|
||||||
|
|
||||||
|
[codesigning]
|
||||||
|
keyUsage=digitalSignature
|
||||||
|
extendedKeyUsage=codeSigning, clientAuth, emailProtection
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
openssl req \
|
openssl req \
|
||||||
-newkey rsa:4096 \
|
-newkey rsa:4096 \
|
||||||
-keyout "${codesigning_key}" \
|
-keyout "${codesigning_key}" \
|
||||||
-nodes \
|
-nodes \
|
||||||
-sha256 \
|
-sha256 \
|
||||||
-x509 \
|
-out "${codesigning_cert}.csr" \
|
||||||
-days 365 \
|
|
||||||
-out "${codesigning_cert}" \
|
|
||||||
-config "${codesigning_conf}" \
|
-config "${codesigning_conf}" \
|
||||||
-subj "${codesigning_subj}" \
|
-subj "${codesigning_subj}" \
|
||||||
-extensions codesigning
|
-extensions codesigning
|
||||||
|
|
||||||
|
# Sign the code signing certificate with the CA
|
||||||
|
openssl ca \
|
||||||
|
-batch \
|
||||||
|
-config "${ca_conf}" \
|
||||||
|
-extensions v3_intermediate_ca \
|
||||||
|
-days 3650 \
|
||||||
|
-notext \
|
||||||
|
-md sha256 \
|
||||||
|
-in "${codesigning_cert}.csr" \
|
||||||
|
-out "${codesigning_cert}"
|
||||||
|
|
||||||
print_section_end "ephemeral_codesigning_key"
|
print_section_end "ephemeral_codesigning_key"
|
||||||
}
|
}
|
||||||
|
|
||||||
run_mkarchiso() {
|
run_mkarchiso() {
|
||||||
# run mkarchiso
|
# run mkarchiso
|
||||||
create_ephemeral_pgp_key
|
create_ephemeral_pgp_key
|
||||||
create_ephemeral_codesigning_key
|
create_ephemeral_codesigning_keys
|
||||||
|
|
||||||
print_section_start "mkarchiso" "Running mkarchiso"
|
print_section_start "mkarchiso" "Running mkarchiso"
|
||||||
mkdir -p "${output}/" "${tmpdir}/"
|
mkdir -p "${output}/" "${tmpdir}/"
|
||||||
GNUPGHOME="${gnupg_homedir}" ./archiso/mkarchiso \
|
GNUPGHOME="${gnupg_homedir}" ./archiso/mkarchiso \
|
||||||
-D "${install_dir}" \
|
-D "${install_dir}" \
|
||||||
-c "${codesigning_cert} ${codesigning_key}" \
|
-c "${codesigning_cert} ${codesigning_key} ${ca_cert}" \
|
||||||
-g "${pgp_key_id}" \
|
-g "${pgp_key_id}" \
|
||||||
-G "${pgp_sender}" \
|
-G "${pgp_sender}" \
|
||||||
-o "${output}/" \
|
-o "${output}/" \
|
||||||
|
@ -36,3 +36,4 @@ Archiso Authors
|
|||||||
* Øyvind Heggstad <heggstad@gmail.com>
|
* Øyvind Heggstad <heggstad@gmail.com>
|
||||||
* plain linen <bcdedit@hotmail.com>
|
* plain linen <bcdedit@hotmail.com>
|
||||||
* Pellegrino Prevete <pellegrinoprevete@gmail.com>
|
* Pellegrino Prevete <pellegrinoprevete@gmail.com>
|
||||||
|
* Anton Hvornum <anton@hvornum.se>
|
||||||
|
@ -8,6 +8,8 @@ Changelog
|
|||||||
Added
|
Added
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
- The ability to generate rootfs signatures using openssl CMS module if ``-c`` is given.
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -43,7 +43,6 @@ bootmodes=()
|
|||||||
airootfs_image_type=""
|
airootfs_image_type=""
|
||||||
airootfs_image_tool_options=()
|
airootfs_image_tool_options=()
|
||||||
cert_list=()
|
cert_list=()
|
||||||
sign_netboot_artifacts=""
|
|
||||||
declare -A file_permissions=()
|
declare -A file_permissions=()
|
||||||
efibootimg=""
|
efibootimg=""
|
||||||
efiboot_files=()
|
efiboot_files=()
|
||||||
@ -94,10 +93,11 @@ usage: ${app_name} [options] <profile_dir>
|
|||||||
Default: '${iso_label}'
|
Default: '${iso_label}'
|
||||||
-P <publisher> Set the ISO publisher
|
-P <publisher> Set the ISO publisher
|
||||||
Default: '${iso_publisher}'
|
Default: '${iso_publisher}'
|
||||||
-c [cert ..] Provide certificates for codesigning of netboot artifacts
|
-c [cert ..] Provide certificates for codesigning of netboot artifacts as
|
||||||
|
well as the rootfs artifact.
|
||||||
Multiple files are provided as quoted, space delimited list.
|
Multiple files are provided as quoted, space delimited list.
|
||||||
The first file is considered as the signing certificate,
|
The first file is considered as the signing certificate,
|
||||||
the second as the key.
|
the second as the key and the third as the optional certificate authority.
|
||||||
-g <gpg_key> Set the PGP key ID to be used for signing the rootfs image.
|
-g <gpg_key> Set the PGP key ID to be used for signing the rootfs image.
|
||||||
Passed to gpg as the value for --default-key
|
Passed to gpg as the value for --default-key
|
||||||
-G <mbox> Set the PGP signer (must include an email address)
|
-G <mbox> Set the PGP signer (must include an email address)
|
||||||
@ -250,14 +250,11 @@ _mkchecksum() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# GPG sign the root file system image.
|
# GPG sign the root file system image.
|
||||||
_mksignature() {
|
_mk_pgp_signature() {
|
||||||
local airootfs_image_filename gpg_options=()
|
local gpg_options=()
|
||||||
_msg_info "Signing rootfs image..."
|
local airootfs_image_filename="${1}"
|
||||||
if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then
|
_msg_info "Signing rootfs image using GPG..."
|
||||||
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs"
|
|
||||||
elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then
|
|
||||||
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs"
|
|
||||||
fi
|
|
||||||
rm -f -- "${airootfs_image_filename}.sig"
|
rm -f -- "${airootfs_image_filename}.sig"
|
||||||
# Add gpg sender option if the value is provided
|
# Add gpg sender option if the value is provided
|
||||||
[[ -z "${gpg_sender}" ]] || gpg_options+=('--sender' "${gpg_sender}")
|
[[ -z "${gpg_sender}" ]] || gpg_options+=('--sender' "${gpg_sender}")
|
||||||
@ -342,6 +339,15 @@ _make_packages() {
|
|||||||
exec {ARCHISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
|
exec {ARCHISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
|
||||||
export ARCHISO_GNUPG_FD
|
export ARCHISO_GNUPG_FD
|
||||||
fi
|
fi
|
||||||
|
if [[ -v cert_list[0] ]]; then
|
||||||
|
exec {ARCHISO_TLS_FD}<>"${cert_list[0]}"
|
||||||
|
export ARCHISO_TLS_FD
|
||||||
|
fi
|
||||||
|
if [[ -v cert_list[2] ]]; then
|
||||||
|
exec {ARCHISO_TLSCA_FD}<>"${cert_list[2]}"
|
||||||
|
export ARCHISO_TLSCA_FD
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Unset TMPDIR to work around https://bugs.archlinux.org/task/70580
|
# Unset TMPDIR to work around https://bugs.archlinux.org/task/70580
|
||||||
if [[ "${quiet}" = "y" ]]; then
|
if [[ "${quiet}" = "y" ]]; then
|
||||||
@ -350,6 +356,14 @@ _make_packages() {
|
|||||||
env -u TMPDIR pacstrap -C "${work_dir}/${buildmode}.pacman.conf" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}"
|
env -u TMPDIR pacstrap -C "${work_dir}/${buildmode}.pacman.conf" -c -G -M -- "${pacstrap_dir}" "${buildmode_pkg_list[@]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -v cert_list[0] ]]; then
|
||||||
|
exec {ARCHISO_TLS_FD}<&-
|
||||||
|
unset ARCHISO_TLS_FD
|
||||||
|
fi
|
||||||
|
if [[ -v cert_list[2] ]]; then
|
||||||
|
exec {ARCHISO_TLSCA_FD}<&-
|
||||||
|
unset ARCHISO_TLSCA_FD
|
||||||
|
fi
|
||||||
if [[ -n "${gpg_key}" ]]; then
|
if [[ -n "${gpg_key}" ]]; then
|
||||||
exec {ARCHISO_GNUPG_FD}<&-
|
exec {ARCHISO_GNUPG_FD}<&-
|
||||||
unset ARCHISO_GNUPG_FD
|
unset ARCHISO_GNUPG_FD
|
||||||
@ -998,8 +1012,18 @@ _validate_requirements_bootmode_uefi-x64.grub.eltorito() {
|
|||||||
_prepare_airootfs_image() {
|
_prepare_airootfs_image() {
|
||||||
_run_once "_mkairootfs_${airootfs_image_type}"
|
_run_once "_mkairootfs_${airootfs_image_type}"
|
||||||
_mkchecksum
|
_mkchecksum
|
||||||
|
|
||||||
|
if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then
|
||||||
|
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs"
|
||||||
|
elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then
|
||||||
|
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -n "${gpg_key}" ]]; then
|
if [[ -n "${gpg_key}" ]]; then
|
||||||
_mksignature
|
_mk_pgp_signature "${airootfs_image_filename}"
|
||||||
|
fi
|
||||||
|
if [[ -v cert_list ]]; then
|
||||||
|
_cms_sign_artifact "${airootfs_image_filename}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,6 +1036,32 @@ _export_netboot_artifacts() {
|
|||||||
du -hs -- "${out_dir}/${install_dir}"
|
du -hs -- "${out_dir}/${install_dir}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cms_sign_artifact() {
|
||||||
|
local artifact="${1}"
|
||||||
|
local openssl_flags=(
|
||||||
|
"-sign"
|
||||||
|
"-binary"
|
||||||
|
"-nocerts"
|
||||||
|
"-noattr"
|
||||||
|
"-outform" "DER" "-out" "${artifact}.cms.sig"
|
||||||
|
"-in" "${artifact}"
|
||||||
|
"-signer" "${cert_list[0]}"
|
||||||
|
"-inkey" "${cert_list[1]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if (( ${#cert_list[@]} > 2 )); then
|
||||||
|
openssl_flags+=("-certfile" "${cert_list[2]}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
_msg_info "Signing ${artifact} image using openssl cms..."
|
||||||
|
|
||||||
|
rm -f -- "${artifact}.cms.sig"
|
||||||
|
|
||||||
|
openssl cms "${openssl_flags[@]}"
|
||||||
|
|
||||||
|
_msg_info "Done!"
|
||||||
|
}
|
||||||
|
|
||||||
# sign build artifacts for netboot
|
# sign build artifacts for netboot
|
||||||
_sign_netboot_artifacts() {
|
_sign_netboot_artifacts() {
|
||||||
local _file _dir
|
local _file _dir
|
||||||
@ -1115,6 +1165,26 @@ _validate_common_requirements_buildmode_iso_netboot() {
|
|||||||
_msg_error "Packages file '${packages}' does not exist." 0
|
_msg_error "Packages file '${packages}' does not exist." 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ -v cert_list ]]; then
|
||||||
|
# Check if the certificate files exist
|
||||||
|
for _cert in "${cert_list[@]}"; do
|
||||||
|
if [[ ! -e "${_cert}" ]]; then
|
||||||
|
(( validation_error=validation_error+1 ))
|
||||||
|
_msg_error "File '${_cert}' does not exist." 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Check if there are at least three certificate files to sign netboot and rootfs.
|
||||||
|
if (( ${#cert_list[@]} < 2 )); then
|
||||||
|
(( validation_error=validation_error+1 ))
|
||||||
|
_msg_error "Two certificates are required for codesigning netboot artifacts, but '${cert_list[*]}' is provided." 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if the specified airootfs_image_type is supported
|
# Check if the specified airootfs_image_type is supported
|
||||||
if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then
|
if typeset -f "_mkairootfs_${airootfs_image_type}" &> /dev/null; then
|
||||||
if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then
|
if typeset -f "_validate_requirements_airootfs_image_type_${airootfs_image_type}" &> /dev/null; then
|
||||||
@ -1156,31 +1226,8 @@ _validate_requirements_buildmode_iso() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_validate_requirements_buildmode_netboot() {
|
_validate_requirements_buildmode_netboot() {
|
||||||
local _override_cert_list=()
|
|
||||||
|
|
||||||
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
|
|
||||||
_validate_common_requirements_buildmode_iso_netboot
|
_validate_common_requirements_buildmode_iso_netboot
|
||||||
_validate_common_requirements_buildmode_all
|
_validate_common_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
|
# SYSLINUX El Torito
|
||||||
@ -1541,10 +1588,7 @@ _set_overrides() {
|
|||||||
fi
|
fi
|
||||||
[[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key"
|
[[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key"
|
||||||
[[ ! -v override_gpg_sender ]] || gpg_sender="$override_gpg_sender"
|
[[ ! -v override_gpg_sender ]] || gpg_sender="$override_gpg_sender"
|
||||||
if [[ -v override_cert_list ]]; then
|
[[ ! -v override_cert_list ]] || mapfile -t cert_list < <(realpath -- "${override_cert_list[@]}")
|
||||||
sign_netboot_artifacts="y"
|
|
||||||
fi
|
|
||||||
[[ ! -v override_cert_list ]] || cert_list+=("${override_cert_list[@]}")
|
|
||||||
if [[ -v override_quiet ]]; then
|
if [[ -v override_quiet ]]; then
|
||||||
quiet="$override_quiet"
|
quiet="$override_quiet"
|
||||||
elif [[ -z "$quiet" ]]; then
|
elif [[ -z "$quiet" ]]; then
|
||||||
@ -1675,8 +1719,16 @@ _build_buildmode_netboot() {
|
|||||||
local run_once_mode="${buildmode}"
|
local run_once_mode="${buildmode}"
|
||||||
|
|
||||||
_build_iso_base
|
_build_iso_base
|
||||||
|
|
||||||
|
if [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.sfs" ]]; then
|
||||||
|
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.sfs"
|
||||||
|
elif [[ -e "${isofs_dir}/${install_dir}/${arch}/airootfs.erofs" ]]; then
|
||||||
|
airootfs_image_filename="${isofs_dir}/${install_dir}/${arch}/airootfs.erofs"
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ -v cert_list ]]; then
|
if [[ -v cert_list ]]; then
|
||||||
_run_once _sign_netboot_artifacts
|
_run_once _sign_netboot_artifacts
|
||||||
|
_cms_sign_artifact "${airootfs_image_filename}"
|
||||||
fi
|
fi
|
||||||
_run_once _export_netboot_artifacts
|
_run_once _export_netboot_artifacts
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user