diff --git a/dkms-custom-sign-file b/dkms-custom-sign-file new file mode 100755 index 0000000000000000000000000000000000000000..9193e38df383d222d6356207fc29fae97ce6f177 --- /dev/null +++ b/dkms-custom-sign-file @@ -0,0 +1,295 @@ +#!/bin/bash +# 新增内网环境下 DKMS 自定义签名逻辑 +# **签名路径选择逻辑**: + +# prepare_signing() 被调用 +# │ +# ├── 检测到 /etc/dkms/token 文件 +# │ └── 使用 dkms-custom-sign-file(自定义签名平台) +# │ ├── 读取 token 中的 API 地址和证书 ID +# │ ├── 计算模块 hash 并请求签名平台 +# │ └── 将 PKCS#7 签名附加到内核模块 +# │ +# └── 未检测到 token 文件 +# └── 继续执行原生 DKMS 签名流程(MOK 密钥签名) + +# 使用方式 + +# 内部机器(自定义签名平台) + +# 1. 申请 token:Tmaneger 签名平台 +# 2. 放置 token 文件: +# sudo tee /etc/dkms/token < your-token.json +# sudo chmod 600 /etc/dkms/token + +# 3. 安装 dkms 后,后续所有 DKMS 模块构建会自动使用自定义签名平台 + +# 外部机器(原生 MOK 签名) + +# 无需任何额外配置。没有 token 文件时,dkms 自动使用原生签名流程(MOK 密钥),与上游行为一致。 + + +set -e + +LOG_FILE="${DKMS_CUSTOM_SIGN_LOG:-/var/log/dkms-custom-sign.log}" +# 日志函数 +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" +} + +log_error() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "$LOG_FILE" >&2 +} + +# 配置项 +TOKEN_FILE="/etc/dkms/token" + +# 查找 sign-file 工具(用于附加签名到内核模块) +find_sign_file_tool() { + local kernel_ver + # 使用当前运行的内核版本 + kernel_ver="$(uname -r)" + + local kernel_scripts_dir="/usr/src/kernels/${kernel_ver}/scripts" + local kernel_sign_file="${kernel_scripts_dir}/sign-file" + local kernel_sign_file_c="${kernel_scripts_dir}/sign-file.c" + + if [[ ! -d "${kernel_scripts_dir}" ]]; then + log_error "kernel-devel 未安装或损坏,尝试执行: dnf install kernel-devel-$(uname -r) -y" + return 1 + fi + + is_elf() { + if command -v file >/dev/null 2>&1; then + file -b "$1" 2>/dev/null | grep -q "ELF" + return $? + fi + if command -v readelf >/dev/null 2>&1; then + readelf -h "$1" >/dev/null 2>&1 + return $? + fi + return 1 + } + + if [[ ! -f "${kernel_sign_file}" ]] || ! is_elf "${kernel_sign_file}"; then + # 注意: 本函数通过 stdout 返回路径,所有日志必须重定向到 stderr,避免污染返回值 + log "sign-file 不是可执行的 ELF 文件,从源码编译: ${kernel_sign_file_c}" >&2 + if [[ ! -f "${kernel_sign_file_c}" ]]; then + log_error "找不到 sign-file 源码: ${kernel_sign_file_c}, kernel-devel 未安装或损坏,尝试执行: dnf install kernel-devel-$(uname -r) -y" + return 1 + fi + + local gcc_output + if ! gcc_output=$(gcc -o "${kernel_sign_file}" "${kernel_sign_file_c}" -lcrypto 2>&1); then + [[ -n "$gcc_output" ]] && echo "$gcc_output" >> "$LOG_FILE" + log_error "编译 sign_file 失败: ${kernel_sign_file_c}" + return 1 + fi + [[ -n "$gcc_output" ]] && echo "$gcc_output" >> "$LOG_FILE" + + chmod +x "${kernel_sign_file}" 2>/dev/null || true + log "已重新编译 sign_file: ${kernel_sign_file}" >&2 + fi + + echo "${kernel_sign_file}" + return 0 +} + +# 使用自定义签名平台签名 +custom_sign() { + log "使用自定义签名平台签名模块: ${MODULE_PATH}" + + # 1. 验证模块文件存在 + if [[ ! -f "${MODULE_PATH}" ]]; then + log_error "模块文件不存在: ${MODULE_PATH}" + return 1 + fi + + # 2. 读取 token 并解析配置 + if [[ ! -f "$TOKEN_FILE" ]]; then + log_error "Token 文件不存在: $TOKEN_FILE" + return 1 + fi + + local token + token=$(<"$TOKEN_FILE") + if [[ -z "$token" ]]; then + log_error "Token 文件为空" + return 1 + fi + + # 从 token 文件中解析签名配置 + local sign_hash signing_api_url certificate_id + if command -v jq >/dev/null 2>&1; then + sign_hash=$(jq -r '.hash_algo' < "$TOKEN_FILE") + signing_api_url=$(jq -r '.url' < "$TOKEN_FILE") + certificate_id=$(jq -r '.uuid' < "$TOKEN_FILE") + else + sign_hash=$(grep -o '"hash_algo":"[^"]*"' "$TOKEN_FILE" | sed 's/"hash_algo":"//;s/"$//') + signing_api_url=$(grep -o '"url":"[^"]*"' "$TOKEN_FILE" | sed 's/"url":"//;s/"$//') + certificate_id=$(grep -o '"uuid":"[^"]*"' "$TOKEN_FILE" | sed 's/"uuid":"//;s/"$//') + fi + + if [[ -z "$sign_hash" || "$sign_hash" == "null" ]]; then + log_error "Token 文件中缺少 hash_algo 字段" + return 1 + fi + if [[ -z "$signing_api_url" || "$signing_api_url" == "null" ]]; then + log_error "Token 文件中缺少 url 字段" + return 1 + fi + if [[ -z "$certificate_id" || "$certificate_id" == "null" ]]; then + log_error "Token 文件中缺少 uuid 字段" + return 1 + fi + + log "签名配置: hash_algo=$sign_hash, api_url=$signing_api_url, uuid=$certificate_id" + + # 3. 计算模块 hash + log "计算模块 hash (算法: sha256)..." + local kmod_hash + kmod_hash=$(sha256sum "${MODULE_PATH}" | awk '{print $1}') + + log "模块 hash: $kmod_hash" + + # 5. 调用签名平台 API + log "请求签名平台..." + local temp_response + temp_response=$(mktemp) + + local http_code + http_code=$(curl -s -w "%{http_code}" -o "$temp_response" \ + -X POST \ + -H "Content-Type: application/json" \ + -d "{\"data\": \"${kmod_hash}\", \"certificate_id\": \"${certificate_id}\", \"data_type\": \"HASH_VALUE\", \"hash_algorithm\": \"SHA256\"}" \ + "${signing_api_url}/certificates/sign") + + if [[ "$http_code" != "200" ]]; then + log_error "签名平台返回错误: HTTP $http_code" + echo "--- 签名平台响应内容 ---" >> "$LOG_FILE" + cat "$temp_response" >> "$LOG_FILE" + echo "" >> "$LOG_FILE" + rm -f "$temp_response" + return 1 + fi + + # 5. 提取 PKCS#7 签名 + log "提取签名数据..." + local pkcs7_signature + + # 假设 API 返回 JSON: {"signature": "base64-encoded-pkcs7", "status": "success"} + # 根据您的实际 API 调整 + if command -v jq >/dev/null 2>&1; then + pkcs7_signature=$(jq -r '.pkcs7_detached' < "$temp_response") + else + # 简单的 grep/sed 方案(不推荐用于生产) + pkcs7_signature=$(grep -o '"pkcs7_detached":"[^"]*"' "$temp_response" | sed 's/"pkcs7_detached":"//;s/"$//') + fi + + if [[ -z "$pkcs7_signature" || "$pkcs7_signature" == "null" ]]; then + log_error "无法从响应中提取签名" + echo "--- 签名平台响应内容 ---" >> "$LOG_FILE" + cat "$temp_response" >> "$LOG_FILE" + echo "" >> "$LOG_FILE" + rm -f "$temp_response" + return 1 + fi + + rm -f "$temp_response" + + # 6. 保存签名到临时文件 + local temp_sig + temp_sig=$(mktemp --suffix=.pem) + { + echo "-----BEGIN PKCS7-----" + echo "$pkcs7_signature" + echo "-----END PKCS7-----" + } > "$temp_sig" + + openssl pkcs7 -inform PEM -outform DER -in "$temp_sig" -out "$temp_sig.der" + + if [[ ! -s "$temp_sig" ]]; then + log_error "签名文件创建失败" + rm -f "$temp_sig" + return 1 + fi + + # 7. 使用内核 sign-file -s 附加签名到模块 + log "附加签名到模块..." + + # 查找 sign-file 工具 + local sign_file_tool + if ! sign_file_tool=$(find_sign_file_tool); then + log_error "找不到 sign-file 工具" + rm -f "$temp_sig" + return 1 + fi + + log "使用 sign-file 工具: $sign_file_tool" + + # 使用 -s 参数附加已有签名 + # 用法: sign_file -s + local sign_output + if ! sign_output=$("$sign_file_tool" -s "$temp_sig.der" "$sign_hash" "noused" "${MODULE_PATH}" 2>&1); then + [[ -n "$sign_output" ]] && echo "$sign_output" >> "$LOG_FILE" + log_error "附加签名失败" + rm -f "$temp_sig.der" + return 1 + fi + [[ -n "$sign_output" ]] && echo "$sign_output" >> "$LOG_FILE" + + rm -f "$temp_sig.der" + + log "模块签名成功: ${MODULE_PATH}" + + return 0 +} + +# 主逻辑 +main() { + if [[ $# -ne 4 ]]; then + log_error "参数错误: 需要 4 个参数 (hash_algo key cert module_path)" + echo "用法: $0 hash_algo key cert module_path" >&2 + exit 1 + fi + MODULE_PATH="$4" + + mkdir -p "$(dirname "$LOG_FILE")" + + log "========== DKMS 自定义签名开始 ==========" + log "Token 文件: $TOKEN_FILE ($([ -f "$TOKEN_FILE" ] && echo '存在' || echo '不存在'))" + log "模块路径: ${MODULE_PATH}" + + if [[ ! -f "$TOKEN_FILE" ]]; then + log_error "Token 文件不存在: $TOKEN_FILE" + log_error "dkms-custom-sign-file 仅用于自定义签名平台,需要 token 文件" + log_error "" + log_error "申请地址: 参考帮助文档" + log_error "放置命令: sudo tee $TOKEN_FILE < your-token.json && sudo chmod 600 $TOKEN_FILE" + exit 1 + fi + + local help_url + help_url=$(jq -r '.help_url // empty' < "$TOKEN_FILE" 2>/dev/null) + + log "检测到 token 文件,使用内部自定义签名平台" + if custom_sign; then + log "========== 自定义签名成功 ==========" + exit 0 + else + log_error "========== 自定义签名失败 ==========" + log_error "" + log_error "排查步骤:" + log_error " 1. 检查 token 文件是否有效: cat $TOKEN_FILE | jq ." + log_error " 2. 检查网络是否可达签名平台: curl -s -o /dev/null -w '%{http_code}' \$(jq -r '.url' $TOKEN_FILE)/health" + log_error " 3. 检查 token 是否过期,如需重新申请:" + log_error " 申请地址: 参考帮助文档" + [[ -n "$help_url" ]] && log_error " 帮助链接: $help_url" + log_error " 放置命令: sudo tee $TOKEN_FILE < your-token.json && sudo chmod 600 $TOKEN_FILE" + log_error " 4. 检查 kernel-devel 是否安装: rpm -q kernel-devel-\$(uname -r)" + log_error " 5. 查看详细日志: $LOG_FILE" + exit 1 + fi +} + +main "$@" \ No newline at end of file diff --git a/dkms.spec b/dkms.spec index 603fdf1c7eba82151cc81f4f0450aad6884ea06f..ad26ad9b2d521e254fafef2716125efc4f12c384 100644 --- a/dkms.spec +++ b/dkms.spec @@ -1,10 +1,14 @@ Summary: Dynamic Kernel Module Support Framework Name: dkms Version: 3.2.2 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ URL: http://linux.dell.com/dkms Source0: https://github.com/dell/%{name}/archive/v%{version}.tar.gz +Source1: dkms-custom-sign-file + +%global dkms_token_path /etc/dkms/token +%global custom_sign_file %{_prefix}/lib/%{name}/dkms-custom-sign-file BuildArch: noarch BuildRequires: make systemd @@ -19,7 +23,7 @@ Requires(post): systemd Requires(preun): systemd Requires(postun): systemd -Recommends: openssl +Requires: curl openssl jq %description This package provides the framework for the Dynamic Kernel Module Support (DKMS) @@ -35,72 +39,39 @@ DKMS_IN=dkms.in ## added more distributions support sed -i "s/fedora\*/opencloudos\* | tencentos\* | fedora\*/g" $DKMS_IN -## disable prepare_mok function via return 0 -sed -i -e '/^prepare_mok()$/!b' -e 'n' -e 'n' -e 'i\ # OpenCloudOS and TencentOS does not need dkms signing kernel module.' -e 'i\ return 0' $DKMS_IN - -## custom prepare_signing -signing_start=$(grep -n "^prepare_signing()" "$DKMS_IN" | cut -d: -f1) - -if [[ -z "$signing_start" ]]; then - echo "✗ prepare_signing() function not found" +## add custom signing support in prepare_signing() +## insert token detection logic after "do_signing=0" (content-based, no line numbers) +if ! grep -q '^prepare_signing()$' "$DKMS_IN"; then + echo "ERROR: prepare_signing() function not found in $DKMS_IN" exit 1 fi - -# find start line number of do_build -next_func_start=$(tail -n +$((signing_start + 1)) "$DKMS_IN" | grep -n "^do_build()" | head -1 | cut -d: -f1) - -if [[ -z "$next_func_start" ]]; then - # if not find do_build, find the next anyone function - next_func_start=$(tail -n +$((signing_start + 1)) "$DKMS_IN" | grep -n "^[a-zA-Z_].*()$" | head -1 | cut -d: -f1) -fi - -if [[ -z "$next_func_start" ]]; then - echo "✗ can't find the end line number of next function after prepare_signing()" +if ! sed -n '/^prepare_signing()$/,/^}$/p' "$DKMS_IN" | grep -q '^ do_signing=0$'; then + echo "ERROR: 'do_signing=0' not found in prepare_signing() in $DKMS_IN" exit 1 fi -# Calculate the actual line number of the next function in the file -next_func_real_line=$((signing_start + next_func_start)) - -# The range we will delete: from prepare_signing start up to before the next function starts -# But be careful to preserve any comments above the next function (if any) -# To be safe, we look for the first standalone `}` after prepare_signing -# This approach is reasonably reliable for well-formatted bash scripts - -func_end_line=$(tail -n +$signing_start "$DKMS_IN" | grep -n "^}" | head -1 | cut -d: -f1) -func_end_line=$((signing_start + func_end_line - 1)) - -echo " Located prepare_signing() function range: lines $signing_start - $func_end_line" - -# Create new function content -cat > /tmp/new_prepare_signing.txt << 'NEWFUNC' -prepare_signing() -{ - # Lazy source in signing related configuration - read_framework_conf "$dkms_framework_signing_variables" - - # OpenCloudOS and TencentOS: Check if custom signing is configured - # If sign_file is set via config, enable signing; otherwise disable - if [[ -n "${sign_file}" ]]; then - echo "Custom signing enabled: $sign_file" +cat > /tmp/custom_signing_block.txt << 'SIGNBLOCK' + + # Custom signing: if token file exists, use custom signing platform (internal machines) + if [[ -f __DKMS_TOKEN_PATH__ ]]; then + sign_file="__CUSTOM_SIGN_FILE__" + if [[ ! -f ${sign_file} || ! -x ${sign_file} ]]; then + echo "Error: Custom sign script ${sign_file} not found or not executable" + return + fi + sign_hash="sha256" + mok_signing_key="unused" + mok_certificate="unused" do_signing=1 + echo "Custom signing: token detected, using ${sign_file}" return 0 fi - - # No custom signing configured, disable signing - echo "Signing disabled (no custom sign_file configured)" - do_signing=0 - return 0 -} -NEWFUNC +SIGNBLOCK +sed -i "s|__DKMS_TOKEN_PATH__|%{dkms_token_path}|" /tmp/custom_signing_block.txt +sed -i "s|__CUSTOM_SIGN_FILE__|%{custom_sign_file}|" /tmp/custom_signing_block.txt -# Use sed to delete the old function and insert the new function -# Delete lines from start to end -sed -i "${signing_start},${func_end_line}d" "$DKMS_IN" -# Insert new content at start position -sed -i "${signing_start}r /tmp/new_prepare_signing.txt" "$DKMS_IN" - -rm -f /tmp/new_prepare_signing.txt +sed -i '/^ do_signing=0$/r /tmp/custom_signing_block.txt' $DKMS_IN +rm -f /tmp/custom_signing_block.txt @@ -112,6 +83,7 @@ make install \ install -D -m 0644 dkms.service %{buildroot}%{_unitdir}/dkms.service install -D -m 0755 redhat_kernel_install.d %{buildroot}%{_prefix}/lib/kernel/install.d/40-dkms.install install -D -m 0755 dkms_common.postinst %{buildroot}%{_prefix}/lib/dkms/common.postinst +install -D -m 0755 %{SOURCE1} %{buildroot}%{custom_sign_file} sed -i -e 's|# modprobe_on_install="true"|modprobe_on_install="true"|g' %{buildroot}%{_sysconfdir}/%{name}/framework.conf @@ -123,34 +95,53 @@ ln -sf %{_bindir}/dkms %{buildroot}%{_sbindir}/dkms %check set -e -echo "starting sign module function and distributions support" +echo "=== Starting build verification ===" DKMS_FILE="%{buildroot}%{_bindir}/dkms" +CUSTOM_SIGN_FILE="%{buildroot}%{custom_sign_file}" -# check1: if prepare_mok() has return 0 -if ! grep -A 3 '^prepare_mok()$' "$DKMS_FILE" | grep -q -x ' return 0'; then - echo "错误: prepare_mok()函数中未正确添加return 0" +# check1: opencloudos* and tencentos* distribution support +if ! grep -q 'opencloudos\* | tencentos\* | fedora\*' "$DKMS_FILE"; then + echo "错误: 未正确添加opencloudos*和tencentos*到分发列表" exit 1 fi +echo "check1 passed: distribution support OK" -# check2: if prepare_signing() has return 0 after do_signing=0 -if ! grep -A 2 '^ do_signing=0$' "$DKMS_FILE" | grep -q -x ' return 0'; then - echo "错误: prepare_signing()函数中未正确添加return 0" +# check2: custom signing token detection in prepare_signing() +if ! grep -q '%{dkms_token_path}' "$DKMS_FILE"; then + echo "错误: prepare_signing()函数中未包含 token 检测逻辑" exit 1 fi +echo "check2 passed: token detection in prepare_signing() OK" -# check3: if opencloudos* and tencentos* be added -if ! grep -q 'opencloudos\* | tencentos\* | fedora\*' "$DKMS_FILE"; then - echo "错误: 未正确添加opencloudos*和tencentos*到分发列表" +# check3: custom sign script path reference in dkms +if ! grep -q 'dkms-custom-sign-file' "$DKMS_FILE"; then + echo "错误: dkms 中未引用 dkms-custom-sign-file" + exit 1 +fi +echo "check3 passed: custom sign script reference OK" + +# check4: dkms-custom-sign-file exists and is executable +if [[ ! -f "$CUSTOM_SIGN_FILE" || ! -x "$CUSTOM_SIGN_FILE" ]]; then + echo "错误: dkms-custom-sign-file 不存在或不可执行: $CUSTOM_SIGN_FILE" exit 1 fi +echo "check4 passed: dkms-custom-sign-file installed OK" -# check4: check script sytax +# check5: dkms script syntax check if ! bash -n "$DKMS_FILE"; then echo "错误: $DKMS_FILE 存在语法错误" exit 1 fi +echo "check5 passed: dkms syntax OK" + +# check6: dkms-custom-sign-file syntax check +if ! bash -n "$CUSTOM_SIGN_FILE"; then + echo "错误: $CUSTOM_SIGN_FILE 存在语法错误" + exit 1 +fi +echo "check6 passed: dkms-custom-sign-file syntax OK" -echo "opencloudos and tencentos has been added in" +echo "=== All build verification checks passed ===" @@ -184,6 +175,14 @@ echo "opencloudos and tencentos has been added in" %changelog +* Mon Mar 09 2026 yugozhang - 3.2.2-5 +- [type] sync +- add custom signing support: use /etc/dkms/token to detect internal machines +- internal machines: use dkms-custom-sign-file for custom signing platform +- external machines: restore native DKMS signing with MOK keys +- add dkms-custom-sign-file to /usr/lib/dkms/ +- add opencloudos and tencentos distribution support in source + * Wed Dec 24 2025 Zhao Zhen - 3.2.2-4 - customed prepare_signing