Klice/homepki
GitHub: Klice/homepki
一个可自托管的轻量级私有 TLS 证书颁发机构,通过浏览器界面为内网服务和 Tailscale 节点签发、轮换与吊销自签名证书。
Stars: 0 | Forks: 0
# homepki
一个小型的、可自托管的 **web app**,用于运行你自己的私有 TLS
证书颁发机构。签发、轮换、吊销和下载证书;
配置部署目标;管理密码短语 —— **所有操作都在
浏览器中完成**。无需安装 `homepki` CLI:只需一个 Docker 容器
即可运行服务器并提供 UI。

## 为什么你可能需要它
**这个项目存在的原因:为 [Tailscale](https://tailscale.com/) 节点提供 HTTPS。**
如果你给你的 tailnet 机器起了像 `nas`、
`media` 或 `pi.tail-scale.ts.net` 这样友好的名称,Let's Encrypt 帮不上忙 —— 这些
名称不在公共 DNS 中。但是一旦你在每台
设备上信任了 homepki root,你的 tailnet 上的每个内部服务都会获得真正的 HTTPS,而没有
浏览器警告,并且证书使用你的设备实际
响应的任何名称:简短的 MagicDNS 名称、`*.lan` 别名、原始 tailnet IP。
同样的问题也出现在普通家庭实验室中 —— 位于 `nas.lan` 的 NAS,
位于 `192.168.x.x` 的路由器管理页面、Home Assistant 盒子、
自托管的 Git 服务器。Let's Encrypt 不会签发,浏览器警告很快就会让人厌烦,
而从博客文章中复制过来的 `openssl` 咒语很容易出岔子。
homepki 为你提供了缺失的中间环节:
- 一个你在设备上信任一次的 **root CA**。
- 用于单个服务的 **Leaf certs**,带有正确的 SAN(`nas.lan`、
`192.168.1.10`、MagicDNS 名称)和你真正可以
轮换的短生命周期。
- **部署目标** 会将证书和密钥存放到 nginx /
Caddy / haproxy 预期的磁盘路径上,之后还可以选择为你运行 `nginx -s reload`。
- 内置于每个证书中的 **公共 CRL endpoint**,这样当你吊销
证书时,你信任的设备就能真正获知。
- 使用你选择的密码短语进行静态加密。锁定应用后,
内存中的密钥会被擦除;磁盘上的私钥将变得不可读,直到
你再次解锁。
它故意做得很小:一个二进制文件、一个 SQLite 文件、一个操作者。没有
ACME 服务器,没有多租户 CA 仪表板。如果你需要这些,请使用
[step-ca](https://github.com/smallstep/certificates) 或
[EJBCA](https://www.ejbca.org/)。
## 运行它
发布的镜像位于 `ghcr.io/klice/homepki`。选择符合
你需求的标签 —— `latest` 是最新的稳定版本,
`vX.Y.Z` 固定到特定版本,而 `edge` 跟随 main 分支
(最前沿,可能是未发布的)。
### 使用 Docker 快速开始
**1. 启动容器。**
```
docker run -d \
--name homepki \
-p 8080:8080 \
-v homepki-data:/data \
-e CRL_BASE_URL=http://localhost:8080 \
ghcr.io/klice/homepki:latest
```
**2. 在浏览器中打开 web UI** 。
首次访问时,你会看到 **首次运行设置** 界面 —— 选择一个
密码短语(≥ 12 个字符)并确认它。把它记在
安全的地方;没有它,加密的密钥将无法恢复。
**3. 从仪表板中签发你的第一个证书链**:一个 root CA,然后
是一个中间 CA,最后是你想要保护的服务的 leaf certs。
所有操作都是点击式的;不需要 `openssl`。
### 使用 Docker Compose
如果你希望它在重启后依然存活并与其他
服务共存,请将以下内容放入 `docker-compose.yml`:
```
services:
homepki:
image: ghcr.io/klice/homepki:latest
container_name: homepki
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- homepki-data:/data
environment:
# Required. Base URL clients use to fetch CRLs — baked into every
# cert at issuance, so it must be reachable by the devices that will
# verify those certs. http://localhost:8080 is fine if you only use
# them on the host running homepki; otherwise use the address other
# machines on your network see this service at.
CRL_BASE_URL: http://homepki.lan:8080
# Address the HTTP server binds to inside the container. The image
# always exposes 8080; change this only if you remap ports.
# CM_LISTEN_ADDR: ":8080"
# Where the SQLite database lives inside the container. The image
# already declares /data as a volume; mount yours there.
# CM_DATA_DIR: "/data"
# If set, the app auto-unlocks at startup using this passphrase.
# Convenient for unattended boxes; less secure than typing it into
# the unlock screen each time. Leave unset to require manual unlock.
# CM_PASSPHRASE: ""
# Auto-lock after this many minutes of inactivity. Unset / 0 means
# the app stays unlocked until you click Lock or restart it.
# CM_AUTO_LOCK_MINUTES: "0"
# Log format. "json" is friendlier for log shippers; "text" for tailing.
# CM_LOG_FORMAT: "json"
# UID/GID the homepki process drops to inside the container. The
# entrypoint runs as root, recreates the homepki user at these
# IDs, chowns /data, and then exec's the binary as that user.
# Defaults to 1000:1000. Set PUID=99 PGID=100 on Unraid so the
# appdata share's default ownership Just Works.
# PUID: "1000"
# PGID: "1000"
volumes:
homepki-data:
```
然后:
```
docker compose up -d
```
与单纯的 Docker 路径相同:在浏览器中打开 即可访问 web UI。
### 在你的设备上信任 root CA
一旦你签发了 root CA,从其详情页面下载 `cert.pem` 并
将其添加到每台设备的信任存储中。以下是一些常见的:
- **Linux (Debian/Ubuntu):** 将文件复制到
`/usr/local/share/ca-certificates/homepki-root.crt` 并运行
`sudo update-ca-certificates`。
- **macOS:** 双击 `.crt` 文件 → 钥匙串访问 →
将 *使用此证书时* 设置为 *始终信任*。
- **Windows:** 双击 → *安装证书* → *本地计算机* →
*将所有证书放入* → *受信任的根证书颁发机构*。
- **Firefox:** 有自己的存储。*设置 → 隐私与安全 →
证书 → 查看证书 → 证书颁发机构 → 导入*。
- **Android / iOS:** 查阅适用于你版本的 *安装用户证书*;
现代 Android 会将用户安装的 root 视为值得警告,除非
你已经 root。
之后,homepki 在该 root 下签发的每个证书都将被信任,而不会出现警告。
## 配置
所有配置均通过环境变量完成。
| 变量 | 默认值 | 作用 |
| ----------------------- | ---------------- | ----------------------------------------------------------- |
| `CRL_BASE_URL` | *必填* | 客户端用于获取 CRL 的 Base URL。内置于每个签发的证书中。 |
| `CM_LISTEN_ADDR` | `:8080` | HTTP 服务器绑定的地址。 |
| `CM_DATA_DIR` | `/data` | SQLite 数据库所在的路径。在此处挂载卷。 |
| `CM_PASSPHRASE` | *未设置* | 如果设置,应用会在启动时自动解锁。对于无人值守的机器很方便;但不如每次在 UI 中输入密码短语安全。 |
| `CM_AUTO_LOCK_MINUTES` | *未设置* | 在此分钟数不活动后自动锁定。未设置 / `0` 则禁用。 |
| `CM_LOG_FORMAT` | `json` | `json` 或 `text`。 |
## 备份
停止容器并复制数据目录:
```
docker stop homepki
cp -r /var/lib/docker/volumes/homepki-data/_data ~/homepki-backup
docker start homepki
```
或者,在运行期间使用 SQLite 的在线备份:
```
docker exec -it homepki \
sh -c 'sqlite3 /data/homepki.db ".backup /data/backup.db"'
docker cp homepki:/data/backup.db ~/homepki-backup.db
docker exec -it homepki rm /data/backup.db
```
备份是一个常规的 SQLite 文件;恢复方法是停止容器,
替换该文件,然后再次启动它。
## 将其置于反向代理之后
容器在 `:8080` 上使用纯 HTTP。大多数操作者会希望在前面有一个
终止 TLS 的反向代理 —— 并且它提供的证书当然
可以是由 homepki 自己签发的。nginx、Caddy、
Traefik 或 haproxy 中的任何一个都可以正常工作;homepki 本身并不在乎。
如果你的反向代理位于不同的主机上,请将 `CRL_BASE_URL` 指向
客户端将看到的 **公共** URL,而不是 `http://localhost:8080`。
## 局限性 —— homepki 不是什么
- **不是 ACME 服务器。** 它不会响应
`acme.sh` 或 `certbot` 来签发证书;操作者(你)需要点击按钮。
- **不是多租户的。** 一个操作者、一个密码短语、一个 CA 层次结构。
- **v1 中没有 JSON / REST API。** 浏览器和 `curl` 使用相同的 HTML
endpoint。证书 / 密钥 / CRL 下载是直接的 PEM/DER
响应,因此编写自动续期脚本很简单 —— 只是不是通过
单独的 API 表面。
## 对于开发者
如果你想阅读设计或做出贡献,规范位于
[docs/](docs/):
| 文档 | 阅读目的 |
| --- | --- |
| [SPEC.md](docs/SPEC.md) | 产品表面、技术栈、部署形态、环境变量。从这里开始。 |
| [LIFECYCLE.md](docs/LIFECYCLE.md) | 锁定/解锁、KEK→DEK 密钥加密设计、轮换、吊销、CRL 重新生成。 |
| [STORAGE.md](docs/STORAGE.md) | SQLite schema、迁移、事务、备份。 |
| [API.md](docs/API.md) | 路由、请求/响应形态、状态码、幂等性。 |
| [COLD_ROOTS.md](docs/COLD_ROOTS.md) | 在可移动数据库中冷存储根密钥的 v2 设计。 |
包含一个 devcontainer;在 VS Code 中使用 Dev
Containers 扩展打开代码库即可获得一个可用的构建。`make help` 列出了
常见的开发者目标。
## 许可证
[MIT](LICENSE)。
标签:CA, Docker, EVTX分析, TCP SYN 扫描, TLS证书, Web UI, 安全防御评估, 日志审计, 自托管, 请求拦截, 运维工具