让每个工具都信任私有 CA
目标
创建私有根 CA 和服务器证书,用内部名称启动 HTTPS,并分别放入操作系统、Python 和 Node 的信任库。亲自测量哪个工具看哪个信任库,并整理成表,还要让服务器把中间 CA 证书链也一并发送。
为什么重要
内部镜像一旦使用 HTTPS,所有构建工具就都必须信任那个 CA。但信任库分为操作系统、certifi、Node 内置列表和 JVM 几种,只放进一个地方,就只有部分工具可用。在不能用的工具上把验证关掉,是最常见的事故。这个实验 Pod 连不上外部(只有 DNS),因为私有 CA 是隔离网络内部的事情。
步骤
- 用
/root/pki/ca.key和/root/pki/ca.crt创建 CN 为Airgap Internal Root CA的根 CA(CA:TRUE,keyCertSign)。 - 用
/root/pki/server.key和/root/pki/server.crt,由该 CA 签发repo.airgap.internal服务器证书(SAN 中为 DNS 名称,用途为 serverAuth,不是 CA)。签名时给出的扩展保存为/root/pki/server.ext。 - 在
/etc/hosts中加入127.0.0.1 repo.airgap.internal,并用/root/pki/serve.py启动一个服务器,通过https://repo.airgap.internal:8443/提供/root/pki/www/hello.txt(内容为hello-airgap)。 - 把根 CA 以
airgap-root.crt的名称放入操作系统信任库并更新。curl必须在不带--cacert的情况下通过。 - 在
/etc/profile.d/airgap-ca.sh中写入让 Python requests 和 httpx 使用操作系统证书包的环境变量。 - 在同一个文件中加入让 Node 信任私有根证书的环境变量。
- 不用环境变量(仅靠操作系统信任库),测量每个工具是否通过,把
curl、urllib、requests、httpx、node、go六行,以도구=os或도구=env(占位符为工具名称)的形式写入/root/pki/trust-matrix.txt。 - 用根证书签发中间 CA(
/root/pki/int.crt,pathlen 0),再用该中间 CA 签发mirror.airgap.internal证书(/root/pki/mirror.crt)。在/etc/hosts中加入该名称,并用把叶证书和中间证书连在一起的/root/pki/mirror-chain.crt,在 9443 端口启动同样的服务器。
参考
- 查看证书:
openssl x509 -in <파일> -noout -text(占位符为文件名)、确认名称:openssl x509 -in <파일> -noout -checkhost <이름>(占位符依次为文件名和主机名称) - 服务器发送的证书链:
openssl s_client -connect 127.0.0.1:8443 -servername <이름> -showcerts </dev/null(占位符为主机名称) - 用登录 shell 确认 profile.d 已生效:
env -i bash -lc 'echo $REQUESTS_CA_BUNDLE' - 常见错误 1:只在 CN 中写名称。如今的工具只看 SAN。
- 常见错误 2:以
.pem扩展名放入。update-ca-certificates 只读取.crt。 - 常见错误 3:在不能用的工具上把验证关掉(
verify=False、NODE_TLS_REJECT_UNAUTHORIZED=0)。本实验的评分器只在开启验证的情况下测量。
创建私有根 CA
用 /root/pki/ca.key 和 /root/pki/ca.crt 创建 CN 为 Airgap Internal Root CA 的根 CA(CA:TRUE,keyCertSign)。
给 openssl req 加上 -x509,就会直接生成自签名证书,而不是请求。扩展可以通过 -addext 给出,根 CA 需要的是基本约束(CA)和密钥用途(证书签名)。
签发带有 SAN 的服务器证书
用 /root/pki/server.key 和 /root/pki/server.crt,由根 CA 签发 repo.airgap.internal 服务器证书(SAN 为 DNS,serverAuth,不是 CA)。签名时给出的扩展保存为 /root/pki/server.ext。
生成密钥和签名请求(CSR)之后,用 CA 密钥签名时,通过扩展文件给出 subjectAltName 和 extendedKeyUsage。请注意,openssl x509 -req 默认不会把请求中的扩展带过去。
用内部名称启动 HTTPS
在 /etc/hosts 中加入 127.0.0.1 repo.airgap.internal,并用 /root/pki/serve.py 启动一个服务器,通过 https://repo.airgap.internal:8443/ 提供 /root/pki/www/hello.txt(hello-airgap)。
用 ssl 上下文包装 Python 标准库的 http.server,就成了 HTTPS 服务器。把证书文件和密钥文件交给 load_cert_chain。请放到后台启动,并用通过 --cacert 指定了根证书的 curl 来确认。
放入操作系统信任库
把根 CA 以 airgap-root.crt 放入操作系统信任库并更新。curl 必须在不带 --cacert 的情况下通过。
Debian 系列把文件以 .crt 扩展名放在指定目录中,再运行更新命令,就会重新生成连接成一个文件的证书包。更新命令的输出中会显示新增了几个。
让 Python requests 和 httpx 信任它
在 /etc/profile.d/airgap-ca.sh 中写入让 requests 和 httpx 使用操作系统证书包的环境变量。
requests 和 httpx 使用的不是操作系统信任库,而是 certifi 证书包。这两个库遵循的环境变量各不相同——请看阅读材料中的表。值是操作系统生成的证书包文件。
让 Node 信任它
在 /etc/profile.d/airgap-ca.sh 中加入让 Node 信任私有根证书的环境变量。
Node 使用构建时内置的列表,并且有一个环境变量可以向该列表中添加证书。它只在进程启动时读取一次,所以请在新的 shell 中确认。
测量各工具的信任情况表
不用环境变量,测量每个工具是否通过,把 curl、urllib、requests、httpx、node、go 以 도구=os 或 도구=env(占位符为工具名称)的形式写入 /root/pki/trust-matrix.txt。
用 env -i 清空环境,profile.d 中的变量也就没有了。在这种状态下通过,就说明仅靠操作系统信任库即可(os);失败,就说明需要环境变量(env)。Go 请写一个小程序,用 go run 运行。
中间 CA 与发送证书链
创建中间 CA /root/pki/int.crt(pathlen 0)和由该 CA 签发的 mirror.airgap.internal 证书 /root/pki/mirror.crt,并用把叶证书和中间证书连在一起的 /root/pki/mirror-chain.crt,在 9443 端口启动服务器。
中间 CA 也是 CA,所以需要基本约束,并用 pathlen 禁止在它之下再设置 CA。服务器的证书文件中,要在叶证书之后接上中间 CA 证书。客户端只知道根。