HTTPS 内部 Maven 镜像,并让 JVM 信任
目标
在联网的一侧收集依赖和插件,用 HTTPS 搭建内部 Maven 仓库,并通过 settings.xml 的 mirror 转发所有请求。让 JVM 信任私有 CA,用隔离网络一侧的新本地仓库构建,并完成对导入清单中遗漏的插件进行追加导入的流程。
为什么重要
Java 构建在隔离网络中的失败有三层——仓库地址、证书,以及导入清单中的漏洞。地址由一行 mirror 解决,证书由 JVM 信任库解决,漏洞则靠“按实际要执行的 goal 制作清单”的习惯来堵住。这个 Pod 能访问互联网(80/443),所以承担下载端的角色,而在隔离网络一侧,mirrorOf * 会把所有请求转到内部仓库,使外部无法使用。评分器根据本地仓库的 _remote.repositories 和内部服务器的访问记录,确认内容是从哪里下载的。
步骤
- 在
/root/mvn/app中创建pom.xml(gson 2.11.0,固定插件版本)和src/main/java/demo/App.java,并用新的本地仓库/root/mvn/outside-repo运行package。 - 把
/root/mvn/outside-repo搬到/srv/maven,但要去掉_remote.repositories、*.lastUpdated和resolver-status.properties。 - 在
/root/pki中创建 CN 为Airgap Internal Root CA的根证书(ca.crt、ca.key)和maven.airgap.internal服务器证书(maven.crt、maven.key,带 SAN),并把该名称加入/etc/hosts。 - 用
/root/mvn/nginx.conf启动 nginx,让https://maven.airgap.internal:8443/提供/srv/maven。访问记录写入/root/mvn/access.log。 - 在
~/.m2/settings.xml中写入 id 为airgap-internal、mirrorOf 为*的 mirror。同时创建扮演联网一侧时使用的空配置/root/mvn/outside-settings.xml。 - 用隔离网络一侧的新本地仓库
/root/mvn/inside-repo运行package,把失败的输出保存到/root/mvn/pkix.log。 - 让 JVM 信任私有根证书(不使用关闭验证的选项)。
- 用同样的命令让隔离网络一侧的构建通过。
- 把导致
mvn clean package失败的制品坐标,以groupId:artifactId:version的形式写入/root/mvn/missing.txt,在联网的一侧下载后加入内部仓库,再让隔离网络一侧的clean package通过。
参考
- 指定本地仓库:
-Dmaven.repo.local=<경로>(占位符为路径)、其他配置文件:-s <파일>(占位符为文件路径) - 从哪里下载的:
<로컬 저장소>/com/google/code/gson/gson/2.11.0/_remote.repositories(占位符为本地仓库路径) - nginx 配置检查与启动:
nginx -t -c /root/mvn/nginx.conf→nginx -c /root/mvn/nginx.conf - 查看 JVM 信任库:
keytool -list -cacerts -storepass changeit | head - 常见错误 1:在第 5 步之后直接执行联网一侧的命令。用户 settings.xml 中的 mirror 会把发往外部的请求也转到内部——扮演外部角色时要加
-s /root/mvn/outside-settings.xml。 - 常见错误 2:追加导入之后直接重新构建。404 已记录在本地仓库中,所以需要
-U。 - 常见错误 3:用这个镜像的默认本地仓库(
/root/.m2/repository)做第一次构建。它是已经填充过的仓库,会使导入清单出现偏差。
下载端:用新的本地仓库构建并收集
在 /root/mvn/app 中创建 pom.xml 和 App.java,并用新的本地仓库 /root/mvn/outside-repo 运行 package。
可以通过系统属性更改本地仓库的位置。给出新的目录,就只会堆放这次构建实际下载的内容。如果不在 POM 中固定插件版本,这个 Maven 默认的 compiler 就不认识 JDK 21 的设置。
去掉追踪文件,变成内部仓库
把 /root/mvn/outside-repo 搬到 /srv/maven,但要去掉追踪文件(_remote.repositories、*.lastUpdated、resolver-status.properties)。
内部仓库保持文件布局原样即可。追踪文件是下载一侧的本地仓库记录“从哪里下载”的记录,不应留在仓库中。可以用 find 按名称挑出来删除。
用私有 CA 签发镜像证书
在 /root/pki 中创建根证书(ca.crt、ca.key,CN 为 Airgap Internal Root CA)和 maven.airgap.internal 证书(maven.crt、maven.key,带 SAN),并把该名称加入 /etc/hosts。
根证书是 CA:TRUE 的自签名,服务器证书则用根证书签名,同时通过扩展文件给出 SAN。这与在私有 CA 模块中做过的流程相同。
用 nginx 启动 HTTPS 内部仓库
用 /root/mvn/nginx.conf 启动 nginx,让 https://maven.airgap.internal:8443/ 提供 /srv/maven,并把访问记录写入 /root/mvn/access.log。
Maven 仓库是静态文件,所以一个 root 就够了。在这个 Pod 中无法开放 1024 以下的端口,pid 和日志路径必须移到可写的位置。请先用 -t 检查。
用 mirrorOf * 转发所有请求
在 ~/.m2/settings.xml 中写入 id 为 airgap-internal、mirrorOf 为 *、url 为 https://maven.airgap.internal:8443/ 的 mirror,并创建空配置 /root/mvn/outside-settings.xml。
用户配置文件位于主目录的 .m2 之下。给 mirrorOf 写上星号,就会把 POM 中声明的仓库也全部截获。用于外部角色的配置,只需是没有 mirror 的最小 settings 元素。
JVM 不认识的 CA——记录 PKIX 错误
用新的本地仓库 /root/mvn/inside-repo 运行 package,把失败的输出保存到 /root/mvn/pkix.log。
让 curl 知道根证书,与让 JVM 信任根证书,是两回事。请保存失败的完整输出,并在报错行中找出关于证书路径的说明。
把根证书放进 JVM 信任库
让 JVM 信任私有根证书(不使用关闭验证的选项)。
JDK 中有处理信任库的工具,也有直接指向默认信任库(cacerts)的选项。文档写明了初始密码。如果是 Ubuntu,还有一条路:更新操作系统信任库时连带更新 JVM 信任库。
隔离网络一侧的构建通过
用与第 6 步相同的命令(/root/mvn/inside-repo,package)让构建通过。
JVM 信任根证书之后,同一条命令应该能通过。通过之后,请看隔离网络一侧本地仓库的追踪文件中写的是哪个仓库 id。
clean 这一个词引出的追加导入
把 mvn clean package 失败的原因坐标,以 groupId:artifactId:version 的形式写入 /root/mvn/missing.txt,在联网的一侧下载后加入内部仓库,再让隔离网络一侧的 clean package 通过。
报错行会告诉你找不到哪个插件。扮演外部角色时,用空配置文件和外部专用的本地仓库运行同样的 goal,就能收集到那个插件。搬过去之后如果仍然失败,请把报错的句子读完——之前的失败已被记录下来。