Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Hvornum <anton@hvornum.se>2022-05-18 16:42:28 +0200
committerDavid Runge <dvzrv@archlinux.org>2022-09-25 19:54:41 +0200
commit326cfed7cc59af487cdae1d1a75e1e3a8f84cb67 (patch)
tree4e717352509de8de4e30b64581dddc83fc715114
parent5f135b4342eb8306bce917bc9f3095fc38985c4c (diff)
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".
-rwxr-xr-x.gitlab/ci/build_archiso.sh80
-rw-r--r--AUTHORS.rst1
-rw-r--r--CHANGELOG.rst2
-rwxr-xr-xarchiso/mkarchiso130
4 files changed, 165 insertions, 48 deletions
diff --git a/.gitlab/ci/build_archiso.sh b/.gitlab/ci/build_archiso.sh
index 5250b51..104792a 100755
--- a/.gitlab/ci/build_archiso.sh
+++ b/.gitlab/ci/build_archiso.sh
@@ -30,6 +30,8 @@ gnupg_homedir=""
codesigning_dir=""
codesigning_cert=""
codesigning_key=""
+ca_cert=""
+ca_key=""
pgp_key_id=""
print_section_start() {
@@ -204,43 +206,103 @@ EOF
print_section_end "ephemeral_pgp_key"
}
-create_ephemeral_codesigning_key() {
+create_ephemeral_codesigning_keys() {
# 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/"
- 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"
codesigning_cert="${codesigning_dir}/codesign.crt"
codesigning_key="${codesigning_dir}/codesign.key"
+
+ mkdir -p "${ca_dir}/"{private,newcerts,crl}
mkdir -p "${codesigning_dir}"
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 \
-newkey rsa:4096 \
-keyout "${codesigning_key}" \
-nodes \
-sha256 \
- -x509 \
- -days 365 \
- -out "${codesigning_cert}" \
+ -out "${codesigning_cert}.csr" \
-config "${codesigning_conf}" \
-subj "${codesigning_subj}" \
-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"
}
run_mkarchiso() {
# run mkarchiso
create_ephemeral_pgp_key
- create_ephemeral_codesigning_key
+ create_ephemeral_codesigning_keys
print_section_start "mkarchiso" "Running mkarchiso"
mkdir -p "${output}/" "${tmpdir}/"
GNUPGHOME="${gnupg_homedir}" ./archiso/mkarchiso \
-D "${install_dir}" \
- -c "${codesigning_cert} ${codesigning_key}" \
+ -c "${codesigning_cert} ${codesigning_key} ${ca_cert}" \
-g "${pgp_key_id}" \
-G "${pgp_sender}" \
-o "${output}/" \
diff --git a/AUTHORS.rst b/AUTHORS.rst
index b03b91e..18207eb 100644
--- a/AUTHORS.rst
+++ b/AUTHORS.rst
@@ -36,3 +36,4 @@ Archiso Authors
* Øyvind Heggstad <heggstad@gmail.com>
* plain linen <bcdedit@hotmail.com>
* Pellegrino Prevete <pellegrinoprevete@gmail.com>
+* Anton Hvornum <anton@hvornum.se>
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 687b413..1d9eeea 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,8 @@ Changelog
Added
-----
+- The ability to generate rootfs signatures using openssl CMS module if ``-c`` is given.
+
Changed
-------
diff --git a/archiso/mkarchiso b/archiso/mkarchiso
index 5f0c79b..2fbbf66 100755
--- a/archiso/mkarchiso
+++ b/archiso/mkarchiso
@@ -43,7 +43,6 @@ bootmodes=()
airootfs_image_type=""
airootfs_image_tool_options=()
cert_list=()
-sign_netboot_artifacts=""
declare -A file_permissions=()
efibootimg=""
efiboot_files=()
@@ -94,10 +93,11 @@ usage: ${app_name} [options] <profile_dir>
Default: '${iso_label}'
-P <publisher> Set the 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.
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.
Passed to gpg as the value for --default-key
-G <mbox> Set the PGP signer (must include an email address)
@@ -250,14 +250,11 @@ _mkchecksum() {
}
# GPG sign the root file system image.
-_mksignature() {
- local airootfs_image_filename gpg_options=()
- _msg_info "Signing rootfs image..."
- 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
+_mk_pgp_signature() {
+ local gpg_options=()
+ local airootfs_image_filename="${1}"
+ _msg_info "Signing rootfs image using GPG..."
+
rm -f -- "${airootfs_image_filename}.sig"
# Add gpg sender option if the value is provided
[[ -z "${gpg_sender}" ]] || gpg_options+=('--sender' "${gpg_sender}")
@@ -342,6 +339,15 @@ _make_packages() {
exec {ARCHISO_GNUPG_FD}<>"${work_dir}/pubkey.gpg"
export ARCHISO_GNUPG_FD
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
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[@]}"
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
exec {ARCHISO_GNUPG_FD}<&-
unset ARCHISO_GNUPG_FD
@@ -998,8 +1012,18 @@ _validate_requirements_bootmode_uefi-x64.grub.eltorito() {
_prepare_airootfs_image() {
_run_once "_mkairootfs_${airootfs_image_type}"
_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
- _mksignature
+ _mk_pgp_signature "${airootfs_image_filename}"
+ fi
+ if [[ -v cert_list ]]; then
+ _cms_sign_artifact "${airootfs_image_filename}"
fi
}
@@ -1012,6 +1036,32 @@ _export_netboot_artifacts() {
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_netboot_artifacts() {
local _file _dir
@@ -1115,6 +1165,26 @@ _validate_common_requirements_buildmode_iso_netboot() {
_msg_error "Packages file '${packages}' does not exist." 0
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
if typeset -f "_mkairootfs_${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() {
- 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_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
@@ -1541,10 +1588,7 @@ _set_overrides() {
fi
[[ ! -v override_gpg_key ]] || gpg_key="$override_gpg_key"
[[ ! -v override_gpg_sender ]] || gpg_sender="$override_gpg_sender"
- if [[ -v override_cert_list ]]; then
- sign_netboot_artifacts="y"
- fi
- [[ ! -v override_cert_list ]] || cert_list+=("${override_cert_list[@]}")
+ [[ ! -v override_cert_list ]] || mapfile -t cert_list < <(realpath -- "${override_cert_list[@]}")
if [[ -v override_quiet ]]; then
quiet="$override_quiet"
elif [[ -z "$quiet" ]]; then
@@ -1675,8 +1719,16 @@ _build_buildmode_netboot() {
local run_once_mode="${buildmode}"
_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
_run_once _sign_netboot_artifacts
+ _cms_sign_artifact "${airootfs_image_filename}"
fi
_run_once _export_netboot_artifacts
}