From cd58ab18c150d78ba0c98d1fbf077c543b294997 Mon Sep 17 00:00:00 2001
From: Li Zhou
Date: Fri, 23 Dec 2022 16:46:41 +0800
Subject: [PATCH] debian: secure boot: add scripts for remote signing
Add scripts for supporting building image for board with
secure boot enabled.
The signing process in script sign-secure-boot_debian:
(1) The kernel/grub efi images are obtained from extracted kernel/grub
packages, and they are sent to signing server and signed there and
copied back. Then the kernel/grub packages are repacked with the
signed efi images.
(2) The file sign_rootfs-post-scripts is inserted to where the
hook script "rootfs-post-scripts" is defined in the lat config file
base-bullseye.yaml. This will sign kernel images and LockDown.efi
on signing sever in the lat build process.
(3) The file sign_initramfs-sign-script is inserted to where the hook
script "initramfs-sign-script" is defined in the lat config file
base-bullseye.yaml. This will sign initramfs and mini initrd in
the lat build process.
(4) EFI_SECURE_BOOT is changed from disabled to enabled.
Build process for signed image is as below:
(1) Prepare keys on dockers and signing server to setup access
to signing server without password:
Use "ssh-keygen -t rsa" to create a ssh key pair, e.g.
id_rsa.pub and id_rsa.
Prepare on lat docker:
mkdir ~/.ssh
copy id_rsa to ~/.ssh directory
Prepare on builder docker:
mkdir ~/.ssh
copy id_rsa to ~/.ssh directory
sudo mkdir /root/.ssh
sudo copy id_rsa to /root/.ssh directory
(https://review.opendev.org/c/starlingx/tools/+/872742
simplifies those steps on dockers as:
stx control keys-add --key-type=signing-server --key=[key file].)
Prepare on signing server:
append id_rsa.pub to the file on signing server:
/home/${signing_user}/.ssh/authorized_keys
(2) Run on builder docker:
export SIGNING_SERVER="signing_user@signing_server_ip"
/sign-secure-boot_debian
build-image
The is:
/localdisk/designer/${USER}/stx/cgcs-root/build-tools
Test plan:
The tests are done with all the changes which involve tools/integ/root
repo and lat-sdk.sh and signing tools on signing server.
- PASS: Follow below build process to build unsigned image
and build successfully:
build-pkgs
build-image
- PASS: Follow below build process to build signed image
and build successfully:
build-pkgs
export SIGNING_SERVER="user@signing_server_ip"
/sign-secure-boot_debian
build-image
- PASS: Do AIO-DX installation successfully for both
unsigned image on secure boot disabled lab and
signed image on secure boot enabled lab.
- PASS: Failure path tests include:
[NG]unsigned image on secure boot enabled
[NG]image signed with wrong key on secure boot enabled
[OK]signed image on secure boot disabled
NG: fail to boot; OK: succeed to boot.
Depends-On: https://review.opendev.org/c/starlingx/tools/+/868918
Story: 2009221
Task: 47097
Signed-off-by: Li Zhou
Change-Id: Iea0d03c39f6d3d1fa84577e870675ab103937fbd
---
build-tools/sign-secure-boot_debian | 192 +++++++++++++++++++++++++
build-tools/sign_initramfs-sign-script | 51 +++++++
build-tools/sign_rootfs-post-scripts | 59 ++++++++
3 files changed, 302 insertions(+)
create mode 100755 build-tools/sign-secure-boot_debian
create mode 100644 build-tools/sign_initramfs-sign-script
create mode 100644 build-tools/sign_rootfs-post-scripts
diff --git a/build-tools/sign-secure-boot_debian b/build-tools/sign-secure-boot_debian
new file mode 100755
index 00000000..c99cfe4f
--- /dev/null
+++ b/build-tools/sign-secure-boot_debian
@@ -0,0 +1,192 @@
+#!/bin/bash
+#
+# Copyright (c) 2023 Wind River Systems, Inc.
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. The ASF licenses this
+# file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Input for the script:
+# export SIGNING_SERVER="user name for the signing server"+@+"ip for the signing server"
+# Tools needed by the script: ssh/cut/scp/sed
+
+echo "sign-secure-boot_debian: start"
+
+# If no right input, exit.
+if [ -z "${SIGNING_SERVER}" ]; then
+ echo "ERROR: Please export SIGNING_SERVER first!"
+ exit 1
+fi
+
+# Get shim deb version number.
+SHIM_DEB=$(ls /localdisk/loadbuild/${USER}/stx/std/shim/shim-unsigned_*_amd64.deb)
+SHIM_DEB=${SHIM_DEB##*/}
+if [ -z "${SHIM_DEB}" ]; then
+ echo "No shim-unsigned deb!"
+ exit 1
+fi
+SHIM_VERSION=$(echo ${SHIM_DEB} | cut -d '_' -f 2)
+if [ -z "${SHIM_VERSION}" ]; then
+ echo "Wrong shim deb version!"
+ exit 1
+fi
+
+# Get grub-efi deb version number.
+GRUB_EFI_DEB=$(ls /localdisk/loadbuild/${USER}/stx/std/grub-efi/grub-efi-amd64_*_amd64.deb)
+GRUB_EFI_DEB=${GRUB_EFI_DEB##*/}
+if [ -z "${GRUB_EFI_DEB}" ]; then
+ echo "No grub-efi-amd64 deb!"
+ exit 1
+fi
+GRUB_EFI_VERSION=$(echo ${GRUB_EFI_DEB} | cut -d '_' -f 2)
+if [ -z "${GRUB_EFI_VERSION}" ]; then
+ echo "Wrong grub-efi-amd64 deb version!"
+ exit 1
+fi
+
+SSH_OPTION_NOCHECKING="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+
+# Request upload path from signing server.
+REQUEST=$(ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} sudo /opt/signing/sign-debian.sh -r)
+UPLOAD_PATH=${REQUEST#*Upload: }
+echo UPLOAD_PATH: ${UPLOAD_PATH}
+if [ -z "${UPLOAD_PATH}" ]; then
+ echo "Fail to request for upload path!"
+ exit 1
+fi
+
+echo "***(1) Start signing shim***"
+cd /localdisk/loadbuild/${USER}/stx/std/shim
+ls sign > /dev/null && echo "Removing old sign folder!" && sudo rm sign -rf
+mkdir sign
+cp shim-unsigned_${SHIM_VERSION}_amd64.deb ./sign \
+ || { echo "No right shim-unsigned deb!"; exit 1; }
+cd sign
+# Raw-extract shim deb
+sudo dpkg-deb -R shim-unsigned_${SHIM_VERSION}_amd64.deb ./shim-unsigned/ \
+ || { echo "Fail to extract shim-unsigned deb!"; exit 1; }
+cd shim-unsigned/usr/lib/shim
+
+# Copy shimx64.efi to signing server
+scp ${SSH_OPTION_NOCHECKING} shimx64.efi ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy shimx64.efi to signing server!"; exit 1; }
+# Sign shimx64.efi
+ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/shimx64.efi -t shim \
+ || { echo "Fail to sign shimx64.efi!"; exit 1; }
+# Copy back signed shimx64.efi which is renamed as bootx64.efi
+sudo scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/bootx64.efi ./ \
+ || { echo "Fail to copy back signed shim image!"; exit 1; }
+
+# Copy mmx64.efi to signing server
+scp ${SSH_OPTION_NOCHECKING} mmx64.efi ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy mmx64.efi to signing server!"; exit 1; }
+# Sign mmx64.efi
+ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/mmx64.efi -t shimtool \
+ || { echo "Fail to sign mmx64.efi!"; exit 1; }
+# Copy back signed mmx64.efi (renamed to grubx64.efi by server and need rename it back)
+sudo scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/mmx64.efi.signed ./mmx64.efi \
+ || { echo "Fail to copy back signed shim tool image!"; exit 1; }
+
+cd -
+# Repack the shim package and replace the one in the repo
+dpkg-deb -b ./shim-unsigned shim-unsigned_${SHIM_VERSION}_amd64.deb \
+ || { echo "Fail to repack the shim package with signed images!"; exit 1; }
+repo_manage.py delete_pkg -r deb-local-build -p shim-unsigned -t binary -v ${SHIM_VERSION} \
+ || { echo "Fail to delete the old shim-unsigned package from repo!"; exit 1; }
+repo_manage.py upload_pkg -r deb-local-build -p ./shim-unsigned_${SHIM_VERSION}_amd64.deb \
+ || { echo "Fail to upload the new shim package to repo!"; exit 1; }
+echo "***Finish signing shim***"
+
+echo "***(2) Start signing grub***"
+cd /localdisk/loadbuild/${USER}/stx/std/grub-efi
+ls sign > /dev/null && echo "Removing old sign folder!" && sudo rm sign -rf
+mkdir sign
+cp grub-efi-amd64_${GRUB_EFI_VERSION}_amd64.deb ./sign \
+ || { echo "No right grub-efi-amd64 deb!"; exit 1; }
+cd sign
+# Raw-extract grub-efi-amd64 deb
+sudo dpkg-deb -R grub-efi-amd64_${GRUB_EFI_VERSION}_amd64.deb ./grub-efi-amd64 \
+ || { echo "Fail to extract grub-efi-amd64 deb"; exit 1; }
+cd ./grub-efi-amd64/boot/efi/EFI/BOOT
+
+# Copy grubx64.efi to signing server
+scp ${SSH_OPTION_NOCHECKING} grubx64.efi ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy grubx64.efi to signing server!"; exit 1; }
+# Sign grubx64.efi
+ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/grubx64.efi -t grub \
+ || { echo "Fail to sign grubx64.efi!"; exit 1; }
+# Copy back signed grubx64.efi
+sudo scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/grubx64.efi . \
+ || { echo "Fail to copy back signed grub image!"; exit 1; }
+
+cd -
+# Repack the grub-efi-amd64 package and replace the one in the repo
+dpkg-deb -b ./grub-efi-amd64 grub-efi-amd64_${GRUB_EFI_VERSION}_amd64.deb \
+ || { echo "Fail to repack the grub-efi-amd64 package!"; exit 1; }
+repo_manage.py delete_pkg -r deb-local-build -p grub-efi-amd64 -t binary -v ${GRUB_EFI_VERSION} \
+ || { echo "Fail to delete the old grub-efi-amd64 package from repo!"; exit 1; }
+repo_manage.py upload_pkg -r deb-local-build -p ./grub-efi-amd64_${GRUB_EFI_VERSION}_amd64.deb \
+ || { echo "Fail to upload the new grub-efi-amd64 package to repo!"; exit 1; }
+echo "***Finish signing grub***"
+
+echo "***(3) Prepare gpg signing for lat genimage***"
+# The gpg signings are done when build-image. Here prepare the setting file for lat.
+YAML_FILE=/localdisk/designer/${USER}/stx/stx-tools/debian-mirror-tools/config/debian/common/base-bullseye.yaml
+# Definition for signing part of rootfs-post-scripts, which is used to sign kernel std/rt images and LockDown.efi.
+ROOTFS_SIGNING_FILE=/localdisk/designer/${USER}/stx/cgcs-root/build-tools/sign_rootfs-post-scripts
+# Definition for initramfs-sign-script, which is used to sign initramfs and mini initrd.
+INITRAMFS_SIGNING_FILE=/localdisk/designer/${USER}/stx/cgcs-root/build-tools/sign_initramfs-sign-script
+
+# Enable secure boot when building for secure boot.
+sed -i "s/EFI_SECURE_BOOT: disable/EFI_SECURE_BOOT: enable/g" ${YAML_FILE}
+
+# Find the line in base-bullseye.yaml: rootfs-post-scripts
+n=$(sed -n -e '/rootfs-post-scripts:/=' ${YAML_FILE})
+
+# Insert sign_rootfs-post-scripts into base-bullseye.yaml in the place of "rootfs-post-scripts:"
+# with a format needed.
+cat ${ROOTFS_SIGNING_FILE} | while read line
+do
+ n=$(expr ${n} + 1)
+ line="2SPACE"${line}
+ sed -i "${n} i ${line}" ${YAML_FILE}
+done
+# No space is needed before "- |-"
+sed -i "s/2SPACE- |-/- |-/g" ${YAML_FILE}
+
+# Find the line in base-bullseye.yaml: initramfs-sign-script
+n=$(sed -n -e '/initramfs-sign-script:/=' ${YAML_FILE})
+
+# Insert sign_initramfs-sign-script into base-bullseye.yaml in the place of "initramfs-sign-script:"
+# with a format needed.
+cat ${INITRAMFS_SIGNING_FILE} | while read line
+do
+ n=$(expr ${n} + 1)
+ line="2SPACE"${line}
+ sed -i "${n} i ${line}" ${YAML_FILE}
+done
+
+# Deal with space format needed by lat.
+sed -i "s/2SPACE/ /g" ${YAML_FILE}
+
+# Replace the signing server in the base-bullseye.yaml with the input of this script.
+sed -i "s/INPUT_SIGNING_SERVER/${SIGNING_SERVER}/g" ${YAML_FILE}
+
+echo "***Finish preparing gpg signing***"
+
+echo "sign-secure-boot_debian: done"
diff --git a/build-tools/sign_initramfs-sign-script b/build-tools/sign_initramfs-sign-script
new file mode 100644
index 00000000..66254588
--- /dev/null
+++ b/build-tools/sign_initramfs-sign-script
@@ -0,0 +1,51 @@
+ #
+ # Copyright (c) 2023 Wind River Systems, Inc.
+ #
+ # Licensed to the Apache Software Foundation (ASF) under one
+ # or more contributor license agreements. The ASF licenses this
+ # file to you under the Apache License, Version 2.0 (the
+ # "License"); you may not use this file except in compliance
+ # with the License. You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing,
+ # software distributed under the License is distributed on an
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ # KIND, either express or implied. See the License for the
+ # specific language governing permissions and limitations
+ # under the License.
+ #
+ # Fragment of base-bullseye.yaml for initramfs-sign-script definition
+ echo "***Start initramfs-sign-script***"
+ SIGNING_SERVER=INPUT_SIGNING_SERVER
+ INITRAMFS_PATH=/localdisk/deploy/
+ INITRAMFS_INIT=$(ls ${INITRAMFS_PATH}/starlingx-initramfs-ostree-image-intel-x86-64-*.rootfs.cpio.gz)
+ [ -z ${INITRAMFS_INIT} ] && { echo "No initramfs file!"; exit 1; }
+ INITRAMFS_FILE=$(basename ${INITRAMFS_INIT})
+ INITRD_MINI_FILE=initrd-mini
+ INITRD_MINI_PATH=/localdisk/workdir/starlingx/rootfs/var/miniboot/
+ SSH_OPTION_NOCHECKING="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+ REQUEST=$(ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} sudo /opt/signing/sign-debian.sh -r)
+ UPLOAD_PATH=${REQUEST#*Upload: }
+ echo UPLOAD_PATH: ${UPLOAD_PATH}
+ [ -z ${UPLOAD_PATH}] && { echo "Fail to request for upload path!"; exit 1; }
+ echo "(4) Sign initramfs"
+ scp ${SSH_OPTION_NOCHECKING} ${INITRAMFS_PATH}/${INITRAMFS_FILE} ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy initramfs file to signing server!"; exit 1; }
+ ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/${INITRAMFS_FILE} -t grub-gpg \
+ || { echo "Fail to sign initramfs file!"; exit 1; }
+ scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/${INITRAMFS_FILE}.sig ${INITRAMFS_PATH} \
+ || { echo "Fail to copy back initramfs sig file!"; exit 1; }
+ ln -snf -r ${INITRAMFS_PATH}/${INITRAMFS_FILE}.sig ${INITRAMFS_PATH}/starlingx-initramfs-ostree-image-intel-x86-64.cpio.gz.sig \
+ || { echo "Fail to create the initramfs sig file's link!"; exit 1; }
+ echo "(5) Sign mini initramfs"
+ scp ${SSH_OPTION_NOCHECKING} ${INITRD_MINI_PATH}/${INITRD_MINI_FILE} ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy mini initrd file to signing server!"; exit 1; }
+ ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/${INITRD_MINI_FILE} -t grub-gpg \
+ || { echo "Fail to sign mini initrd file!"; exit 1; }
+ scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/${INITRD_MINI_FILE}.sig ${INITRD_MINI_PATH} \
+ || { echo "Fail to copy back mini initrd sig file!"; exit 1; }
+ echo "***Finish initramfs-sign-script***"
diff --git a/build-tools/sign_rootfs-post-scripts b/build-tools/sign_rootfs-post-scripts
new file mode 100644
index 00000000..d54ec4d1
--- /dev/null
+++ b/build-tools/sign_rootfs-post-scripts
@@ -0,0 +1,59 @@
+- |-
+ #
+ # Copyright (c) 2023 Wind River Systems, Inc.
+ #
+ # Licensed to the Apache Software Foundation (ASF) under one
+ # or more contributor license agreements. The ASF licenses this
+ # file to you under the Apache License, Version 2.0 (the
+ # "License"); you may not use this file except in compliance
+ # with the License. You may obtain a copy of the License at
+ #
+ # http://www.apache.org/licenses/LICENSE-2.0
+ #
+ # Unless required by applicable law or agreed to in writing,
+ # software distributed under the License is distributed on an
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ # KIND, either express or implied. See the License for the
+ # specific language governing permissions and limitations
+ # under the License.
+ #
+ # Fragment of base-bullseye.yaml for signing part of rootfs-post-scripts definition
+ echo "***Start signing part of rootfs-post-scripts***"
+ SIGNING_SERVER=INPUT_SIGNING_SERVER
+ LOCKD_FILE=LockDown.efi
+ LOCKD_PATH=${IMAGE_ROOTFS}/boot/efi/EFI/BOOT/
+ LOCKD_INIT=${IMAGE_ROOTFS}/usr/lib/efitools/x86_64-linux-gnu/LockDown.efi
+ KERNEL_RT_FILE=vmlinuz-5.10.0-6-rt-amd64
+ KERNEL_RT_PATH=${IMAGE_ROOTFS}/boot/
+ KERNEL_FILE=vmlinuz-5.10.0-6-amd64
+ KERNEL_PATH=${IMAGE_ROOTFS}/boot/
+ SSH_OPTION_NOCHECKING="-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"
+ REQUEST=$(ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} sudo /opt/signing/sign-debian.sh -r)
+ UPLOAD_PATH=${REQUEST#*Upload: }
+ echo "UPLOAD_PATH: ${UPLOAD_PATH}"
+ [ -z ${UPLOAD_PATH}] && { echo "Fail to request for upload path!"; exit 1; }
+ echo "(1) Sign LockDown.efi"
+ scp ${SSH_OPTION_NOCHECKING} ${LOCKD_INIT} ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy LockDown.efi to signing server!"; exit 1; }
+ ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/${LOCKD_FILE} -t grub-gpg \
+ || { echo "Fail to sign LockDown.efi!"; exit 1; }
+ scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/${LOCKD_FILE}.sig ${LOCKD_PATH} \
+ || { echo "Fail to copy back LockDown.efi sig file!"; exit 1; }
+ echo "(2) Sign kernel-rt"
+ scp ${SSH_OPTION_NOCHECKING} ${KERNEL_RT_PATH}/${KERNEL_RT_FILE} ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy kernel-rt image to signing server!"; exit 1; }
+ ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/${KERNEL_RT_FILE} -t grub-gpg \
+ || { echo "Fail to sign kernel-rt image!"; exit 1; }
+ scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/${KERNEL_RT_FILE}.sig ${KERNEL_RT_PATH} \
+ || { echo "Fail to copy back kernel-rt image sig file!"; exit 1; }
+ echo "(3) Sign kernel-std"
+ scp ${SSH_OPTION_NOCHECKING} ${KERNEL_PATH}/${KERNEL_FILE} ${SIGNING_SERVER}:${UPLOAD_PATH} \
+ || { echo "Fail to copy kernel-std image to signing server!"; exit 1; }
+ ssh ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER} \
+ sudo /opt/signing/sign-debian.sh -i ${UPLOAD_PATH}/${KERNEL_FILE} -t grub-gpg \
+ || { echo "Fail to sign kernel-std image!"; exit 1; }
+ scp ${SSH_OPTION_NOCHECKING} ${SIGNING_SERVER}:${UPLOAD_PATH}/${KERNEL_FILE}.sig ${KERNEL_PATH} \
+ || { echo "Fail to copy back kernel-std image sig file"; exit 1; }
+ echo "***Finish signing part of rootfs-post-scripts***"