媒体に載せる六つのもの
一言でいうと
持ち込み媒体にrpmだけを入れてはいけません。rpm、repodata、GPG鍵、マニフェスト、チェックサム、インストール手順書の6つがすべて必要です。実際に最もよく抜けるのが、GPG鍵とマニフェストです。
なぜ必要なのか
ネットワークにつながった環境で成功したダウンロードコマンドは、インストール対象のバージョン・アーキテクチャ・インストール済みの状態が違うと、エアギャップ環境で同じ結果が保証されません。抜け落ちた依存関係は再持ち込みを生み、出どころを検証する鍵や承認リストがないと、ファイルがあってもインストールの許可を証明できません。そのため、バンドルはパッケージの集まりではなく、再現条件と検証の根拠まで含めた配布単位でなければなりません。
どう動くのか
外で取得する
2つの方向があります。
解決済みの結果を運ぶ
dnf download --resolve --alldeps --destdir=/srv/bundle/rpms httpd mod_ssl
--resolve: 「指定したパッケージの依存関係を解決し、システムにない依存関係をダウンロードします」--alldeps: 「--resolveと一緒に使うと、すでにインストール済みのものも飛ばさずにすべて取得します」
--resolveだけを使うと、この機器にないものだけを取得します。エアギャップ環境のバンドルを作る目的なら、これはほぼいつも誤った結果です。
ところが、--alldepsだけでも足りません。依存関係の解決そのものが、依然としてこのシステムの状態を前提に行われるからです。ネットワークにつながった機器が9.4で、対象が9.2なら、結果が変わることがあります。定石は、空のルートを使うことです。
mkdir -p /var/tmp/airgap-root
dnf download --installroot=/var/tmp/airgap-root --releasever=9.4 \
--setopt=reposdir=/etc/yum.repos.d --resolve --alldeps \
--destdir=/srv/bundle/rpms httpd mod_ssl
--releaseverを必ず一緒に指定する必要があります。ドキュメントの警告: 「installrootを作るときに--releaseverを使わないと、$releaseverの値がinstallroot内のrpmdbから来ますが、生成時点では空なので、トランザクションが失敗します。」
リポジトリを丸ごと運ぶ
dnf reposync --repoid=rhel-9-for-x86_64-baseos-rpms \
--download-path=/srv/sync --download-metadata --gpgcheck \
--newest-only --arch=x86_64 --arch=noarch --remote-time
主なオプション: --download-metadata(メタデータも一緒に)、--gpgcheck(署名に失敗したパッケージを削除し、1つでも削除されると終了コード1)、--newest-only(リポジトリごとに最新だけ)、--delete(リモートにないものを削除)、--norepopath(パスにrepo名を付けない)。
落とし穴: --newest-onlyと--download-metadataを一緒に使うと、最新のパッケージだけを取得しますが、メタデータには以前のパッケージの情報が残ります。内側で特定の古いバージョンを要求すると、メタデータにはあるのにファイルがない状態になります。
マニフェスト
マニフェストに入れるべき5つの要素: 完全なNEVRA(Name、Epoch、Version、Release、Architecture)、パッケージごとのSHA-256、スナップショットの日付、使用したreleasever、出どころのリポジトリID。
rpm -qp --queryformat '%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}' "$f"
sha256sum "$f" | cut -d' ' -f1
epochの三項表記が核心です。mod_sslのようなパッケージは、epochが1なのに、ファイル名のどこにもその数字がありません。
内側で検証
rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
rpmkeys --list
rpmkeys --checksig /srv/bundle/rpms/*.rpm | grep -v 'digests signatures OK'
最後の行が何も出力しなければ、すべて正常です。そして、マニフェストにないのに媒体にあるファイルも確認します。
comm -13 <(cut -f7 MANIFEST.tsv | sort) <(cd rpms && ls -1 *.rpm | sort)
欠落はインストールの失敗ですぐに表面化しますが、追加されたファイルは誰にも気づかれずに通り過ぎます。持ち込みの審査の観点では、こちらのほうがより大きな問題です。
内側に立てる
createrepo_c /srv/repo/labhub
cat > /etc/yum.repos.d/airgap.repo <<'EOF'
[airgap-baseos]
name=RHEL 9 BaseOS (airgap local, snapshot 2026-08-20)
baseurl=file:///srv/repo/labhub
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
metadata_expire=-1
EOF
dnf --disablerepo='*' --enablerepo=airgap-baseos install -y httpd
現場での姿
アーキテクチャを書き漏らす。x86_64のノートPCで取得してARMサーバーへ持ち込むと、エラーなしで誤ったものが入ります。--archにnoarchを一緒に指定しないと、Pythonモジュールや設定パッケージがまるごと抜け落ちます。
次のラボですること
持ち込みバンドルを作り、マニフェストで検証し、アーカイブにまとめて展開し、リポジトリを立てて、それだけでインストールします。