Maven 社内ミラーを HTTPS で、JVM に信頼させる
目標
接続された側で、依存関係とプラグインを集めて、社内Mavenリポジトリを、HTTPSで立て、settings.xmlのmirrorで、すべてのリクエストを回します。JVMにプライベートCAを信頼させて、エアギャップ環境側の新しいローカルリポジトリでビルドし、持ち込み一覧から抜けたプラグインを、追加で持ち込む手続きまで終えます。
なぜ重要なのか
Javaビルドのエアギャップ環境での失敗は、3重です。リポジトリのアドレス、証明書、そして、持ち込み一覧の穴。アドレスはmirror 1行が、証明書は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が失敗する原因となった、アーティファクトの座標を、/root/mvn/missing.txtに書いて(groupId:artifactId:version)、接続された側で取得して社内リポジトリに追加したあと、エアギャップ環境側で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が1つあれば十分です。この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なら、OSのストアの更新が、JVMのストアまで更新する道もあります。
エアギャップ環境側のビルドを通す
ステップ6と同じコマンド(/root/mvn/inside-repo、package)で、ビルドを通してください。
JVMがルートを信頼するようになったあとなら、同じコマンドが通るはずです。通ったあと、エアギャップ環境側のローカルリポジトリの追跡ファイルが、どのリポジトリidを書いたかを見てください。
cleanの一言が呼んだ追加の持ち込み
mvn clean packageの失敗の原因となった座標を、/root/mvn/missing.txtにgroupId:artifactId:versionの形式で書いて、接続された側で取得して社内リポジトリに追加したあと、エアギャップ環境側でclean packageを通してください。
エラーの行が、どのプラグインが見つからなかったかを教えてくれます。外部の役割は、空の設定ファイルと、外部用のローカルリポジトリで、同じgoalを実行すれば、そのプラグインが集まります。もう一度移したあとも失敗するなら、エラー文を最後まで読んでみてください。以前の失敗が記録されています。