在内网环境里推进 Harbor、Gitea、Argo CD、Ingress 这一类平台建设时,证书问题往往会很快暴露出来。浏览器访问需要 HTTPS,镜像仓库需要可信证书,Kubernetes 里的服务发布也离不开统一的 TLS 体系。如果前期没有把证书基础设施搭起来,后面每接一个系统,都会重复处理一次信任链和证书签发的问题,很麻烦,排错也容易失控。
我这一次基于 Smallstep step-ca 的内网 PKI 搭建过程。目标很明确:在企业内网中部署一套私有 CA,用它来签发内部业务系统证书,并让相关 Linux 主机能够建立对这套证书链的信任。后续无论是 Harbor、Gitea,还是 Ingress 与 cert-manager,对接时都会顺畅很多。
一、为什么需要内网 PKI
PKI 的核心不是仅仅只做一张证书,而是建立一套完整的信任关系。一个业务系统之所以能够安全地使用 HTTPS,并不取决于它单独持有一份证书,而取决于客户端是否信任给它签发证书的机构。
在一个规范的 PKI 体系里,通常会有根证书、中间证书和业务证书三个层次。根证书是整条信任链的起点,中间证书负责在线签发,业务证书则分发给 Harbor、Gitea、Ingress 这类具体系统。客户端访问内部业务域名时,会沿着这条链一路校验上去,只要根证书已经进入本机信任库,TLS 握手就可以正常完成。
生产环境一般不会让根证书直接给业务系统签发证书,而是采用根 CA + 中间 CA的结构。根密钥应尽量少用甚至离线保存,真正在线运行的是中间 CA。这样做的好处是边界更清晰,也更符合后续扩展和轮换的要求。
二、为什么选择 step-ca
Smallstep 的 step-ca 很适合内网 PKI 的起步阶段。它部署轻量,配置不复杂,支持本地自管模式。对于一套正在逐步完善的内网平台来说,它的优势并不在于功能堆得多,而在于结构清晰,落地速度快,而且后续能够自然衔接 Kubernetes 里的 cert-manager。
三、环境规划
本次实践中的 CA 服务器运行在 Ubuntu 24.04 LTS 上,主机名为 docker-srv,IP 地址是172.16.11.63。为这台 CA 服务器规划的业务访问域名是 ca.yxwa.info。我的后续计划由这套 CA 为 harbor.yxwa.info、gitea.yxwa.info、argocd.yxwa.info 等内部业务域名签发证书。

step-ca 的工作目录统一放在 /opt/step 下面。这样做的原因很简单:证书、私钥、配置文件和数据库目录都集中在一个位置,后续排查和备份都更方便。

四、安装前的一个典型问题:系统自带 step 冲突
在 Ubuntu 环境里,名为 step 的命令未必就是 Smallstep 的 step-cli。实际操作中,第一次执行 step version 时,终端返回的是 Qt 插件相关报错,而不是版本信息。这说明系统命中了另一个同名程序。

这类问题如果不先排掉,后面所有初始化命令都会变形,因此第一步不是安装,而是先确认当前被调用的 step 究竟是谁。排查时使用了 which step、type step 和 step --help,结果显示系统中的 /usr/bin/step 来自 Ubuntu 自带的软件包,而不是 Smallstep 的工具。
确认之后,先把冲突程序移除:
sudo apt remove -y step
hash -r
这一步完成后,再次执行 step version,命令不存在反而是正确结果,说明环境已经清理干净,可以开始安装真正需要的 step-cli 和 step-ca 了。
五、安装 step-cli 与 step-ca
在 Ubuntu 上,使用官方 APT 源安装是最稳妥的做法。安装前先补齐 curl、gpg 和 ca-certificates,然后写入 Smallstep 官方仓库配置,最后安装 step-cli 和 step-ca:
sudo apt-get update && sudo apt-get install -y --no-install-recommends curl gpg ca-certificates
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://packages.smallstep.com/keys/apt/repo-signing-key.gpg | sudo tee /etc/apt/keyrings/smallstep.asc >/dev/null
cat << 'EOF' | sudo tee /etc/apt/sources.list.d/smallstep.sources >/dev/null
Types: deb
URIs: https://packages.smallstep.com/stable/debian
Suites: debs
Components: main
Signed-By: /etc/apt/keyrings/smallstep.asc
EOF
sudo apt-get update
sudo apt-get -y install step-cli step-ca
hash -r
安装完成后,用下面两条命令检查版本:
step version
step-ca version
如果输出中已经出现 Smallstep CLI 和 Smallstep CA 的版本号,就说明环境已经准备好。

六、初始化 CA
正式初始化之前,先创建工作目录,并把目录属主改成当前操作用户。这里之所以不继续保留 root 属主,是因为 step-ca 初始化时会在工作目录中生成配置和上下文文件,如果当前用户没有写权限,会直接报 permission denied。

目录准备命令如下:
sudo mkdir -p /opt/step
sudo chmod 700 /opt/step
sudo chown -R yxwa:yxwa /opt/step
export STEPPATH=/opt/step
接下来执行初始化:
step ca init
整个初始化过程采用交互式方式完成,部署类型选择 Standalone,PKI 名称填写为 yxwa Internal PKI,CA 对外访问地址填写为 ca.yxwa.info,监听地址最初填的是 :443(其实该填一千往后,443这里需要root)
首个 provisioner 名称设为 admin。
密码则由操作者自己设定,用于保护 CA 密钥和 provisioner。

初始化成功后,系统会生成根证书、中间证书、私钥和核心配置。
关键文件分别位于 /opt/step/certs、/opt/step/secrets 和 /opt/step/config 目录下。为了确认初始化结果,可以执行以下检查:
find /opt/step -maxdepth 2 -type f | sort
grep -n '"address"' /opt/step/config/ca.json
step path
看到 root_ca.crt、intermediate_ca.crt 和 ca.json 都已经生成,说明 PKI 初始化已经完成。

七、首次启动与 443 端口问题
初始化结束后,下一步是先手工启动 step-ca,验证服务本身是否能跑起来。由于 step-ca 启动时需要解密密钥,因此先把初始化阶段设置的密码写入单独的密码文件中,便于服务读取:
mkdir -p /opt/step/secrets
nano /opt/step/secrets/password.txt
chmod 600 /opt/step/secrets/password.txt
接着以前台方式启动服务:
export STEPPATH=/opt/step
step-ca /opt/step/config/ca.json --password-file /opt/step/secrets/password.txt
第一次启动时,日志里虽然显示服务开始运行,但外部检查却始终无法连接 443。进一步排查发现,问题并不在 step-ca 本身,而在监听端口上。Linux 中 1024 以下属于特权端口,普通用户默认无法绑定 443。由于当前使用的是普通用户启动 step-ca,所以进程虽然在,端口却并未真正监听成功。

为了先把链路跑通,这里没有一开始就切换成 root,也没有先做额外的端口授权,而是直接把监听地址调整为 8443。修改方法如下:
sed -i 's/"address": ":443"/"address": ":8443"/' /opt/step/config/ca.json
grep -n '"address"' /opt/step/config/ca.json
修改后重新启动 step-ca,再用 ss 和 curl 检查:
ss -lnt | grep ':8443'
curl -vk https://ca.yxwa.info:8443/health
当 /health 接口返回状态正常时,就说明 step-ca 已经真正开始对外提供 HTTPS 服务。

八、域名解析与信任建立
服务启动之后,遇到的下一个问题通常不是证书,而是解析。最开始执行健康检查时,系统提示找不到 ca.yxwa.info。说明这台机器本身还没有完成该域名的 DNS 解析。

在正式接入企业 DNS 之前,临时解决方式是将域名写入 /etc/hosts:
sudo sh -c 'echo "172.16.11.63 ca.yxwa.info" >> /etc/hosts'
getent hosts ca.yxwa.info
ping -c 1 ca.yxwa.info
这样做的目的不是长期依赖 hosts,而是先验证 CA 服务链路本身是否工作正常。等验证通过后,再回到企业 DNS 里补正式记录。
解析打通以后,还需要解决另一件更关键的事:让当前机器信任这套根证书。只有根证书进入本机信任库,客户端才会把 step-ca 返回的证书链视为可信。

在 CA 服务器本机安装根证书的方式如下:
step certificate install /opt/step/certs/root_ca.crt
如果当前环境需要提升权限,也可以显式用 sudo 执行。安装完成后,再次执行 curl 检查 health 接口。如果这时不再出现证书校验失败的报错,并且可以正常返回 ok,就说明当前主机已经建立了对内部根证书的信任。
九、签发第一张业务证书
到这里为止,CA 服务已经能运行,根证书也已经被本机信任。下一步就不是继续检查 CA 本身,而是验证它是否真的能够给业务域名签出一张可用证书。
这里选择 harbor.yxwa.info 作为第一张测试证书的签发对象。首先准备一个测试目录,然后执行签发命令:
mkdir -p ~/pki-test
cd ~/pki-test
step ca certificate harbor.yxwa.info harbor.crt harbor.key \
--ca-url https://ca.yxwa.info:8443 \
--root /opt/step/certs/root_ca.crt
执行过程中需要输入 provisioner 密码。签发成功后,会生成一张证书和一把私钥,分别是 harbor.crt 和 harbor.key。随后可以用 inspect 命令检查签发结果:
step certificate inspect ~/pki-test/harbor.crt --short
检查重点在于三项:主题名是否为 harbor.yxwa.info,签发者是否为内部中间 CA,以及有效期是否正常。只要这三项没有问题,就说明 CA 已经具备为业务系统签发服务器证书的能力。

十、用 systemd 托管 step-ca
手工前台启动只适合验证,不适合长期运行。要让 step-ca 成为稳定可维护的基础服务,就需要交给 systemd 托管。
服务文件内容如下:
sudo tee /etc/systemd/system/step-ca.service >/dev/null <<'EOF'
[Unit]
Description=step-ca
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=yxwa
Group=yxwa
Environment=STEPPATH=/opt/step
ExecStart=/usr/bin/step-ca /opt/step/config/ca.json --password-file /opt/step/secrets/password.txt
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
写入完成后,先通过 systemctl cat 检查文件内容,再执行 daemon-reload、enable 和 start:
sudo systemctl daemon-reload
sudo systemctl enable --now step-ca
sudo systemctl status step-ca --no-pager
如果状态显示为 active (running),并且日志里能看到服务正在 8443 端口提供 HTTPS,就说明 systemd 已经成功接管。
为了确认不是“看起来在运行”,还应再次检查端口和 health 接口:

ss -lnt | grep ':8443'
curl -v https://ca.yxwa.info:8443/health
当这一组检查通过后,CA 服务本体就可以认为已经完成搭建。

十一、导出并分发根证书
CA 不只是给自己用的。只要后面 节点要访问由内部 CA 签发的证书,根证书就必须被这些机器信任。因此,在 CA 服务器上还需要把根证书单独整理出来,作为后续分发文件。
可以先检查根证书,再将其复制到当前用户目录:
ls -l /opt/step/certs/root_ca.crt
step certificate inspect /opt/step/certs/root_ca.crt --short
cp /opt/step/certs/root_ca.crt ~/root_ca.crt
ls -l ~/root_ca.crt
这样就得到了一个标准分发文件 root_ca.crt,后续可以通过 scp 复制到其他 Linux 主机。
十二、在 Jumpserver 上验证信任链
为了证明这套 PKI 不只是“本机可用”,还必须让另一台机器也能建立信任。

这里选用 Jumpserver 所在的 Ubuntu 24.04 主机作为验证目标。
首先把根证书复制过去:

scp ~/root_ca.crt <user>@<jumpserver-ip>:/tmp/root_ca.crt
复制完成后,在 Jumpserver 上检查证书内容:

ls -l /tmp/root_ca.crt
openssl x509 -in /tmp/root_ca.crt -noout -subject -issuer
确认无误后,把根证书安装进系统信任库:

sudo cp /tmp/root_ca.crt /usr/local/share/ca-certificates/yxwa-root-ca.crt
sudo chmod 644 /usr/local/share/ca-certificates/yxwa-root-ca.crt
sudo update-ca-certificates
此时如果直接访问 https://ca.yxwa.info:8443/health 仍然失败,多半不是证书问题,而是 Jumpserver 还没有完成该域名解析。测试阶段同样可以先通过 /etc/hosts 临时补上:

echo '172.16.11.63 ca.yxwa.info' | sudo tee -a /etc/hosts
getent hosts ca.yxwa.info
随后再次使用 curl 验证:
curl -v https://ca.yxwa.info:8443/health
只要最终返回 ok,并且不再出现证书不可信的报错,就说明 Jumpserver 已经成功信任这套内部 CA。这一步非常关键,因为它证明了内网 PKI 的信任关系已经可以跨主机成立,而不再局限于 CA 服务器本机。

至此,基于 step-ca 搭建私有 CA我已经完成,在内网环境里,证书体系从来不是一个单独的问题。它会贯穿镜像仓库、代码托管、容器平台、服务网关和自动化发布的全过程。
Comments NOTHING