[{"title":"在 systemd-boot Fedora 上安装 NVIDIA 驱动程序","url":"/zh-cn/posts/systemd-nvidia/","date":"2026-07-26","tags":["driver","linux"],"summary":"0. 开始之前# 我在 Dell Precision 5570 笔记本电脑上进行安装，注册 db 密钥的步骤可能因你的电脑而异。\n这里涉及两个独立的信任操作：\nEFI 签署：固件 db 信任 systemd-boot 和你的 UKI。 内核模块签署：NVIDIA 模块由内核信任的密钥签署。 当直接通过 systemd-boot 启动时，不需要 MOK，前提是模块证书存在于固件 db 中，且 …","content":" 0. 开始之前# 我在 Dell Precision 5570 笔记本电脑上进行安装，注册 db 密钥的步骤可能因你的电脑而异。\n这里涉及两个独立的信任操作：\nEFI 签署：固件 db 信任 systemd-boot 和你的 UKI。 内核模块签署：NVIDIA 模块由内核信任的密钥签署。 当直接通过 systemd-boot 启动时，不需要 MOK，前提是模块证书存在于固件 db 中，且 Fedora 将该密钥导入到模块验证接受的 keyring 中。线上的教程主要针对 grub+shim，通常会要求你注册 MOK 密钥。\n1. 安装所需软件包# 根据 Fedora 版本启用 RPM Fusion，然后安装：\nbash COPY 1sudo dnf install akmods kernel-devel-matched openssl keyutils efitools sbctl 确保匹配的内核开发文件存在：\nbash COPY 1KVER=$(uname -r) 2test -x /usr/src/kernels/\u0026amp;#34;$KVER\u0026amp;#34;/scripts/sign-file 2. 创建专用的 akmods 密钥# 如果软件包尚未生成 Fedora akmods 密钥，请手动生成：\nbash COPY 1sudo kmodgenca -a 检查路径：\nbash COPY 1sudo readlink -e /etc/pki/akmods/private/private_key.priv 2sudo readlink -e /etc/pki/akmods/certs/public_key.der 两个命令都必须返回真实的文件路径。\n确认证书：\nbash COPY 1sudo openssl x509 \\ 2 -inform DER \\ 3 -in /etc/pki/akmods/certs/public_key.der \\ 4 -noout -subject -issuer -fingerprint -sha256 此输出不应为空。\n3. 修复 Akmods 密钥权限# RPM 构建以非特权用户 akmods 身份执行。Root 可以解析并读取 symlink，但 akmods 无法访问其目标。因此，以下测试在构建中失败：\nbash COPY 1[ -e /etc/pki/akmods/private/private_key.priv ] Shell 短路了签署条件，因此从未检查 public_key.der，也从未调用 /usr/lib/rpm/brp-kmodsign。\n对解析后的目标以及目录遍历权限进行设置：\nbash COPY 1PRIVATE=$(sudo readlink -e /etc/pki/akmods/private/private_key.priv) 2PUBLIC=$(sudo readlink -e /etc/pki/akmods/certs/public_key.der) 3 4sudo chown root:akmods \u0026amp;#34;$PRIVATE\u0026amp;#34; 5sudo chmod 640 \u0026amp;#34;$PRIVATE\u0026amp;#34; 6 7sudo chown root:akmods \u0026amp;#34;$PUBLIC\u0026amp;#34; 8sudo chmod 644 \u0026amp;#34;$PUBLIC\u0026amp;#34; 9 10sudo chown root:akmods /etc/pki/akmods/private 11sudo chmod 750 /etc/pki/akmods/private 12 13sudo chmod 755 /etc/pki/akmods/certs 14sudo restorecon -RFv /etc/pki/akmods 使用执行构建的身份进行测试：\nbash COPY 1sudo -u akmods test -r /etc/pki/akmods/private/private_key.priv 2sudo -u akmods test -r /etc/pki/akmods/certs/public_key.der 两个命令都必须返回 0。\n确认密钥和证书匹配：\nbash COPY 1sudo openssl pkey \\ 2 -in /etc/pki/akmods/private/private_key.priv \\ 3 -pubout -outform DER | sha256sum 4 5sudo openssl x509 \\ 6 -inform DER \\ 7 -in /etc/pki/akmods/certs/public_key.der \\ 8 -pubkey -noout …"},{"title":"P2481 [SDOI2010] 代码拍卖会","url":"/zh-cn/notes/p2481/","date":"2026-07-25","tags":["luogu","cp"],"summary":"设：\ndp[len][last][rem] last：目前最后一只猪的出价，范围 1..9 rem：目前构成的 len 位数除以 P 的余数 若在右边加入数字 x，必须满足 x \u0026gt;= last，新余数是：\n$newRem=(rem\\times 10+x)\\bmod P$\n","content":"设：\ndp[len][last][rem] last：目前最后一只猪的出价，范围 1..9 rem：目前构成的 len 位数除以 P 的余数 若在右边加入数字 x，必须满足 x \u0026amp;gt;= last，新余数是：\n$newRem=(rem\\times 10+x)\\bmod P$\n"},{"title":"签署 systemd-boot 文件并注册 PK 以启用安全启动","url":"/zh-cn/posts/systemd-secureboot/","date":"2026-07-25","tags":["linux"],"summary":"0. 开始之前# 最近我尝试安装 Fedora 来取代 Ubuntu，以摆脱 snap 的烂东西。然后我不知道从哪里冒出一个想法，决定试试 systemd-boot，噩梦就是这样开始的。\n这里涉及两个独立的信任操作：\nEFI 签署：固件 db 信任 systemd-boot 和你的 UKI。 内核模块签署：NVIDIA 模块由内核信任的密钥签署。 当直接通过 systemd-boot 启动时，不需 …","content":" 0. 开始之前# 最近我尝试安装 Fedora 来取代 Ubuntu，以摆脱 snap 的烂东西。然后我不知道从哪里冒出一个想法，决定试试 systemd-boot，噩梦就是这样开始的。\n这里涉及两个独立的信任操作：\nEFI 签署：固件 db 信任 systemd-boot 和你的 UKI。 内核模块签署：NVIDIA 模块由内核信任的密钥签署。 当直接通过 systemd-boot 启动时，不需要 MOK，前提是模块证书存在于固件 db 中，且 Fedora 将该密钥导入到模块验证接受的 keyring 中。线上的教程主要针对 grub+shim，通常会要求你注册 MOK 密钥。\n如果你对我的安装过程以及我如何为安全启动配置 systemd-boot 不感兴趣，请跳过第 0 章。\n安装 Fedora# systemd-boot 方式只能使用 Everything netinst 镜像进行配置（据说离线镜像也可以做到，但步骤更复杂）。从 USB 密钥启动后，导航到——\n创建密钥：\nbash COPY 1sudo sbctl create-keys 注册 sbctl 密钥同时保留 Microsoft 证书：\nbash COPY 1sudo sbctl enroll-keys -m 当需要 Microsoft 签署的 Option ROM、Windows 或其他 Microsoft 签署的 EFI 组件时，-m 参数很重要。\n签署已注册的 EFI 文件：\nbash COPY 1sudo sbctl sign-all 2sudo sbctl verify "},{"title":"删除 CF Pages 部署","url":"/zh-cn/posts/cf-too-many-deploy/","date":"2026-07-25","tags":["cloudflare","pages","tools"],"summary":"当你尝试删除一个拥有超过约 100 次部署的 Pages 项目时（每次构建都算作一次部署，例如你更新了博客中的一篇文章），你可能会遇到以下错误：Your project has too many deployments to be deleted, follow this guide to delete them: https://cfl.re/3CXesln\n于是我让大肥鱼（Deepseek）帮 …","content":"当你尝试删除一个拥有超过约 100 次部署的 Pages 项目时（每次构建都算作一次部署，例如你更新了博客中的一篇文章），你可能会遇到以下错误：Your project has too many deployments to be deleted, follow this guide to delete them: https://cfl.re/3CXesln\n于是我让大肥鱼（Deepseek）帮我写一个 Python 脚本，自动删除部署，这样我之后就可以删除该项目。cloudflare-bulk-delete 这个仓库已经坏了，因为 Pages API 每页查询的记录上限是 25 条，而该工具每次发送 100 条记录的查询，导致 HTTP 400 错误。\npython COPY 1#!/usr/bin/env python3 2\u0026amp;#34;\u0026amp;#34;\u0026amp;#34;Bulk delete all Cloudflare Pages deployments in a project.\u0026amp;#34;\u0026amp;#34;\u0026amp;#34; 3 4import os 5import sys 6import requests 7 8# Config — set these via environment variables or edit inline 9ACCOUNT_ID = os.environ.get(\u0026amp;#34;CLOUDFLARE_ACCOUNT_ID\u0026amp;#34;, \u0026amp;#34;your_account_id\u0026amp;#34;) 10TOKEN = os.environ.get(\u0026amp;#34;CLOUDFLARE_API_TOKEN\u0026amp;#34;, \u0026amp;#34;cfut_kfcCRAZYthursday\u0026amp;#34;) 11PROJECT = os.environ.get(\u0026amp;#34;CLOUDFLARE_PROJECT\u0026amp;#34;, \u0026amp;#34;hugo\u0026amp;#34;) 12 13BASE = f\u0026amp;#34;https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/pages/projects/{PROJECT}/deployments\u0026amp;#34; 14HEADERS = {\u0026amp;#34;Authorization\u0026amp;#34;: f\u0026amp;#34;Bearer {TOKEN}\u0026amp;#34;} 15 16 17def fetch_all_deployment_ids(): 18 \u0026amp;#34;\u0026amp;#34;\u0026amp;#34;Fetch all deployment IDs across all pages.\u0026amp;#34;\u0026amp;#34;\u0026amp;#34; 19 ids = [] 20 page = 1 21 while True: 22 resp = requests.get(f\u0026amp;#34;{BASE}?page={page}\u0026amp;#34;, headers=HEADERS).json() 23 if not resp[\u0026amp;#34;success\u0026amp;#34;]: 24 print(f\u0026amp;#34;Error fetching page {page}: {resp[\u0026amp;#39;errors\u0026amp;#39;]}\u0026amp;#34;, file=sys.stderr) 25 sys.exit(1) 26 27 ids.extend(d[\u0026amp;#34;id\u0026amp;#34;] for d in resp[\u0026amp;#34;result\u0026amp;#34;]) 28 total_pages = resp[\u0026amp;#34;result_info\u0026amp;#34;][\u0026amp;#34;total_pages\u0026amp;#34;] 29 print(f\u0026amp;#34; Page {page}/{total_pages} — collected {len(ids)} total\u0026amp;#34;) 30 31 if page \u0026amp;gt;= total_pages: 32 break 33 page += 1 34 35 return ids 36 37 38def delete_deployment(dep_id): 39 \u0026amp;#34;\u0026amp;#34;\u0026amp;#34;Delete a single deployment. Returns (ok, message).\u0026amp;#34;\u0026amp;#34;\u0026amp;#34; 40 r = requests.delete(f\u0026amp;#34;{BASE}/{dep_id}\u0026amp;#34;, headers=HEADERS) 41 data = r.json() 42 if data.get(\u0026amp;#34;success\u0026amp;#34;): 43 return True, \u0026amp;#34;OK\u0026amp;#34; 44 else: 45 err = data.get(\u0026amp;#34;errors\u0026amp;#34;, …"}]