diff --git a/Makefile.work b/Makefile.work index d51d5f5bfb73..3f70a1419173 100644 --- a/Makefile.work +++ b/Makefile.work @@ -288,6 +288,21 @@ ifdef SONIC_BUILD_QUIETER DOCKER_RUN += -e "SONIC_BUILD_QUIETER=$(SONIC_BUILD_QUIETER)" endif +# Mount the Signing key and Certificate in the slave container +ifneq ($(SECURE_UPGRADE_DEV_SIGNING_KEY),) + DOCKER_RUN += -v $(SECURE_UPGRADE_DEV_SIGNING_KEY):$(SECURE_UPGRADE_DEV_SIGNING_KEY):ro +endif +ifneq ($(SECURE_UPGRADE_DEV_SIGNING_CERT),) + DOCKER_RUN += -v $(SECURE_UPGRADE_DEV_SIGNING_CERT):$(SECURE_UPGRADE_DEV_SIGNING_CERT):ro +endif +# Mount the Signing prod tool in the slave container +$(info "SECURE_UPGRADE_PROD_SIGNING_TOOL": "$(SECURE_UPGRADE_PROD_SIGNING_TOOL)") +ifneq ($(SECURE_UPGRADE_PROD_SIGNING_TOOL),) + SECURE_UPGRADE_PROD_SIGNING_TOOL_DST = /sonic/scripts/$(shell basename -- $(SECURE_UPGRADE_PROD_SIGNING_TOOL)) + DOCKER_RUN += -v $(SECURE_UPGRADE_PROD_SIGNING_TOOL):$(SECURE_UPGRADE_PROD_SIGNING_TOOL_DST):ro + SECURE_UPGRADE_PROD_SIGNING_TOOL := $(SECURE_UPGRADE_PROD_SIGNING_TOOL_DST) +endif + ifneq ($(SONIC_DPKG_CACHE_SOURCE),) DOCKER_RUN += -v "$(SONIC_DPKG_CACHE_SOURCE):/dpkg_cache:rw" endif @@ -498,6 +513,10 @@ SONIC_BUILD_INSTRUCTION := $(MAKE) \ BUILD_LOG_TIMESTAMP=$(BUILD_LOG_TIMESTAMP) \ SONIC_ENABLE_IMAGE_SIGNATURE=$(ENABLE_IMAGE_SIGNATURE) \ SONIC_ENABLE_SECUREBOOT_SIGNATURE=$(SONIC_ENABLE_SECUREBOOT_SIGNATURE) \ + SECURE_UPGRADE_MODE=$(SECURE_UPGRADE_MODE) \ + SECURE_UPGRADE_DEV_SIGNING_KEY=$(SECURE_UPGRADE_DEV_SIGNING_KEY) \ + SECURE_UPGRADE_DEV_SIGNING_CERT=$(SECURE_UPGRADE_DEV_SIGNING_CERT) \ + SECURE_UPGRADE_PROD_SIGNING_TOOL=$(SECURE_UPGRADE_PROD_SIGNING_TOOL) \ SONIC_DEFAULT_CONTAINER_REGISTRY=$(DEFAULT_CONTAINER_REGISTRY) \ ENABLE_HOST_SERVICE_ON_START=$(ENABLE_HOST_SERVICE_ON_START) \ SLAVE_DIR=$(SLAVE_DIR) \ diff --git a/build_debian.sh b/build_debian.sh index fb9b0d918ac9..f710942088b2 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -156,7 +156,8 @@ if [[ $CONFIGURED_ARCH == amd64 ]]; then fi ## Sign the Linux kernel -if [ "$SONIC_ENABLE_SECUREBOOT_SIGNATURE" = "y" ]; then +# note: when flag SONIC_ENABLE_SECUREBOOT_SIGNATURE is enabled the Secure Upgrade flags should be disabled (no_sign) to avoid conflict between the features. +if [ "$SONIC_ENABLE_SECUREBOOT_SIGNATURE" = "y" ] && [ "$SECURE_UPGRADE_MODE" != 'dev' ] && [ "$SECURE_UPGRADE_MODE" != "prod" ]; then if [ ! -f $SIGNING_KEY ]; then echo "Error: SONiC linux kernel signing key missing" exit 1 @@ -621,6 +622,62 @@ then fi +# ################# +# secure boot +# ################# +if [[ $SECURE_UPGRADE_MODE == 'dev' || $SECURE_UPGRADE_MODE == "prod" && $SONIC_ENABLE_SECUREBOOT_SIGNATURE != 'y' ]]; then + # note: SONIC_ENABLE_SECUREBOOT_SIGNATURE is a feature that signing just kernel, + # SECURE_UPGRADE_MODE is signing all the boot component including kernel. + # its required to do not enable both features together to avoid conflicts. + echo "Secure Boot support build stage: Starting .." + + # debian secure boot dependecies + sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install \ + shim-unsigned \ + grub-efi + + if [ ! -f $SECURE_UPGRADE_DEV_SIGNING_CERT ]; then + echo "Error: SONiC SECURE_UPGRADE_DEV_SIGNING_CERT=$SECURE_UPGRADE_DEV_SIGNING_CERT key missing" + exit 1 + fi + + if [[ $SECURE_UPGRADE_MODE == 'dev' ]]; then + # development signing & verification + + if [ ! -f $SECURE_UPGRADE_DEV_SIGNING_KEY ]; then + echo "Error: SONiC SECURE_UPGRADE_DEV_SIGNING_KEY=$SECURE_UPGRADE_DEV_SIGNING_KEY key missing" + exit 1 + fi + + sudo ./scripts/signing_secure_boot_dev.sh -a $CONFIGURED_ARCH \ + -r $FILESYSTEM_ROOT \ + -l $LINUX_KERNEL_VERSION \ + -c $SECURE_UPGRADE_DEV_SIGNING_CERT \ + -p $SECURE_UPGRADE_DEV_SIGNING_KEY + elif [[ $SECURE_UPGRADE_MODE == "prod" ]]; then + # Here Vendor signing should be implemented + OUTPUT_SEC_BOOT_DIR=$FILESYSTEM_ROOT/boot + + if [ ! -f $SECURE_UPGRADE_PROD_SIGNING_TOOL ]; then + echo "Error: SONiC SECURE_UPGRADE_PROD_SIGNING_TOOL=$SECURE_UPGRADE_PROD_SIGNING_TOOL script missing" + exit 1 + fi + + sudo $SECURE_UPGRADE_PROD_SIGNING_TOOL $CONFIGURED_ARCH $FILESYSTEM_ROOT $LINUX_KERNEL_VERSION $OUTPUT_SEC_BOOT_DIR + + # verifying all EFI files and kernel modules in $OUTPUT_SEC_BOOT_DIR + sudo ./scripts/secure_boot_signature_verification.sh -e $OUTPUT_SEC_BOOT_DIR \ + -c $SECURE_UPGRADE_DEV_SIGNING_CERT \ + -k $FILESYSTEM_ROOT + + # verifying vmlinuz file. + sudo ./scripts/secure_boot_signature_verification.sh -e $FILESYSTEM_ROOT/boot/vmlinuz-${LINUX_KERNEL_VERSION}-${CONFIGURED_ARCH} \ + -c $SECURE_UPGRADE_DEV_SIGNING_CERT \ + -k $FILESYSTEM_ROOT + fi + echo "Secure Boot support build stage: END." +fi + ## Update initramfs sudo chroot $FILESYSTEM_ROOT update-initramfs -u ## Convert initrd image to u-boot format diff --git a/files/build_templates/sonic_debian_extension.j2 b/files/build_templates/sonic_debian_extension.j2 index ef52114da617..90f46cc58260 100644 --- a/files/build_templates/sonic_debian_extension.j2 +++ b/files/build_templates/sonic_debian_extension.j2 @@ -78,6 +78,10 @@ fi # Update apt's snapshot of its repos sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get update +# Install efitools to support secure upgrade +sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install efitools +sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install mokutil + # Apply environtment configuration files sudo cp $IMAGE_CONFIGS/environment/environment $FILESYSTEM_ROOT/etc/ sudo cp $IMAGE_CONFIGS/environment/motd $FILESYSTEM_ROOT/etc/ diff --git a/installer/default_platform.conf b/installer/default_platform.conf index c0758b6fb94b..ddf59baffdc8 100755 --- a/installer/default_platform.conf +++ b/installer/default_platform.conf @@ -359,6 +359,71 @@ demo_install_uefi_grub() } +# Install UEFI BIOS SHIM for DEMO OS +demo_install_uefi_shim() +{ + echo "demo_install_uefi_shim(): Installing Secure Boot components" + + local demo_mnt="$1" + local blk_dev="$2" + + # make sure /boot/efi is mounted + if ! mount | grep -q "/boot/efi"; then + mount /boot/efi || { + echo "Error: Unable to mount /boot/efi" + exit 1 + } + fi + + # Look for the EFI system partition UUID on the same block device as + # the ONIE-BOOT partition. + local uefi_part=0 + for p in $(seq 8) ; do + if sgdisk -i $p $blk_dev | grep -q C12A7328-F81F-11D2-BA4B-00A0C93EC93B ; then + uefi_part=$p + break + fi + done + + [ $uefi_part -eq 0 ] && { + echo "ERROR: Unable to determine UEFI system partition" + exit 1 + } + + echo "creating demo_volume_label=$demo_volume_label in dir: /boot/efi/EFI/ ." + mkdir -p /boot/efi/EFI/$demo_volume_label + + if [ ! -f $demo_mnt/$image_dir/boot/mmx64.efi ]; then + echo "ERROR: $demo_mnt/$image_dir/boot/mmx64.efi file does not exist" + exit 1 + fi + + if [ ! -f $demo_mnt/$image_dir/boot/shimx64.efi ]; then + echo "ERROR: $demo_mnt/$image_dir/boot/shimx64.efi file does not exist" + exit 1 + fi + + if [ ! -f $demo_mnt/$image_dir/boot/grubx64.efi ]; then + echo "ERROR: $demo_mnt/$image_dir/boot/grubx64.efi file does not exist" + exit 1 + fi + + echo "copying signed shim, mm, grub, grub.cfg from $demo_mnt/$image_dir/boot/ to /boot/efi/EFI/$demo_volume_label directory" + cp $demo_mnt/$image_dir/boot/mmx64.efi /boot/efi/EFI/$demo_volume_label/mmx64.efi + cp $demo_mnt/$image_dir/boot/shimx64.efi /boot/efi/EFI/$demo_volume_label/shimx64.efi + cp $demo_mnt/$image_dir/boot/grubx64.efi /boot/efi/EFI/$demo_volume_label/grubx64.efi + + # Configure EFI NVRAM Boot variables. --create also sets the + # new boot number as active. + efibootmgr --quiet --create \ + --label "$demo_volume_label" \ + --disk $blk_dev --part $uefi_part \ + --loader "/EFI/$demo_volume_label/shimx64.efi" || { + echo "ERROR: efibootmgr failed to create new boot variable on: $blk_dev" + exit 1 + } + echo "demo_install_uefi_shim(): Secure Boot components installed successfully" +} bootloader_menu_config() { @@ -370,7 +435,15 @@ bootloader_menu_config() mv $onie_initrd_tmp/tmp/onie-support*.tar.bz2 $demo_mnt/$image_dir/ if [ "$firmware" = "uefi" ] ; then - demo_install_uefi_grub "$demo_mnt" "$blk_dev" + secure_boot_state=$(mokutil --sb-state) + echo secure_boot_state=$secure_boot_state + if [ "$secure_boot_state" = "SecureBoot enabled" ]; then + echo "UEFI Secure Boot is enabled" + demo_install_uefi_shim "$demo_mnt" "$blk_dev" + else + echo "UEFI Secure Boot is disabled" + demo_install_uefi_grub "$demo_mnt" "$blk_dev" + fi else demo_install_grub "$demo_mnt" "$blk_dev" fi @@ -469,9 +542,34 @@ EOF fi fi +# Make a first grub config file that located in default debian path:/boot/efi/EFI/debian/ +# this first grub.cfg will call the complete grub.cfg created below with sonic configuration +tmp_config=$(mktemp) +cat < $tmp_config +search --no-floppy --label --set=root $demo_volume_label +set prefix=(\$root)'/grub' +configfile \$prefix/grub.cfg +EOF + +# Copy first grub.cfg as entrypoint to default debian path where grubx64.efi expected it. +mkdir -p /boot/efi/EFI/debian/ +echo "cp $tmp_config /boot/efi/EFI/debian/grub.cfg" +cp $tmp_config /boot/efi/EFI/debian/grub.cfg + # Add extra linux command line echo "EXTRA_CMDLINE_LINUX=$extra_cmdline_linux" GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX $extra_cmdline_linux" +GRUB_CFG_LINUX_CMD="" +GRUB_CFG_INITRD_CMD="" +if [ "$firmware" = "uefi" ] ; then + # grub.cfg when BIOS is UEFI and support Secure Boot + GRUB_CFG_LINUX_CMD="linuxefi" + GRUB_CFG_INITRD_CMD="initrdefi" +else + # grub.cfg when BIOS is Legacy + GRUB_CFG_LINUX_CMD="linux" + GRUB_CFG_INITRD_CMD="initrd" +fi cat <> $grub_cfg menuentry '$demo_grub_entry' { @@ -481,13 +579,13 @@ menuentry '$demo_grub_entry' { if [ x$grub_platform = xxen ]; then insmod xzio; insmod lzopio; fi insmod part_msdos insmod ext2 - linux /$image_dir/boot/vmlinuz-5.10.0-18-2-amd64 root=$grub_cfg_root rw $GRUB_CMDLINE_LINUX \ + $GRUB_CFG_LINUX_CMD /$image_dir/boot/vmlinuz-5.10.0-18-2-amd64 root=$grub_cfg_root rw $GRUB_CMDLINE_LINUX \ net.ifnames=0 biosdevname=0 \ loop=$image_dir/$FILESYSTEM_SQUASHFS loopfstype=squashfs \ systemd.unified_cgroup_hierarchy=0 \ apparmor=1 security=apparmor varlog_size=$VAR_LOG_SIZE usbcore.autosuspend=-1 $ONIE_PLATFORM_EXTRA_CMDLINE_LINUX echo 'Loading $demo_volume_label $demo_type initial ramdisk ...' - initrd /$image_dir/boot/initrd.img-5.10.0-18-2-amd64 + $GRUB_CFG_INITRD_CMD /$image_dir/boot/initrd.img-5.10.0-18-2-amd64 } EOF @@ -510,6 +608,17 @@ EOF cp $grub_cfg $onie_initrd_tmp/$demo_mnt/grub/grub.cfg fi + if [ "$secure_boot_state" = "SecureBoot enabled" ]; then + # Secure Boot grub.cfg support + # Saving grub_cfg in the same place where is grubx64.efi, + # this grub_cfg file will be called by first grub.cfg file from: /boot/efi/EFI/debian/grub.cfg + if [ -f $NVOS_BOOT_DIR/grub.cfg ]; then + rm $NVOS_BOOT_DIR/grub.cfg + fi + + cp $grub_cfg $NVOS_BOOT_DIR/grub.cfg + fi + cd / echo "Installed SONiC base image $demo_volume_label successfully" diff --git a/rules/config b/rules/config index 9b214d582dd5..739a5b2d778a 100644 --- a/rules/config +++ b/rules/config @@ -218,6 +218,15 @@ SONIC_ENABLE_IMAGE_SIGNATURE ?= n # The absolute path should be provided. SONIC_ENABLE_SECUREBOOT_SIGNATURE ?= n +# Full Secure Boot feature flags. +# SECURE_UPGRADE_DEV_SIGNING_KEY - path to development signing key, used for image signing during build +# SECURE_UPGRADE_DEV_SIGNING_CERT - path to development signing certificate, used for image signing during build +# SECURE_UPGRADE_MODE - enum value for secure upgrade mode, valid options are "dev", "prod" and "no_sign" +# SECURE_UPGRADE_PROD_SIGNING_TOOL - path to a vendor signing tool for production flow. +SECURE_UPGRADE_DEV_SIGNING_KEY = /sonic/your/private/key/path/private_key.pem +SECURE_UPGRADE_DEV_SIGNING_CERT = /sonic/your/certificate/path/cert.pem +SECURE_UPGRADE_MODE = "no_sign" +SECURE_UPGRADE_PROD_SIGNING_TOOL ?= # PACKAGE_URL_PREFIX - the package url prefix PACKAGE_URL_PREFIX ?= https://packages.trafficmanager.net/public/packages diff --git a/rules/linux-kernel.dep b/rules/linux-kernel.dep index 4706f56afbaf..7e2dd4741486 100644 --- a/rules/linux-kernel.dep +++ b/rules/linux-kernel.dep @@ -4,11 +4,10 @@ DEP_FILES := rules/linux-kernel.mk rules/linux-kernel.dep SMDEP_FILES := $(addprefix $(SPATH)/,$(shell cd $(SPATH) && git ls-files)) DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST) \ - $(KERNEL_PROCURE_METHOD) $(KERNEL_CACHE_PATH) + $(KERNEL_PROCURE_METHOD) $(KERNEL_CACHE_PATH) $(SECURE_UPGRADE_MODE) $(SECURE_UPGRADE_DEV_SIGNING_CERT) $(LINUX_HEADERS_COMMON)_CACHE_MODE := GIT_CONTENT_SHA $(LINUX_HEADERS_COMMON)_DEP_FLAGS := $(DEP_FLAGS) $(LINUX_HEADERS_COMMON)_DEP_FILES := $(DEP_FILES) $(LINUX_HEADERS_COMMON)_SMDEP_FILES := $(SMDEP_FILES) $(LINUX_HEADERS_COMMON)_SMDEP_PATHS := $(SPATH) - diff --git a/scripts/efi-sign.sh b/scripts/efi-sign.sh new file mode 100755 index 000000000000..34dca50b067d --- /dev/null +++ b/scripts/efi-sign.sh @@ -0,0 +1,63 @@ +#!/bin/sh + +set -e + +# +# Sign efi file with secret key and certificate +# - shim +# - grub +# - vmlinuz +# +print_usage() { + cat < -c -e -s +Usage example: efi-sign.sh -p priv-key.pem -c pub-key.pem -e shimx64.efi -s shimx64-signed.efi + +EOF +} + +while getopts 'p:c:e:s:hv' flag; do + case "${flag}" in + p) PRIVATE_KEY_PEM="${OPTARG}" ;; + c) CERT_PEM="${OPTARG}" ;; + e) EFI_FILE="${OPTARG}" ;; + s) EFI_FILE_SIGNED="${OPTARG}" ;; + v) VERBOSE='true' ;; + h) print_usage + exit 1 ;; + esac +done +if [ $OPTIND -eq 1 ]; then echo "no options were pass"; print_usage; exit 1 ;fi + +[ -f "$PRIVATE_KEY_PEM" ] || { + echo "Error: PRIVATE_KEY_PEM file does not exist: $PRIVATE_KEY_PEM" + print_usage + exit 1 +} + +[ -f "$CERT_PEM" ] || { + echo "Error: CERT_PEM file does not exist: $CERT_PEM" + print_usage + exit 1 +} + +[ -f "$EFI_FILE" ] || { + echo "Error: File for signing does not exist: $EFI_FILE" + print_usage + exit 1 +} + +if [ -z ${EFI_FILE_SIGNED} ]; then + echo "ERROR: no arg named supplied" + print_usage + exit 1 +fi + +echo "$0 signing $EFI_FILE with ${PRIVATE_KEY_PEM}, ${CERT_PEM} to create $EFI_FILE_SIGNED" +sbsign --key ${PRIVATE_KEY_PEM} --cert ${CERT_PEM} \ + --output ${EFI_FILE_SIGNED} ${EFI_FILE} || { + echo "EFI sign error" + exit 1 +} diff --git a/scripts/secure_boot_signature_verification.sh b/scripts/secure_boot_signature_verification.sh new file mode 100755 index 000000000000..e704d8fabc35 --- /dev/null +++ b/scripts/secure_boot_signature_verification.sh @@ -0,0 +1,96 @@ +#!/bin/bash +# This Script is verifying the efi file signature by using sbverify. +# In addition, is verifying that kernel modules a directory contained a signature. +# Note: Kernel Module verification is not checking that the signature is correct, but its checking that the Kernel Modules have one. + +EFI_FILE='' +KERNEL_MODULES_DIR='' +CERT_PEM='' +VERBOSE='false' + +print_usage() { + cat < -c -k +Run Example: secure_boot_signature_verification.sh -e shimx64.efi -c pub-key.pem -k fsroot-mellanox +Run Example: secure_boot_signature_verification.sh -e /boot/efi_dir -c pub-key.pem -k fsroot-mellanox + +EOF +} + +verify_efi(){ + cert_pem=$1 + efi_file=$2 + echo "sbverify --cert $cert_pem $efi_file" + sbverify --cert $cert_pem $efi_file || { + echo "sbverify error with $efi_file" + exit 1 + } + echo "$efi_file signed OK." +} + +while getopts 'e:k:c:hv' flag; do + case "${flag}" in + e) EFI_FILE="${OPTARG}" ;; + k) KERNEL_MODULES_DIR="${OPTARG}" ;; + c) CERT_PEM="${OPTARG}" ;; + v) VERBOSE='true' ;; + h) print_usage + exit 1 ;; + esac +done +if [ $OPTIND -eq 1 ]; then echo "no options were pass"; print_usage; exit 1 ;fi + +if [ -d "$EFI_FILE" ];then + [ -f "$CERT_PEM" ] || { + echo "Error: option '-c' incorrect, file: certificate=$CERT_PEM does not exist" + print_usage + exit 1 + } + + # find all efi files. + efi_file_list=$(sudo find ${EFI_FILE} -name "*.efi") + for efi_file in $efi_file_list + do + echo "verifying efi_file named: ${efi_file} .." + verify_efi $CERT_PEM ${efi_file} + done + echo "$0: All EFI files SIGNED OK." +fi + +if [ -f "$EFI_FILE" ]; then + [ -f "$CERT_PEM" ] || { + echo "Error: option '-c' incorrect, file: certificate=$CERT_PEM does not exist" + print_usage + exit 1 + } + verify_efi $CERT_PEM $EFI_FILE +fi + +if [ -d "$KERNEL_MODULES_DIR" ]; then + # Condition checking that all the kernel modules in the KERNEL_MODULES_DIR contain a signature. + + # find all the kernel modules. + modules_list=$(sudo find ${KERNEL_MODULES_DIR} -name "*.ko") + + # Do sign for each found module + kernel_modules_cnt=0 + for mod in $modules_list + do + # check Kernel module is signed. + if ! grep -q "~Module signature appended~" "${mod}"; then + echo "Error: Kernel module=${mod} have no signature appened." + exit 1 + fi + + if [ $VERBOSE = 'true' ]; then + echo "kernel module named=${mod} have signature appended." + fi + + kernel_modules_cnt=$((kernel_modules_cnt+1)) + done + echo "Num of kernel modules signed: kernel_modules_cnt=$kernel_modules_cnt" + echo "$0: All Kernel Modules SIGNED OK." +fi + diff --git a/scripts/signing_kernel_modules.sh b/scripts/signing_kernel_modules.sh new file mode 100755 index 000000000000..d6db8f8cd39d --- /dev/null +++ b/scripts/signing_kernel_modules.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# This script is signing kernel modules by using sign-file tool + +usage() { + cat < -c -p -s -e -k +Sign kernel modules in using private & public keys. + +Parameters description: +LINUX_KERNEL_VERSION +PEM_CERT public key (pem format) +PEM_PRIVATE_KEY private key (pem format) +LOCAL_SIGN_FILE path of the sign-file tool for signing Kernel Modules, if the value is empty it will used the sign-file installed in /usr/lib/linux-kbuild-/scripts +LOCAL_EXTRACT_CERT path of the extract-cert tool for Extract X.509 certificate, if the value is empty it will used the extract-cert installed in /usr/lib/linux-kbuild-/scripts +KERNEL_MODULES_DIR root directory of all the kernel modules to be sign by the script, if the value empty it will use the call script location as root. + +Runs examples: +1. ./scripts/signing_kernel_modules.sh -l 5.10.0-8-2 -c cert.pem -p priv-key.pem +2. ./scripts/signing_kernel_modules.sh -l 5.10.0-8-2 -c cert.pem -p priv-key.pem -k fsroot-mellanox -e /usr/lib/linux-kbuild-5.10/scripts/extract-cert -s /usr/lib/linux-kbuild-5.10/scripts/sign-file +EOF +} + +while getopts 'l:c:p:k:s:e:hv' flag; do + case "${flag}" in + l) LINUX_KERNEL_VERSION="${OPTARG}" ;; + c) PEM_CERT="${OPTARG}" ;; + p) PEM_PRIVATE_KEY="${OPTARG}" ;; + k) KERNEL_MODULES_DIR="${OPTARG}" ;; + s) LOCAL_SIGN_FILE="${OPTARG}" ;; + e) LOCAL_EXTRACT_CERT="${OPTARG}" ;; + v) VERBOSE='true' ;; + h) usage + exit 1 ;; + esac +done +if [ $OPTIND -eq 1 ]; then echo "no options were pass"; usage; exit 1 ;fi + +if [ -z ${LINUX_KERNEL_VERSION} ]; then + echo "ERROR: LINUX_KERNEL_VERSION arg1 is empty" + usage + exit 1 +fi + +if [ ! -f ${PEM_CERT} ]; then + echo "ERROR: arg2 PEM_CERT=${PEM_CERT} file does not exist" + usage + exit 1 +fi + +if [ ! -f ${PEM_PRIVATE_KEY} ]; then + echo "ERROR: arg3 PEM_PRIVATE_KEY=${PEM_PRIVATE_KEY} file does not exist" + usage + exit 1 +fi + +kbuild_ver_major="$(cut -d '.' -f 1 <<< "$LINUX_KERNEL_VERSION")"."$(cut -d '.' -f 2 <<< "$LINUX_KERNEL_VERSION")" +if [ -z ${LOCAL_SIGN_FILE} ]; then + LOCAL_SIGN_FILE="/usr/lib/linux-kbuild-${kbuild_ver_major}/scripts/sign-file" +fi + +if [ ! -f ${LOCAL_SIGN_FILE} ]; then + echo "ERROR: LOCAL_SIGN_FILE=${LOCAL_SIGN_FILE} file does not exist" + usage + exit 1 +fi + +if [ -z ${LOCAL_EXTRACT_CERT} ]; then + LOCAL_EXTRACT_CERT="/usr/lib/linux-kbuild-${kbuild_ver_major}/scripts/extract-cert" +fi + +if [ ! -f ${LOCAL_EXTRACT_CERT} ]; then + echo "ERROR: LOCAL_EXTRACT_CERT=${LOCAL_EXTRACT_CERT} file does not exist" + usage + exit 1 +fi + +if [ ! -d "$KERNEL_MODULES_DIR" ]; then + # If the user do not provide a KERNEL_MODULES_DIR, the script is going to search in the script call path for Kernel modules. + KERNEL_MODULES_DIR="./" + echo "KERNEL_MODULES_DIR set to default path: $KERNEL_MODULES_DIR" +fi + +# find all the kernel modules. +modules_list=$(find ${KERNEL_MODULES_DIR} -name "*.ko") + +dev_certs_tmp_folder="/tmp/dev_kmod_sign" + +# clean env +if [ -d ${dev_certs_tmp_folder} ]; then + rm -r ${dev_certs_tmp_folder} +fi + +mkdir -p ${dev_certs_tmp_folder} +local_sign_key="${dev_certs_tmp_folder}/$(basename $PEM_PRIVATE_KEY)" +local_sign_cert="${dev_certs_tmp_folder}/$(basename $PEM_CERT)" + +# Combine cert for module signing +echo "keys concat: cat ${PEM_PRIVATE_KEY} ${PEM_CERT} > ${local_sign_key}" +cat ${PEM_PRIVATE_KEY} ${PEM_CERT} > ${local_sign_key} + +# Extract x509 cert in corect format +echo "create x509 cert: ${LOCAL_EXTRACT_CERT} ${local_sign_key} ${local_sign_cert}" +${LOCAL_EXTRACT_CERT} ${local_sign_key} ${local_sign_cert} + +# Do sign for each found module +kernel_modules_cnt=0 +for mod in $modules_list +do + echo "signing module named: ${mod} .." + echo "${LOCAL_SIGN_FILE} sha512 ${local_sign_key} ${local_sign_cert} ${mod}" + kernel_modules_cnt=$((kernel_modules_cnt+1)) + ${LOCAL_SIGN_FILE} sha512 ${local_sign_key} ${local_sign_cert} ${mod} + + # check Kernel module is signed. + if ! grep -q "~Module signature appended~" "${mod}"; then + echo "Error: Kernel module=${mod} have no signature appened." + exit 1 + fi +done + +echo "Num of kernel modules signed: kernel_modules_cnt=$kernel_modules_cnt" +echo "$0: All Kernel Modules SIGNED OK." \ No newline at end of file diff --git a/scripts/signing_secure_boot_dev.sh b/scripts/signing_secure_boot_dev.sh new file mode 100755 index 000000000000..cc937215b9b2 --- /dev/null +++ b/scripts/signing_secure_boot_dev.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# This script is signing boot components: shim, mmx, grub, kernel and kernel modules in development env. + +## Enable debug output for script & exit code when failing occurs +set -x -e + +print_usage() { + cat < -l -c -p + +EOF +} + +clean_file() { + if [ -f $1 ]; then + echo "clean old file named: $1" + echo "rm -f $1" + rm -f $1 + fi +} + +while getopts 'a:r:l:c:p:hv' flag; do + case "${flag}" in + a) CONFIGURED_ARCH="${OPTARG}" ;; + r) FS_ROOT="${OPTARG}" ;; + l) LINUX_KERNEL_VERSION="${OPTARG}" ;; + c) PEM_CERT="${OPTARG}" ;; + p) PEM_PRIV_KEY="${OPTARG}" ;; + v) VERBOSE='true' ;; + h) print_usage + exit 1 ;; + esac +done +if [ $OPTIND -eq 1 ]; then echo "no options were pass"; print_usage; exit 1 ;fi + +echo "$0 signing & verifying EFI files and Kernel Modules start ..." + +if [ -z ${CONFIGURED_ARCH} ]; then + echo "ERROR: CONFIGURED_ARCH=${CONFIGURED_ARCH} is empty" + print_usage + exit 1 +fi + +if [ -z ${FS_ROOT} ]; then + echo "ERROR: FS_ROOT=${FS_ROOT} is empty" + print_usage + exit 1 +fi + +if [ -z ${LINUX_KERNEL_VERSION} ]; then + echo "ERROR: LINUX_KERNEL_VERSION=${LINUX_KERNEL_VERSION} is empty" + print_usage + exit 1 +fi + +if [ ! -f "${PEM_CERT}" ]; then + echo "ERROR: PEM_CERT=${PEM_CERT} file does not exist" + print_usage + exit 1 +fi + +if [ ! -f "${PEM_PRIV_KEY}" ]; then + echo "ERROR: PEM_PRIV_KEY=${PEM_PRIV_KEY} file does not exist" + print_usage + exit 1 +fi + +# efi-sign.sh is used to sign: shim, mmx, grub, and kernel (vmlinuz) +EFI_SIGNING=scripts/efi-sign.sh + +# ###################################### +# Signing EFI files: mm, shim, grub +# ##################################### +efi_file_list=$(sudo find ${KERNEL_MODULES_DIR} -name "*.efi") + +for efi in $efi_file_list +do + # grep filename from full path + efi_filename=$(echo $efi | grep -o '[^/]*$') + + if echo $efi_filename | grep -e "shim" -e "grub" -e "mm"; then + + clean_file ${efi}-signed + + echo "signing efi file - full path: ${efi} filename: ${efi_filename}" + echo "sudo ${EFI_SIGNING} -p $PEM_PRIV_KEY -c $PEM_CERT -e ${efi} -s ${efi}-signed" + ${EFI_SIGNING} -p $PEM_PRIV_KEY -c $PEM_CERT -e ${efi} -s ${efi}-signed + + # cp shim & mmx signed files to boot directory in the fs. + cp ${efi}-signed $FS_ROOT/boot/${efi_filename} + + # verifying signature of mm & shim efi files. + ./scripts/secure_boot_signature_verification.sh -c $PEM_CERT -e $FS_ROOT/boot/${efi_filename} + fi +done + +###################### +## vmlinuz signing +###################### + +CURR_VMLINUZ=$FS_ROOT/boot/vmlinuz-${LINUX_KERNEL_VERSION}-${CONFIGURED_ARCH} + +# clean old files +clean_file ${CURR_VMLINUZ}-signed + +echo "signing ${CURR_VMLINUZ} .." +${EFI_SIGNING} -p $PEM_PRIV_KEY -c $PEM_CERT -e ${CURR_VMLINUZ} -s ${CURR_VMLINUZ}-signed + +# rename signed vmlinuz with the name vmlinuz without signed suffix +mv ${CURR_VMLINUZ}-signed ${CURR_VMLINUZ} + +./scripts/secure_boot_signature_verification.sh -c $PEM_CERT -e ${CURR_VMLINUZ} + +######################### +# Kernel Modules signing +######################### +./scripts/signing_kernel_modules.sh -l $LINUX_KERNEL_VERSION -c ${PEM_CERT} -p ${PEM_PRIV_KEY} -k ${FS_ROOT} + +echo "$0 signing & verifying EFI files and Kernel Modules DONE" diff --git a/slave.mk b/slave.mk index 7cfae3d408f5..017ba9cfb145 100644 --- a/slave.mk +++ b/slave.mk @@ -371,6 +371,10 @@ $(info "USE_NATIVE_DOCKERD_FOR_BUILD" : "$(SONIC_CONFIG_USE_NATIVE_DOCKERD_FO $(info "SONIC_USE_DOCKER_BUILDKIT" : "$(SONIC_USE_DOCKER_BUILDKIT)") $(info "USERNAME" : "$(USERNAME)") $(info "PASSWORD" : "$(PASSWORD)") +$(info "SECURE_UPGRADE_MODE" : "$(SECURE_UPGRADE_MODE)") +$(info "SECURE_UPGRADE_DEV_SIGNING_KEY" : "$(SECURE_UPGRADE_DEV_SIGNING_KEY)") +$(info "SECURE_UPGRADE_DEV_SIGNING_CERT" : "$(SECURE_UPGRADE_DEV_SIGNING_CERT)") +$(info "SECURE_UPGRADE_PROD_SIGNING_TOOL": "$(SECURE_UPGRADE_PROD_SIGNING_TOOL)") $(info "ENABLE_DHCP_GRAPH_SERVICE" : "$(ENABLE_DHCP_GRAPH_SERVICE)") $(info "SHUTDOWN_BGP_ON_START" : "$(SHUTDOWN_BGP_ON_START)") $(info "ENABLE_PFCWD_ON_START" : "$(ENABLE_PFCWD_ON_START)") @@ -1229,6 +1233,10 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ export enable_ztp="$(ENABLE_ZTP)" export include_teamd="$(INCLUDE_TEAMD)" export include_router_advertiser="$(INCLUDE_ROUTER_ADVERTISER)" + export sonic_su_dev_signing_key="$(SECURE_UPGRADE_DEV_SIGNING_KEY)" + export sonic_su_dev_signing_cert="$(SECURE_UPGRADE_DEV_SIGNING_CERT)" + export sonic_su_mode="$(SECURE_UPGRADE_MODE)" + export sonic_su_prod_signing_tool="$(SECURE_UPGRADE_PROD_SIGNING_TOOL)" export include_system_telemetry="$(INCLUDE_SYSTEM_TELEMETRY)" export include_restapi="$(INCLUDE_RESTAPI)" export include_nat="$(INCLUDE_NAT)" @@ -1426,6 +1434,10 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \ TARGET_MACHINE=$(dep_machine) \ IMAGE_TYPE=$($*_IMAGE_TYPE) \ SONIC_ENABLE_IMAGE_SIGNATURE="$(SONIC_ENABLE_IMAGE_SIGNATURE)" \ + SECURE_UPGRADE_MODE="$(SECURE_UPGRADE_MODE)" \ + SECURE_UPGRADE_DEV_SIGNING_KEY="$(SECURE_UPGRADE_DEV_SIGNING_KEY)" \ + SECURE_UPGRADE_DEV_SIGNING_CERT="$(SECURE_UPGRADE_DEV_SIGNING_CERT)" \ + SECURE_UPGRADE_PROD_SIGNING_TOOL="$(SECURE_UPGRADE_PROD_SIGNING_TOOL)" \ SIGNING_KEY="$(SIGNING_KEY)" \ SIGNING_CERT="$(SIGNING_CERT)" \ CA_CERT="$(CA_CERT)" \