- Published on
閉域網の中にローカルリポジトリを作る — createrepo_c、repodata、.repo ファイルと GPG キー
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — rpm が集まったディレクトリはまだリポジトリではありません
- createrepo_c でメタデータを作る
- .repo ファイルで登録する
- GPG キーを持ち込んで登録する
- キャッシュが更新を飲み込みます
- リポジトリが実際に動作するか確認する
- おわりに — リポジトリはファイルではなく契約です
- 自分で試す
- 前回 / 次回
- 参考資料
はじめに — rpm が集まったディレクトリはまだリポジトリではありません
第 2 回で作ったのは rpm ファイルが入ったディレクトリです。その状態で閉域網へ持ち込んで dnf install をしても、依存関係は相変わらず見つかりません。
dnf は rpm ファイルを 1 つずつ開いて見るわけではないからです。dnf が読むのはリポジトリメタデータであり、それは別途生成してやる必要があります。その生成ツールが createrepo_c です。
今回はそのメタデータを作り、リポジトリとして登録し、署名を検証させ、最後に閉域網でもっとも頻繁に人を捕まえるキャッシュの問題まで整理します。
createrepo_c でメタデータを作る
もっとも基本的な形です。
# リポジトリツールのインストール (接続網の機材で先に受け取り、一緒に持ち込む必要があります)
sudo dnf install createrepo_c
# ディレクトリをリポジトリにする
sudo createrepo_c /srv/repo/rhel9-baseos
コマンドが終わると対象ディレクトリの中に repodata ができます。中を覗くと構造が見えます。
ls -1 /srv/repo/rhel9-baseos/repodata/
# repomd.xml
# <checksum>-primary.xml.gz
# <checksum>-filelists.xml.gz
# <checksum>-other.xml.gz
repomd.xml が索引で、残りが実際のデータです。primary はパッケージの名前とバージョン、依存関係情報を、filelists はパッケージが含むファイル一覧を、other は changelog を保持します。ファイル名の先頭にチェックサムが付く理由は man ページに出ています。--unique-md-filenames が "Include file's checksum in metadata filename for HTTP caching (default)" であり、これが既定値です。内容が変わればファイル名も変わるので、途中のプロキシが古い写しを返してしまう事故を防いでくれます。
実務でよく使うオプションはこのくらいです。
# パッケージを追加した後、全体再生成ではなく増分更新
sudo createrepo_c --update /srv/repo/rhel9-baseos
# ワーカーを増やして大規模リポジトリの生成時間を減らす
sudo createrepo_c --workers 8 /srv/repo/rhel9-baseos
# パッケージグループ (comps) 情報も一緒に含める
sudo createrepo_c --groupfile /srv/repo/comps.xml /srv/repo/rhel9-baseos
man ページ基準での各オプションの定義はこうです。
| オプション | man ページの説明 |
|---|---|
--update | "Reuse existing metadata for unchanged rpms based on file size and mtime." |
--workers | "Number of workers to spawn for reading rpms." |
-g, --groupfile | "Path to groupfile to include in metadata." |
-s, --checksum | "Choose the checksum type used in repomd.xml and for packages in the metadata. The default is now sha256." |
--compress-type | "Compression type (bz2, gz, zck, zstd, xz)." |
-i, --pkglist | "Text file containing complete list of packages to include." |
--retain-old-md NUM | "Keep old repodata (0 removes all, positive numbers specify copies to retain)." |
-x, --excludes | "Path patterns to exclude, can be specified multiple times." |
-o, --outputdir | "Optional output directory." |
-d, --database は man ページに "DEPRECATED: Generate sqlite databases for use with yum" と表示されています。古い手順書でこのオプションを見かけたら消して構いません。
--pkglist は閉域網で特に役に立ちます。第 4 回で作る持ち込みマニフェストをそのまま入力として渡せば、マニフェストに書かれたものだけが正確にリポジトリへ入ります。一覧と実際のリポジトリが食い違う事故を構造的に防ぐ方法です。
.repo ファイルで登録する
メタデータがあっても dnf がその場所を知らなければ意味がありません。RHEL 9 のドキュメントは明確です。リポジトリは /etc/dnf/dnf.conf か /etc/yum.repos.d/ 配下の .repo ファイルに定義するとし、"Define your repositories in the .repo file instead of /etc/dnf/dnf.conf" と推奨しています。
サーバー 1 台でローカルディレクトリを直接使う場合です。
# /etc/yum.repos.d/airgap-local.repo
[airgap-baseos]
name=RHEL 9 BaseOS (airgap local, snapshot 2026-08-15)
baseurl=file:///srv/repo/rhel9-baseos
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
metadata_expire=-1
[airgap-appstream]
name=RHEL 9 AppStream (airgap local, snapshot 2026-08-15)
baseurl=file:///srv/repo/rhel9-appstream
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
metadata_expire=-1
複数サーバーへ配布するなら、社内 HTTP サーバーを 1 台立てて baseurl だけ変えます。
# /etc/yum.repos.d/airgap-internal.repo
[airgap-baseos]
name=RHEL 9 BaseOS (internal mirror, snapshot 2026-08-15)
baseurl=http://repo.internal.example.com/rhel9/baseos
enabled=1
gpgcheck=1
gpgkey=http://repo.internal.example.com/keys/RPM-GPG-KEY-redhat-release
metadata_expire=-1
priority=1
名前にスナップショット日付を埋め込んでいるのは趣味ではありません。6 か月後にこのサーバーがどの時点のコンテンツでインストールされたのかを突き止める最速の方法が、dnf repolist -v の出力だからです。
dnf の設定ドキュメント基準での各項目の意味はこうです。baseurl は "List of URLs for the repository"、enabled は "Include this repository as a package source. The default is True"、gpgcheck は "Whether to perform GPG signature check on packages found in this repository. The default is False"、gpgkey は "URLs of a GPG key files that can be used for signing metadata and packages of this repository"、priority は "The priority value of this repository, default is 99" です。
gpgcheck の既定値が偽であるという点は必ず覚えておかなければなりません。書いておかなければ検査しません。閉域網ではこれが特に危険で、その理由は第 4 回で扱います。
コマンドでリポジトリを追加することもでき、ここでバージョンの差が出ます。
# RHEL 9 / RHEL 10
sudo dnf config-manager --add-repo http://repo.internal.example.com/rhel9/baseos
# RHEL 8 (公式ドキュメントの表記)
sudo yum-config-manager --add-repo http://repo.internal.example.com/rhel8/baseos
RHEL 8 の公式ドキュメントは yum-config-manager --add-repo と、RHEL 9 と RHEL 10 のドキュメントは dnf config-manager --add-repo と書いています。どちらのドキュメントも "repositories added by this command are enabled by default" と付け加えています。
GPG キーを持ち込んで登録する
gpgcheck=1 にしておくと、最初のインストールでキーを要求されます。閉域網にはキーを自動で取ってくる経路がないので、キーファイルも持ち込み対象です。
キーファイルの正確なパスと名前はディストリビューションとバージョンによって異なるため、この記事では特定のファイル名を断定しません。接続網の機材でまず確認し、そのファイルをバンドルに入れてください。以下の例のファイル名は、確認した実際の名前に置き換えて使う必要があります。
# まずこのシステムがどの GPG キーファイルを持っているか確認する
ls -1 /etc/pki/rpm-gpg/
# リリースパッケージがインストールしたキーファイルを正確に突き止める
rpm -ql redhat-release | grep -i gpg
# 現在のリポジトリ設定がどのキーを参照しているか確認する
grep -h '^gpgkey=' /etc/yum.repos.d/*.repo | sort -u
# 確認したキーを持ち込んで標準の場所に配置
sudo cp ./keys/RPM-GPG-KEY-redhat-release /etc/pki/rpm-gpg/
# キーを rpm キーリングに登録する
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
# 登録されたキーの確認
rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE} %{SUMMARY}\n'
ここでバージョン関連の注意点が 1 つあります。最新のアップストリーム RPM の man ページは -K、--checksig、--import を "Obsolete compatibility aliases" に分類し、rpmkeys(8) を見るよう案内しています。RHEL 8・9・10 では rpm --import がそのまま動作しますが、今後を考えると rpmkeys の表記を使うほうが安全です。
# 今後推奨される表記
sudo rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
# 登録されたキーを指紋とユーザー ID で列挙
rpmkeys --list
rpmkeys の man ページは -i、--import を "Import ASCII-armored public keys. Digital signatures cannot be verified without the corresponding public key (aka certificate)"、-l、--list を "List currently imported public key(s) (aka certificates) by their fingerprint and user ID" と定義しています。
パッケージ署名とメタデータ署名は別の設定であるという点も押さえておきます。gpgcheck はパッケージに対する検査で、repo_gpgcheck は "Whether to perform GPG signature check on this repository's metadata" であり、既定値はやはり偽です。自分で作ったリポジトリのメタデータに署名を付けるには別の手順が必要で、付けていないなら repo_gpgcheck を有効にしてはいけません。
キャッシュが更新を飲み込みます
閉域網でもっともよく上がってくる報告がこれです。リポジトリにパッケージを追加したのにサーバーから見えない、というものです。
原因はたいていメタデータキャッシュです。dnf の設定ドキュメントは metadata_expire を "The period after which the remote repository is checked for metadata update and in the positive case the local metadata cache is updated. The default corresponds to 48 hours" と定義しています。つまり既定の状態では最大 2 日間、古いメタデータをそのまま使います。
リポジトリを更新したなら、こう処理します。
# 1. リポジトリ側: メタデータを作り直す (これを忘れる場合が本当に多いです)
sudo createrepo_c --update /srv/repo/rhel9-baseos
# 2. クライアント側: キャッシュに期限切れの印を付けるだけ (もっとも軽い)
sudo dnf clean expire-cache
# 3. それでも駄目ならメタデータキャッシュを消す
sudo dnf clean metadata
# 4. 最後の手段 — 全部消す
sudo dnf clean all
# 5. キャッシュを先に埋め直す
sudo dnf makecache
dnf ドキュメントの定義はそれぞれ、expire-cache が "Marks the repository metadata expired"、metadata が "Removes repository metadata"、packages が "Removes any cached packages from the system"、all が "Does all of the above" です。上から順に試すほうがよいでしょう。dnf clean all は閉域網では特にもったいなく、消したキャッシュを埋め直すインターネットがないからです。ただし持ち込みリポジトリはローカルなので、再生成のコストは低いです。
先ほどの .repo の例で metadata_expire=-1 を使った理由がここにあります。持ち込みリポジトリは人が明示的に更新するまで絶対に変わらないので、期限切れによる再検査に意味がありません。更新の時点は人が dnf clean expire-cache で知らせるほうが予測可能です。
リポジトリが実際に動作するか確認する
登録が終わったら 3 つを確認します。
# 1. リポジトリが認識され、パッケージ数が 0 でないか
dnf repolist -v
# 2. このリポジトリだけで依存関係がすべて解けるか
dnf repoclosure --repo=airgap-baseos --repo=airgap-appstream
# 3. 実際にインストールできるか (トランザクションだけ確認して止める)
sudo dnf install --assumeno httpd
2 番は第 2 回で紹介した repoclosure です。持ち込み前に 1 回、持ち込み後に実際のリポジトリ構成でもう 1 回回すのがよいでしょう。持ち込みの過程でファイルが欠落することが実際に起きるからです。
サブスクリプションなしに Red Hat のコンテンツを再配布することは契約違反になり得るので、組織のライセンス条件をまず確認してください。特に社内 HTTP ミラーは「再配布」の性格を帯びるので、構成する前に契約条件を確認するほうが安全です。
おわりに — リポジトリはファイルではなく契約です
createrepo_c 自体はコマンド 1 行です。難しい部分はその後にあります。
メタデータを作り直したか、キーを持ち込んで登録したか、gpgcheck を明示したか、キャッシュを期限切れにしたか。この 4 つが閉域網リポジトリ障害の大部分を占めます。4 つとも確認コマンドが 1 行ずつなので、手順書にそのまま入れておけば済みます。
コマンドとオプションは 2026-08-15 に公式ドキュメントで確認しました。RHEL のバージョンによって異なるので、使用中のバージョンのドキュメントで再確認してください。
自分で試す
- Linux ターミナル — リポジトリのディレクトリ構造を作ってみてパスの感覚をつかむ
- chmod 計算ツール — HTTP で配布するリポジトリディレクトリの権限設計
- ハッシュ生成器 — repomd.xml のチェックサムの概念を自分で確かめてみる