- はじめに — 閉域網で署名検証がより重要になる理由
- 何を媒体に入れるのか
- 署名を検証します
- 再現可能なバンドル — マニフェストを作ります
- 持ち込み後に突き合わせます
- 持ち込み記録に何を残すのか
- おわりに — 媒体が自己完結している必要があります
- 自分で試す
- 前回 / 次回
- 参考資料
はじめに — 閉域網で署名検証がより重要になる理由
接続網では、何かおかしければもう一度ダウンロードしてみればよいだけです。ハッシュが合わなければ原本と突き合わせ、署名がおかしければ配布元に確認します。比較対象がいつでも手の届くところにあります。
閉域網ではその前提が崩れます。媒体が内側に入った瞬間、原本と突き合わせる手段が消えます。6 か月後に「この rpm はどこから来たのですか」と聞かれたとき、答える根拠が同じ媒体の中に一緒に入っていなければ、永遠に答えられません。
だからこそ閉域網では、完全性の検証はセキュリティ要件である前に 運用要件 です。今回はその根拠をバンドルの中に一緒に入れる方法を扱います。
何を媒体に入れるのか
持ち込み単位に入れるべきものは rpm ファイルだけではありません。最低でもこの 6 つです。
- rpm ファイル一式 — 第 2 回で作ったもの
- repodata — 第 3 回で生成したリポジトリメタデータ
- GPG 公開鍵 — 内側で署名を検証するために必要です
- マニフェスト — 何が入っているのか、いつ時点のものなのか
- チェックサムファイル — マニフェストとは別に、媒体の完全性を確認するため
- インストール手順書 — どの順序で何を実行するのか
3 番を落とすと、内側では gpgcheck=1 が最初のインストールをブロックします。4 番を落とすと再現が不可能になります。実際に一番よく抜け落ちるのがこの 2 つです。
署名を検証します
rpm の署名検査は、パッケージのダイジェストと署名をあわせて確認します。最新の上流 man ページはこの機能を rpmkeys に置き、rpm 側の -K、--checksig、--import を "Obsolete compatibility aliases" に分類しています。RHEL 8・9・10 ではどちらの表記も動作しますが、新しく手順書を書くなら rpmkeys 側を使うほうがよいです。
# 鍵を先に登録してはじめて署名検証が意味を持ちます
sudo rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
# バンドル全体の署名とダイジェストを検査する
rpmkeys --checksig /var/tmp/airgap-bundle/rpms/*.rpm
# 失敗したものだけを抜き出して見る
rpmkeys --checksig /var/tmp/airgap-bundle/rpms/*.rpm | grep -v 'digests signatures OK'
rpmkeys の man ページは -K、--checksig を "Verify the digests and signatures contained in PACKAGE_FILE to ensure the integrity and origin of the package" と定義しています。後半の節が核心です。ダイジェストは完全性を、署名は出所を保証します。両者は別々の問いに答えるものであり、どちらも必要です。
鍵を登録していない状態で検査すると、ダイジェストは通過し、署名は確認不能として出ます。この状態を「通過」と読んでしまう間違いがよくあるので、検査の前に rpmkeys --list で鍵が登録されているかを先に見るべきです。
取得の段階であらかじめふるい落とすこともできます。第 2 回で見た dnf reposync の -g、--gpgcheck は "Remove packages that fail GPG signature checking after downloading. Exit code is 1 if at least one package was removed" なので、このオプションを使えば署名の壊れたパッケージはそもそもバンドルに入らず、終了コードで知らせてくれます。
再現可能なバンドル — マニフェストを作ります
ここが今回の核心です。
バンドルが再現可能であるとは、同じマニフェストを手にして 6 か月後に同じインストール結果を得られる、という意味です。そのためにはマニフェストに最低でも 5 つが必要です。
- 完全な NEVRA — 名前、epoch、バージョン、リリース、アーキテクチャ
- パッケージごとのチェックサム — SHA-256
- リポジトリのスナップショット日付 — いつ取得したのか
- 使用したリリースバージョン —
--releaseverに何を渡したのか - 取得元リポジトリ ID — どのリポジトリから来たのか
epoch をあえて入れる理由があります。RPM のタグ文書は Epoch を "Package epoch (optional). An absent epoch is equal to epoch value 0" と定義していますが、ファイル名に epoch は入りません。そのためファイル名だけで作った一覧では、epoch の異なる 2 つのパッケージを区別できません。バージョン比較では epoch が最優先されるので、この差は静かで致命的です。
マニフェストは rpm のクエリで一度に取り出します。
#!/usr/bin/env bash
# make-manifest.sh — バンドルディレクトリから再現可能なマニフェストを生成する
set -euo pipefail
BUNDLE_DIR="${1:?usage: make-manifest.sh <bundle-dir>}"
RELEASEVER="${2:?usage: make-manifest.sh <bundle-dir> <releasever>}"
SNAPSHOT_DATE="$(date -u +%Y-%m-%d)"
OUT="${BUNDLE_DIR}/MANIFEST.tsv"
{
echo "# bundle_id: rhel-airgap-${SNAPSHOT_DATE}"
echo "# snapshot_date: ${SNAPSHOT_DATE}"
echo "# releasever: ${RELEASEVER}"
echo "# source_repos: rhel-9-for-x86_64-baseos-rpms,rhel-9-for-x86_64-appstream-rpms"
echo "# generated_by: $(rpm --version)"
printf 'NAME\tEPOCH\tVERSION\tRELEASE\tARCH\tSHA256\tFILE\n'
} > "${OUT}"
for f in "${BUNDLE_DIR}"/rpms/*.rpm; do
# epoch を持たないパッケージには三項演算子で 0 を埋める
nevra="$(rpm -qp --queryformat \
'%{NAME}\t%|EPOCH?{%{EPOCH}}:{0}|\t%{VERSION}\t%{RELEASE}\t%{ARCH}' \
"$f" 2>/dev/null)"
sum="$(sha256sum "$f" | cut -d' ' -f1)"
printf '%s\t%s\t%s\n' "${nevra}" "${sum}" "$(basename "$f")"
done >> "${OUT}"
echo "wrote ${OUT} ($(grep -cv '^#' "${OUT}") lines)"
-p、--package は rpm の man ページで "Query an (uninstalled) package PACKAGE_FILE"、--queryformat は "Output format of each queried package, as described by rpm-queryformat(7)" と定義されています。三項表記は rpm のクエリ形式の文書に出てくる文法で、タグが存在しないときの値を指定します。epoch が空のパッケージに 0 を埋めるのにそのまま使えます。
出来上がるファイルはこういう形です。
# bundle_id: rhel-airgap-2026-08-15
# snapshot_date: 2026-08-15
# releasever: 9.4
# source_repos: rhel-9-for-x86_64-baseos-rpms,rhel-9-for-x86_64-appstream-rpms
# generated_by: RPM version 4.16.1.3
NAME EPOCH VERSION RELEASE ARCH SHA256 FILE
httpd 0 2.4.57 11.el9_4 x86_64 3f1c... httpd-2.4.57-11.el9_4.x86_64.rpm
mod_ssl 1 2.4.57 11.el9_4 x86_64 9ab2... mod_ssl-2.4.57-11.el9_4.x86_64.rpm
mod_ssl の行の epoch が 1 になっているのを見てください。その数字はファイル名のどこにもありません。
持ち込み後に突き合わせます
マニフェストは作っておけば終わり、ではありません。内側に到着した直後に突き合わせる必要があります。
#!/usr/bin/env bash
# verify-bundle.sh — マニフェストと実際のバンドルを突き合わせる
set -euo pipefail
BUNDLE_DIR="${1:?usage: verify-bundle.sh <bundle-dir>}"
MANIFEST="${BUNDLE_DIR}/MANIFEST.tsv"
ERR=0
echo "== バンドルメタデータ"
grep '^#' "${MANIFEST}"
echo "== チェックサム照合"
while IFS=$'\t' read -r name epoch version release arch sha file; do
path="${BUNDLE_DIR}/rpms/${file}"
if [ ! -f "${path}" ]; then
echo "MISSING ${file}"; ERR=$((ERR+1)); continue
fi
actual="$(sha256sum "${path}" | cut -d' ' -f1)"
if [ "${actual}" != "${sha}" ]; then
echo "MISMATCH ${file}"; ERR=$((ERR+1))
fi
done < <(grep -v '^#' "${MANIFEST}" | tail -n +2)
echo "== マニフェストにないファイル"
comm -13 \
<(grep -v '^#' "${MANIFEST}" | tail -n +2 | cut -f7 | sort) \
<(cd "${BUNDLE_DIR}/rpms" && ls -1 *.rpm | sort)
echo "== 署名検証"
rpmkeys --checksig "${BUNDLE_DIR}"/rpms/*.rpm \
| grep -v 'digests signatures OK' || true
echo "== 結果: エラー ${ERR} 件"
exit "${ERR}"
「マニフェストにないファイル」の検査を入れた理由があります。欠落はインストール失敗としてすぐに表面化しますが、一覧に載っていないのに媒体に入っているファイル は誰にも気づかれずに通り過ぎます。持ち込み審査の観点では、こちらのほうが大きな問題です。
チェックサムファイルを媒体に一緒に入れることについても一言必要です。同じ媒体にしか入っていないチェックサムは、媒体が丸ごと差し替えられれば一緒に変わります。実質的な保証は 署名 から来ます。チェックサムは転送中の破損を検出するためのもので、出所の保証は GPG 署名が担います。だから先ほどのスクリプトは両方を確認しています。
持ち込み記録に何を残すのか
監査対応と再現は、同じ記録で解決できます。バンドル 1 つあたりこれくらい残しておけば十分です。
| 項目 | 例 | なぜ必要か |
|---|---|---|
| バンドル ID | rhel-airgap-2026-08-15 | サーバーとバンドルを結びつける鍵 |
| スナップショット日付 | 2026-08-15 | どの時点のコンテンツなのか |
| releasever | 9.4 | マイナーバージョンの固定値 (第 5 回) |
| 取得元リポジトリ ID | rhel-9-for-x86_64-baseos-rpms | 作り直すときの入力 |
| 生成コマンド | dnf download --installroot=... --releasever=9.4 ... | 再現の核心 |
| 媒体ハッシュ | 媒体イメージの SHA-256 | 媒体単位の完全性 |
| 持ち込み日時・担当者 | — | 監査要件 |
| インストール対象サーバー | — | 影響範囲の追跡 |
「生成コマンド」を丸ごと残せ、という項目が重要です。6 か月後に同じバンドルを作り直すとき、オプションが 1 つ違えば結果も変わります。人の記憶ではなくファイルに残してください。この情報をバンドル内の PROVENANCE.txt のようなファイルとして一緒に入れておけば、媒体だけで自己完結します。
サブスクリプションなしに Red Hat コンテンツを再配布することは契約違反になりうるので、組織のライセンス条件を先に確認してください。持ち込み記録に取得元リポジトリ ID を残しておけば、あとでライセンスの範囲を確認するときにもそのまま根拠になります。
おわりに — 媒体が自己完結している必要があります
今回の原則は 1 つです。媒体 1 つを手にしただけで、何が入っているのか、どこから来たのか、どうやって作り直すのかが分かる状態にしておく。これに尽きます。
rpm ファイルしか入っていない USB は、6 か月後には何も語ってくれません。ここにマニフェストと公開鍵と生成コマンドを一緒に入れれば、その媒体は説明書になります。追加コストはテキストファイル 2 つだけです。
コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって異なるので、使用中のバージョンのドキュメントで再確認してください。
自分で試す
- ハッシュ生成器 — SHA-256 チェックサムを自分で作ってマニフェストの感覚をつかむ
- Linux ターミナル — 検証スクリプトのパイプラインを組み立ててみる
- Linux コマンドクイズ — rpm のクエリオプションの復習
前回 / 次回
参考資料
현재 단락 (1/109)
接続網では、何かおかしければもう一度ダウンロードしてみればよいだけです。ハッシュが合わなければ原本と突き合わせ、署名がおかしければ配布元に確認します。**比較対象がいつでも手の届くところにあります**...