Sign kernel module cho secure boot trong Debian Linux

Nếu hệ thống của bạn đang hỗ trợ EFI Secure Boot thì dù cho bạn cài đặt các kernel module bằng apt thì vẫn cần phải “sign” chúng để có thể sử dụng.

Tạo một 2048 bit RSA certificate (MOK.der, và private key là MOK.priv) trong thư mục /root/module-signing, dùng cho việc sign kernel module. Ta sẽ để expiry date của certificate này hẳn 10 năm (3650 ngày).

$ su -
$ mkdir -p /root/module-signing
$ cd /root/module-signing
$ openssl req \
    -new -x509 -newkey rsa:2048 -keyout MOK.priv -outform DER -out MOK.der -nodes \
    -days 36500 \
    -subj "/CN=$(hostname -s | cut -b1-31) Secure Boot Module Signature key/"
$ chmod 600 MOK.priv

Sử dụng công cụ mokutil để đăng ký key pair vừa tạo với Secure Boot.

$ sudo mokutil --import /root/module-signing/MOK.der
input password:
input password again:

Nhập mật khẩu sẽ được dùng để xác thực trong quá trình cập nhật Secure Boot.

Sau đó hãy khởi động lại máy.

Màn hình khởi động của Bootloader sẽ hiển thị và yêu cầu ta ấn một phím bất kỳ để tiếp tục vào màn hình MOK Manager. Chọn Enroll MOK trong menu, rồi chọn Yes. Tiếp đó hệ thống sẽ yêu cầu mật khẩu bạn đã nhập ở trên.

Sau khi nhập mật khẩu chính xác, key pair bạn tạo sẽ được đăng ký với Secure Boot.

Lấy ví dụ điển hình là các kernel module của VirtualBox. Sau khi khởi động lại, ta tiến hành ‘sign’ các kernel module của VirtualBox bằng đoạn lệnh sau. Lưu ý là ta có thể chọn hash algorithm là hash256 hoặc hash512 (mạnh hơn nhưng ít phổ biến hơn):

for modfile in $(dirname $(modinfo -n vboxdrv))/*.ko; do
  echo "Signing ${modfile}"
  sudo kmodsign sha256 \
          /root/module-signing/MOK.priv \
          /root/module-signing/MOK.der  \
          "${modfile}"
done

Shellscript

/usr/local/bin/sign_ko.sh

#!/usr/bin/env bash
#
# sign_ko.sh - Sign kernel modules for secure boot.
#

# Run the script as a super user
[ ${UID} -eq 0 ] || exec sudo "${BASH}" "${BASH_SOURCE[0]}" "$@"

set -eo pipefail
shopt -s nullglob

HASHALGO=sha512
OPENSSL=/usr/bin/openssl
MOKDIR=/var/lib/shim-signed/mok/
MOKUTIL=/usr/bin/mokutil
MODINFO=/sbin/modinfo
KMODSIGN=/usr/bin/kmodsign
KVER=$(uname -r)

usage()
{
    echo "Usage: $(basename "${0}") [-h] [-c] [-r <rev>] <modules...>"
    echo
    echo "  Signs all given kernel modules."
    echo "  If no kernel revision is specified with -r, the revision"
    echo "  '${KVER}' will be processed."
    exit 1
}

confirm()
{
    read -p "$1 [yN]: " -n 1 -r
    echo
    [[ ${REPLY} =~ ^[Yy]$ ]]
}

gen_x509_keys()
{
    mkdir -p ${MOKDIR}
    cd ${MOKDIR}
    ${OPENSSL} req -new -x509 -newkey rsa:2048 \
        -keyout MOK.priv -outform DER -out MOK.der \
        -nodes -days 36500 \
        -subj "/CN=$(hostname -s | cut -b1-31) Secure Boot Module Signature key/"
    chmod 600 MOK.priv
    ${MOKUTIL} --import MOK.der
    reboot
}

get_x509_subject()
{
    ${OPENSSL} x509 -in ${1} -subject -noout | sed 's/subject=CN = //'
}

get_ko_path()
{
    ${MODINFO} -n "${1}" || true
}

get_ko_signer()
{
    ${MODINFO} "${1}" | grep signer: | sed 's/^signer:[ ]\+//' || true
}

while getopts hcr: OPTION; do
    case ${OPTION} in
        h)  usage >&2
            ;;
        r)  KVER=${OPTARG}
            ;;
        c)  if confirm "Create a new MOK key pair?"; then
                gen_x509_keys
            fi
            exit 0
            ;;
        *)  echo "Unknown option: ${OPTION}!" >&2
            exit 1
            ;;
    esac
done
shift $(( OPTIND - 1 ))

if [ $# -eq 0 ]; then
    echo No kernel module was specified. Exiting. >&2
    exit 1
fi

if [ ! -d "/lib/modules/${KVER}" ]; then
    echo Revision ${KVER} not found. Exiting. >&2
    exit 1
fi

if [ ! -x ${KMODSIGN} ]; then
    # Detect an alternative kmodsign tool
    KMODSIGN=/usr/src/linux-headers-$(uname -r)/scripts/sign-file
    if [ ! -x ${KMODSIGN} ]; then
        echo "No usable 'kmodsign' binary found - aborting" >&2
        exit 1
    fi
    echo "KMODSIGN=${KMODSIGN}"
fi

if [ ! -f ${MOKDIR}/MOK.der -o ! -f ${MOKDIR}/MOK.priv ]; then
    if confirm "MOK not found. Create a new MOK key pair?"; then
        gen_x509_keys
    else
        exit 1
    fi
fi

SUBJECT=$(get_x509_subject ${MOKDIR}/MOK.der)

for module in "$@"; do
    if [ -f "${module}" ]; then
        KOBJ="$(readlink -f "${module}")"
    elif [ -f "${module}.ko" ]; then
        KOBJ="$(readlink -f "${module}.ko")"
    else
        KOBJ="$(get_ko_path "${module}")"
        if [ $? -ne 0 ]; then
            echo Module "'${module}'" not found! >&2
            exit 1
        fi
    fi

    NAME="$(basename "${module}" .ko)"

    SIGNER=$(get_ko_signer "${KOBJ}")

    if [ "${SIGNER}" = "${SUBJECT}" ]; then
        echo "Module '${NAME}' has been signed with the current MOK!"
        continue
    fi

    if test -n "${SIGNER}" && ! confirm "Module '${NAME}' has been signed by ${SIGNER}. Overwrite?"; then
        continue
    fi

    while [ -z "${KBUILD_SIGN_PIN}" ]; do
        read -rs -p "Enter MOK passphrase: " KBUILD_SIGN_PIN
        export KBUILD_SIGN_PIN
        echo
    done

    ${KMODSIGN} ${HASHALGO} ${MOKDIR}/MOK.priv ${MOKDIR}/MOK.der "${KOBJ}"
    echo "Module '${NAME}' has been signed successfully!"
done

Cấp quyền executable cho script.

sudo chmod +x /usr/local/bin/sign_ko.sh

Thực thi script trên bằng sudo.

sudo sign_ko.sh -r 6.11.0-21-generic vboxdrv vboxnetadp vboxnetflt

Phản hồi về bài viết

Cùng thảo luận chút nhỉ!

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.