惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
P
Proofpoint News Feed
The Hacker News
The Hacker News
Know Your Adversary
Know Your Adversary
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tenable Blog
AWS News Blog
AWS News Blog
S
Securelist
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
IT之家
IT之家
腾讯CDC
WordPress大学
WordPress大学
Spread Privacy
Spread Privacy
C
Check Point Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Engineering at Meta
Engineering at Meta
Latest news
Latest news
A
About on SuperTechFans
The Register - Security
The Register - Security
L
LINUX DO - 热门话题
T
The Exploit Database - CXSecurity.com
C
Cisco Blogs
T
Tailwind CSS Blog
Simon Willison's Weblog
Simon Willison's Weblog
阮一峰的网络日志
阮一峰的网络日志
MyScale Blog
MyScale Blog
大猫的无限游戏
大猫的无限游戏
T
Tor Project blog
L
Lohrmann on Cybersecurity
G
GRAHAM CLULEY
B
Blog RSS Feed
Scott Helme
Scott Helme
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
NISL@THU
NISL@THU
P
Privacy International News Feed
Security Latest
Security Latest
Recorded Future
Recorded Future
L
LangChain Blog
Cyberwarzone
Cyberwarzone
C
Cyber Attacks, Cyber Crime and Cyber Security
C
CXSECURITY Database RSS Feed - CXSecurity.com
博客园 - 聂微东
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
Apple Machine Learning Research
Apple Machine Learning Research
F
Fortinet All Blogs
O
OpenAI News
T
Threat Research - Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale

qlAD 的技术笔记

2026 年 6 月 7 日 -- 我有社交恐惧症,恐惧外面到处雷电风雨声(大学生活记录) 手把手带你实现哈希表(C/C++ 数据结构) 手把手带你实现图结构(C/C++ 数据结构) 手把手带你实现二叉树(C/C++ 数据结构) 手把手带你实现栈和队列(C/C++ 数据结构) 手把手带你实现单/双链表(C/C++ 数据结构) 手把手带你实现动态数组(C/C++ 数据结构) 2026 年 1 月 2 日 -- 我不怕冷,因为我内心炽热 【2025 年度总结】(大学生活记录) 2025 年 12 月 2 日 -- 省电模式:大一 100 天耗电日志(大学生活记录) 蓝桥杯备赛第一期 —— 枚举与模拟 2025 年 11 月 3 日 -- 用爱发电、大爱无私(大学生活记录) 使用循环嵌套输出规则图形找规律讲解(嵌套循环-图形输出) 2025 年 10 月 1 日 -- 二字开头第一年的生日记录及感悟随笔(大学生活记录) Python 初级 04 -- 目前为止 Python 中你们做题可能会遇到的问题以及七七八八 2025 年 9 月 24 日 -- 开学后的第一课及军训汇演(大学生活记录) Python 初级 03 -- 程序设计的三种结构之选择结构和循环结构 Python 初级 02 -- 基础的数据类型和输入输出 2025 年 9 月 17 日 -- 大连 2025 国际大体联足球世界杯(大学生活记录) Python 初级 01 -- 初识编程、理解计算机语言及程序运行方式 适合大一新生学习编程的前一课 -- 编程先导(编程的本质) 2025 年 9 月 12 日 -- 三天军训感受以及一些七七八八(大学生活记录) 2025 年 9 月 9 日 -- 体检、军训前的准备(大学生活记录) 适合大一新生的计算机基础知识 -- 个人认为足够版 2025 年 9 月 7 日 -- 既来之、则安之(大学生活记录) C++ 程序的内存布局 —— 代码区、全局/静态区、栈区和堆区 联想小新 pro 16 2025 开箱记录:跳过联网、office 激活、更换 win 11 专业版 R 语言中的数学函数 计算机软件著作权申请过程记录 从聚类到回归:用 Python 解析鸢尾花数据集的完整数据科学流程 条件概率、全概率与贝叶斯公式 —— 一篇文章带你死磕概率公式 高中化学物质反应规则表 —— 方程式之间也有规律可循 集合元素性质、关系、子集公式、基本运算 —— 一篇文章带你全方面死磕集合 如何为你的项目选择合适的许可证 -- 全方位指南 解释 3:1 和 9:3:3:1 的详细来源 —— 建立减数分裂与孟德尔遗传学定律之间的联系 重新设置磁盘分区以及 Btrfs 子卷(subvolume)布局的无损方案 零基础实战:用 Docker 启动 dockermailserver 绕过 25 端口封锁搭建个人邮箱服务器 如何在安装 Debian 时配置 Btrfs 子卷 | 详细分步指南 一个合格的个人网站站长应该知道哪些事? CSS 基础入门教程 —— 选择器、样式声明 HTML 基础入门 —— 基本结构和语法 给博客添加一个文章数据统计的页面 Git 学习笔记(命令备忘表) Anki 牌组选项详细解释与设置 手动编译带有 KernelSU 的 OnePlus 6 内核 安卓类原生系统补全计划 —— PixelExperience(一加 OnePlus) 如何使用 use-sound 为 React 应用添加声音效果 标准化项目的 GitHub Flow 工作流 如何搭建设计自己的博客网站? 使用安知鱼主题同款的 Twikoo 评论组件 配置 GitHub Actions 实现自动化提交百度收录 第00期 | 环境搭建 & 递归 (一) | 基本数列递归 逐字符讲解 C 语言 HelloWorld 程序 2023 年回顾 🤟 Anki:记忆神器,助你高效学习 Gentoo 学习笔记 美化你的 PixelExperience OS Markdown 语法指南 搭建 OneDrive 目录索引 为什么用 65 表示大写字母 A
基于 GitHub Actions + OSS 的自托管 APT 仓库全面指南
qlAD · 2025-04-29 · via qlAD 的技术笔记

cover

效果预览

只需要在软件列表文件中添加你想要入库的 github 链接地址即可,比如:

APT 仓库软件列表

当软件列表文件更新后会生成带有索引目录的 APT 仓库

索引目录的 APT 仓库

核心优势

  • 跨平台分发:GitHub Pages(国际) + 阿里云 OSS(国内)双通道
  • 军工级安全:GPG 签名 + 密钥分层管理
  • 极速构建:单次构建耗时 < 2 分钟(实测数据)
  • 零成本起步:GitHub Actions 免费配额

如果你是专家,你可以直接 Fork 我的仓库改改就能用:https://github.com/qlAD/aptRepo

前置知识

APT 仓库如何工作

首先要明白 APT 仓库其实就是一个规范整理的文件夹,里面包含了软件包和索引文件。

具体的目录结构如下:

.
├── conf
│   └── distributions
├── db
│   ├── checksums.db
│   ├── contents.cache.db
│   ├── packages.db
│   ├── references.db
│   ├── release.caches.db
│   └── version
├── dists
│   └── sid
│       ├── InRelease
│       ├── main
│       │   └── binary-amd64
│       │       ├── Packages
│       │       ├── Packages.gz
│       │       └── Release
│       ├── Release
│       └── Release.gpg
└── pool
    └── main
        └── c
            └── clash-verge
                └── clash-verge_2.2.3_amd64.deb

我们需要做的就是整理好这些文件放到一个可以被访问的地方,并且让 APT 客户端知道这个地方。

整理文件很简单,有一个自动化的工具叫做 reprepro,它会自动帮你生成这些文件。

然后把这些文件放到一个可以被访问的地方,比如 Web 服务器、GitHub Pages 或者阿里云 OSS。

最后在 /etc/apt/sources.list.d 中添加一个 .sources 文件,告诉 APT 客户端这个地方在哪里。

# /etc/apt/sources.list.d/qladgk.sources
Types: deb
URIs: https://apt-repo.qladgk.com/debian
Suites: sid
Components: main
Signed-By: /etc/apt/keyrings/qladgk.asc

然后当你运行 apt update 的时候,APT 客户端就会去这个地方下载索引文件,然后根据索引文件去下载软件包。

GPG 签名

GPG 密钥就好像互联网上的另一个自己,私钥就像是你的身份证,公钥就像是你的名片。

公钥可以随意发给别人,私钥必须要保管好。

APT 仓库的软件包都需要进行 GPG 签名,这样才能保证文件的完整性和安全性,或者直接对整个仓库进行签名。

如果你不进行签名,APT 客户端会报错,提示你这个仓库不可信。

如果你泄露了私钥,别人可以通过你的私钥对软件包进行签名,这样就可以伪造软件包,APT 客户端会认为这个软件包是可信的。

部署搭建步骤

1. 生成 GPG 密钥对

首先需要安装 GPG 工具,如果你使用的是 Debian 或 Ubuntu,可以直接使用以下命令安装:

接下来生成 GPG 密钥对,使用以下命令:

在生成过程中,你需要选择密钥类型、密钥大小、有效期等信息。

其中密钥类型包括如下选项

GPG 密钥类型

其中默认的 ECC(椭圆曲线密码学)提供了更高的安全性与更短的密钥长度,但它在某些旧系统中可能不被支持

所以这里就选择第一项 RSA 和 RSAi 就行

然后密钥大小建议选择 4096 位,这样可以提供更高的安全性

有效期可以根据需要设置,建议设置为 1 年以上,因为 Debian 的 apt 会警告一年内即将过期的密钥,这里我选的是永不过期

当所有信息填完之后会打开密码输入框来加密你的密钥,有密码的好处是即使泄露了私钥,别人也不能通过一个带密码的私钥来签名

全部截图如下:

生成带密码的 GPG 密钥

到此密钥对生成成功,接下来需要把密钥对导出,然后私钥给到 Github Action 让它帮我们自动签名,把公钥暴露在网络上供人使用

2. 导出密钥对

首先我们可以查看刚才创建的密钥对

确认密钥存在后,使用 ASCII 的形式导出

因为我们要把私钥复制到 Github Action 中的环境变量里,所以要用 ASCII 这种方便复制的形式

gpg -a -o public-key.asc --export 你的密钥中的邮箱 #导出公钥
gpg -a -o private-key.asc --export-secret-keys 你的密钥中的邮箱 #导出私钥

导出密钥的时候需要输入你刚才设置的密码

-a 为 –armor 的简写,表示密钥以 ASCII 的形式输出,不加则默认以二进制的形式 gpg 输出

-o 为 –output 的简写,指定写入的文件

全部截图如下:

导出 GPG 密钥

密钥创建并导出之后就可以开始设置 Github 仓库

3. 创建 GitHub 仓库

由于我们要使用 GitHub Actions 来自动化构建 APT 仓库,并且会用到 Github Pages 来搭建仓库的访问地址,所以首先需要一个 GitHub 仓库来存放我们的代码和配置。

首先在 GitHub 上创建一个新的仓库,比如 apt-repo

创建 GitHub 仓库

然后来到仓库的设置页面,允许 Actions 的一些权限

允许 Actions 权限

接着设置一些环境变量,如下

Actions 的环境变量

其中前三个与阿里云的 OSS 相关,如果你没有阿里云的 OSS 可以不设置

后两个是 GPG 的私钥和密码,复制私钥和输入私钥的密码就行

4. 配置 GitHub Actions

在仓库根目录下创建一个 .github/workflows 目录,然后在里面创建一个 update-repo.yml 文件。

内容如下:

name: Update APT Repository

on:
  workflow_dispatch:
  push:
    paths:
      - "packages.list"

jobs:
  build-repo:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Environment
        run: |
          sudo apt-get update
          sudo apt-get install -y reprepro gnupg wget

      - name: Import GPG key
        uses: crazy-max/ghaction-import-gpg@v6
        with:
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.GPG_PASSPHRASE }}

      - name: Fetch Packages
        run: |
          mkdir -p downloaded_packages
          ./scripts/fetch-packages.sh

      - name: Build Repo
        run: |
          ./scripts/setup-reprepro.sh

      - name: Upload APT Repo to Aliyun OSS
        uses: fangbinwei/aliyun-oss-website-action@v1
        with:
          accessKeyId: ${{ secrets.ACCESS_KEY_ID }}
          accessKeySecret: ${{ secrets.ACCESS_KEY_SECRET }}
          bucket: ${{ secrets.ACCESS_BUCKET_NAME }}
          endpoint: oss-cn-shanghai.aliyuncs.com
          folder: ./gh-pages

      - name: Generate Directory Listings
        uses: jayanta525/github-pages-directory-listing@v4.0.0
        with:
          FOLDER: ./gh-pages

      - name: Upload artifact
        uses: actions/upload-pages-artifact@v3.0.1
        with:
          path: "./gh-pages"

  deploy-index:
    needs: build-repo
    permissions:
      pages: write
      id-token: write
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4.0.0

支持手动触发和当 packages.list 有更新后自动触发,当然你也可以设置每周触发一次,但是频率不要太高

如果你不需要阿里云的 OSS 部署就将这部分删掉

- name: Upload APT Repo to Aliyun OSS
  uses: fangbinwei/aliyun-oss-website-action@v1
  with:
    accessKeyId: ${{ secrets.ACCESS_KEY_ID }}
    accessKeySecret: ${{ secrets.ACCESS_KEY_SECRET }}
    bucket: ${{ secrets.ACCESS_BUCKET_NAME }}
    endpoint: oss-cn-shanghai.aliyuncs.com
    folder: ./gh-pages

其中还用到了两个 action 的模板,如果有想了解的自己去看

5. 编写脚本

./scripts/fetch-packages.sh 用于下载软件列表 packages.list 中特点架构的软件包

#!/bin/bash

REPO_LIST="packages.list"
TMP_DIR="downloaded_packages"

while IFS= read -r repo_url; do
  repo_owner=$(echo "$repo_url" | cut -d'/' -f4)
  repo_name=$(echo "$repo_url" | cut -d'/' -f5)

  # 仅下载 amd64 架构的包
  assets=$(curl -s "https://api.github.com/repos/${repo_owner}/${repo_name}/releases/latest" |
           jq -r '.assets[] | select(.name | test(".*_amd64\\.deb$")) | .browser_download_url')

  for asset_url in $assets; do
    wget -P "$TMP_DIR" "$asset_url"
  done
done < "$REPO_LIST"

packages.list 文件中每一行对应一个 Github 仓库

./scripts/setup-reprepro.sh 用来创建 APT 仓库所需要的目录结构

#!/bin/bash

REPO_DIR="gh-pages/debian"
CONF_DIR="repo-config"

# 清理旧数据
rm -rf "$REPO_DIR" "$CONF_DIR"

# 初始化配置目录
mkdir -p "$REPO_DIR"
mkdir -p "$CONF_DIR"/conf

Codename="sid"
Suite="$Codename"

# 生成核心配置文件
cat > "$CONF_DIR"/conf/distributions <<EOF
Origin: apt-repo
Label: apt-repo
Description: Self APT Repository
Codename: $Codename
Suite: $Suite
Architectures: amd64
Components: main
SignWith: yes
EOF

# 导入软件包
for deb in downloaded_packages/*.deb; do
  # 导入到仓库
  reprepro -V -b "$CONF_DIR" -S misc includedeb "$Codename" "$deb"
done

# 生成元数据
reprepro -b "$CONF_DIR" export "$Codename"

# 合并文件到发布目录
rsync -a "$CONF_DIR"/ "$REPO_DIR/"

关于 distribution 中 codename/component/suite 这些术语,debian 官方是有解释

一般 codenam 与 suite 一致,都是各个版本的代号,比如 bionicxenialtrixie

另外别忘了给脚本加上可执行权限

6. Github Pages

接下来在仓库的设置页面,开启 GitHub Pages 功能,选择 Actions 作为源

开启 GitHub Pages 功能

最后别忘了你还要上传一个 packages.list 文件

最后结构应该如下所示

最后结构

然后只用运行 Action 就会自动完成所有事

验收成果

查看索引网页

本身的 APT 仓库是只能被 apt line 识别,没有索引。但是我在 action 里面加了搭建索引的步骤

只需要在浏览器访问你的 pages 地址即可,因为我自定义了域名所以是 https://repos.qladgk.com/debian/

你应该会看到如下界面

索引目录的 APT 仓库

其实索引并不重要,只是方便网页查看而已

导入这个 apt 仓库

首先要接受别人的仓库你需要下载别人仓库提供的公钥,如果你还不知道公钥是什么,就往前翻

我的公钥的直链下载链接在 https://apt-repo.qladgk.com/public-key.asc

所以我可以直接用命令行下载

sudo apt-get update
sudo apt-get install ca-certificates curl #必须安装的工具
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://apt-repo.qladgk.com/public-key.asc -o /etc/apt/keyrings/qladgk.asc
sudo chmod a+r /etc/apt/keyrings/qladgk.asc

这里将下载的公钥存放在 /etc/apt/keyrings 也是 Debian 官方推荐的,现在很少用 apt-key add 这个命令,也不推荐放在 /etc/apt/trusted.gpg.d 或者 /usr/share/keyrings/

然后创建软件源管理文件,推荐使用 DEB822 格式 /etc/apt/sources.list.d/xxxxx.sources 来管理软件源

创建 /etc/apt/sources.list.d/qladgk.sources 文件,内容如下:

Types: deb
URIs: https://repos.qladgk.com/debian
Suites: sid
Components: main
Signed-By: /etc/apt/keyrings/qladgk.asc

其中的 Suites、Components 按照你的仓库来填写

测试安装软件

接下来执行 apt update 你就能看见 apt 已经获取到了这个仓库的数据

完整截图如下:

导入仓库

然后尝试安装你仓库里的软件包

apt安装软件

验证仓库签名

由于我在脚本中选择的是对整个仓库进行签名,所以需要下载仓库的密钥进行核对

在如图的位置中下载仓库签名

仓库签名

然后通过 gpg 验证

gpg --verify Release.gpg Release

完整截图如下:

验证仓库签名

结尾

其实只要是个标准的 apt 仓库都能通过这种方法进行管理,比如说 Docker、Chrome、Vscode

apt软件源

如果你想把旧版本的 sources.list 换成 xxxx.sources 可以通过如下命令

不用担心你会丢失原有的 .list 他只是被重命名成了 .list.old

最后,如何用包管理器快速方便的获取到你想要的包呢?把 Debian 换成 Arch 就行 :)