閉域網の魔物を調教せよ:Puppetエアギャップ環境における極限のローカルミラー構築と証明書自動化の奥義
テックリードの私たちが直面する最大のストレスの一つ、それは「完全なる閉域網(エアギャップ環境)」でのインフラ運用だ。
「セキュリティ要件のため、外部インターネットへの接続は一切禁止。YumもAptも使えない。当然、Puppet Forgeへのアクセスも夢のまた夢。」
こういう環境に放り出された途端、多くのエンジニアは意気消沈し、手動でのパッケージコピーや、期限切れ間近のSSL証明書に怯える夜を過ごすことになる。
だが、案ずるな。Puppetの本質とPKI(公開鍵基盤)のメカニズムを骨の髄まで理解していれば、閉域網はむしろ「外部の脆弱性リスクから完全に隔離された、美しく強固な要塞」へと姿を変える。
今回は、外部インターネット一切遮断の環境下で、Puppet MasterとAgentを完璧に同期させ、パッケージ配信から証明書の自動ローテーションまでを完全無人化する「プロの実践テクニック」を余すところなく伝授しよう。
—
1. 閉域網の生命線:オフラインパッケージミラーの構築
インターネットがない? ならば、インターネットを持ち込めばいい。ただし、スマートに、だ。
社内のDMZ(またはインターネット接続可能な踏み台環境)に「ミラーリングサーバー」を1台建て、そこから定期的に資材を吸い上げ、オフライン環境へ安全にブリッジする。
1-1. プライベートYum/Aptリポジトリの同期と持込
Puppetの各バージョン、および依存するRubyのGemやOSパッケージをローカルにキャッシュするための構成だ。
ここでは例として、RHEL/Rocky Linux系のプライベートリポジトリ構築スクリプト(一部)を示す。
!/usr/bin/env bash
==============================================================================
Script Name: sync-puppet-mirror.sh
Description: 踏み台環境でPuppet公式およびOSリポジトリをミラーリングする
==============================================================================
set -euo pipefail
MIRROR_DIR=”/data/mirrors/rocky9″
PUPPET_VERSION=”7″
echo “=== [1/3] Syncing Base OS and Epel Repositories ==/+/=”
reposync –repoid=baseos –download-metadata –newest-only -p “${MIRROR_DIR}”
reposync –repoid=appstream –download-metadata –newest-only -p “${MIRROR_DIR}”
reposync –repoid=epel –download-metadata –newest-only -p “${MIRROR_DIR}”
echo “=== [2/3] Syncing Puppet Labs Official Repo ==/+/=”
Puppet Labsの公式RPMをリポジトリごとミラーリング
reposync –repoid=”puppet${PUPPET_VERSION}” –download-metadata -p “${MIRROR_DIR}”
echo “=== [3/3] Creating Local Repository Metadata ==/+/=”
createrepo –update “${MIRROR_DIR}/baseos”
createrepo –update “${MIRROR_DIR}/appstream”
createrepo –update “${MIRROR_DIR}/epel”
createrepo –update “${MIRROR_DIR}/puppet${PUPPET_VERSION}”
echo “Mirror sync completed successfully. Move ${MIRROR_DIR} to physical media or secure storage.”
これを可搬メディア(暗号化USB等)や内部の転送ゲートウェイ経由でエアギャップ環境へ流し込み、内部Nginxサーバーなどで配信する。
1-2. エージェント側リポジトリ設定のベストプラクティス (YAML / Hiera)
閉域網内のノードが参照するYumリポジトリ定義は、Hieraを用いて一元管理する。動的にIPやホスト名が変わっても対応できるようにする。
/etc/puppetlabs/code/environments/production/hieradata/common.yaml
—
外部リポジトリを無効化し、内部ミラーに向ける
apt_repositories:
- ensure: absent
name: ‘puppetlabs’
- ensure: present
name: ‘local-puppet7’
baseurl: ‘http://mirror.internal.net/rocky9/puppet7/’
gpgcheck: true
gpgkey: ‘http://mirror.internal.net/rocky9/RPM-GPG-KEY-puppet’
enabled: true
priority: 1
—
2. 内部CA(認証局)と証明書運用の深淵
エアギャップ環境で最もエンジニアを絶望させるのが「SSL/TLS証明書の有効期限切れ」だ。
外と通信できないため、Let’s Encryptのような自動化サービスは使えない。社内に独自のCA(Certificate Authority)を立て、Puppetの強固なセキュリティモデルを維持しつつ、証明書の自動更新・配備を設計しなければならない。
2-1. Puppet CA階層の設計思想
Puppet Master自体がデフォルトでCAとして機能するが、厳格なセキュリティ要件を持つ閉域網では、「オフライン・ルートCA」と「オンライン・サブCA(Puppet Master)」の二重構造を推奨する。
[ オフライン・ルートCA ] (安全な金庫に保管 / 年1回程度起動)
│
▼ 署名
[ サブCA / Puppet Master ] (日常運用)
│
├─► [ Agent Node 01 ]
├─► [ Agent Node 02 ]
└─► [ Agent Node N ]
2-2. 証明書自動承認(Auto-Sign)のセキュアな実装
閉域網内とはいえ、セキュリティを妥協してはならない。`autosign.conf`による単純なワイルドカード許可(`.internal.net`)は、悪意ある内部不正や踏み台からの不正参加を防げないため厳禁だ。
代わりに、ポリシースクリプト(Policy-based Autosigning)を導入し、MACアドレスや事前共有トークン(Pre-Shared Key)による検証を強制する。
設定ファイル: `puppet.conf` (Master側)
[master]
ポリシーベースのオートサインスクリプトを指定
autosign = /etc/puppetlabs/puppet/autosign-policy.rb
ポリシー検証スクリプト: `/etc/puppetlabs/puppet/autosign-policy.rb`
!/usr/bin/env ruby
==============================================================================
Puppet Policy-Based Autosign Script
外部通信なしで、CSRの拡張属性(CSR Attributes)を検証し、許可を決定する
==============================================================================
require ‘openssl’
require ‘json’
certname = ARGV[0]
csr_pem = STDIN.read
csr = OpenSSL::X509::Request.new(csr_pem)
CSRのカスタムOIDs(例: 組織コードやデプロイトトークン)をパースする
事前に関内で発行されたトークンが埋め込まれているか確認
valid_token = “SECURE-INTERNAL-DEPLOY-TOKEN-202X”
token_found = false
csr.attributes.each do |attr|
if attr.oid == “1.2.840.113549.1.9.14” # pkcs9-at-extensionRequest
extensions = OpenSSL::ASN1.decode(attr.value)
extensions.each do |ext|
# 自社定義のOID等から検証データを抽出
# ここでは簡易的に拡張領域の存在チェックとする
token_found = true if ext.to_der.include?(valid_token)
end
end
end
if token_found
# 承認ログを記録して終了コード0(許可)
STDERR.puts “Autosign approved for: #{certname}”
exit(0)
else
# 拒否(終了コード非0)
STDERR.puts “Autosign rejected for: #{certname} (Invalid token)”
exit(1)
end
—
3. 開発・運用の生産性を爆上げするプロの流儀
閉域網でのPuppet開発は、フィードバックループが遅くなりがちだ。「コードを書いて、Masterに反映して、Agentを叩いて…」この手数を極限まで減らすためのテクニックを共有しよう。
3-1. エディタ連携とショートカット(VS Codeを前提とした神設定)
チーム全員の開発環境を統一し、ミスをコンパイル(シンタックスチェック)段階で潰す。
- 必須拡張機能:
- `Puppet.puppet-vscode` (公式シンタックス・補完)
- `jpastoor.puppet-lint` (コーディング規約の自動静的解析)
- `editorconfig.editorconfig` (インデントや改行コードの統一)
ワークスペース設定: `.vscode/settings.json`
{
“puppet.installDirectory”: “/opt/puppetlabs/server/data/puppetserver”,
“puppet.editorService.enabled”: true,
“puppet-lint.enabled”: true,
“puppet-lint.additionalArguments”: [
“–no-class_inherits_from_params_class-check”,
“–no-hard_coded_globals-check”
],
“editor.formatOnSave”: true,
“files.insertFinalNewline”: true
}
3-2. チーム開発における設定共有化ルール(R10K & Git)
閉域網であっても、コード管理の近代化を諦めてはならない。Gitリポジトリ(GitLabやGiteaなどのオンプレミス環境)を軸に、R10Kを用いて環境(Environment)を動的にデプロイする。
設定ファイル: `r10k.yaml`
==============================================================================
R10K Configuration for Air-Gapped Environment
==============================================================================
—
モジュールのソースは社内GitLab(オフプレ)を指定
sources:
puppet:
remote: ‘https://git.internal.net/infra/puppet-control.git’
basedir: ‘/etc/puppetlabs/code/environments’
Forgeから直接ダウンロードできないため、モジュールはすべてコントロールレポの
modules/ 配下に同梱(Vendorパターン)するか、社内Forgeミラーに向ける
cachedir: ‘/var/cache/r10k’
—
4. 完全自動化の真髄:証明書の自動ローテーション設計
Puppet Agentの証明書(SSL証明書)はデフォルトで1年、あるいはMasterの設定に依存する。閉域網で証明書が失効すると、エージェントはMasterと通信できなくなり、手動で証明書を削除・再発行する地獄の作業が発生する。
これを完全に防ぐため、証明書の有効期限を監視し、残り30日を切ったら自動でクリーンアップと再申請を行うDaemon/Cronスクリプトを全ノードに配布する。
巡回監視・自動再申請スクリプト: `/usr/local/bin/puppet-cert-watcher.sh`
!/usr/bin/env bash
==============================================================================
Script Name: puppet-cert-watcher.sh
Description: 証明書の有効期限をチェックし、切れそうなら自動で再申請を行う
==============================================================================
set -euo pipefail
CERT_FILE=”/etc/puppetlabs/puppet/ssl/certs/$(facter fqdn).pem”
THRESHOLD_DAYS=30
if [ ! -f “${CERT_FILE}” ]; then
echo “Certificate file not found: ${CERT_FILE}”
exit 1
fi
有効期限の終了日を取得してエポック秒に変換
END_DATE=$(openssl x509 -enddate -noout -in “${CERT_FILE}” | cut -d= -f1)
EXPIRES_AT=$(date -d “${END_DATE}” +%s)
CURRENT_DATE=$(date +%s)
残り日数を計算
SECONDS_LEFT=$(( EXPIRES_AT – CURRENT_DATE ))
DAYS_LEFT=$(( SECONDS_LEFT / 86400 ))
echo “Puppet certificate days left: ${DAYS_LEFT} days.”
if [ “${DAYS_LEFT}” -lt “${THRESHOLD_DAYS}” ]; then
echo “WARNING: Certificate is expiring soon. Initiating automated renewal…”
# 1. サービス停止
systemctl stop puppet
# 2. 既存のSSL証明書ストアをクリア (Master側でのrevokeは事前にAPI経由等で行う想定、または自動化)
/opt/puppetlabs/bin/puppet ssl clean
# 3. 再起動して自動的にCSRを送信・取得(ポリシーベースオートサインが機能する)
systemctl start puppet
echo “Puppet certificate renewal process triggered.”
fi
これを各ノードのCrontabに登録する。
毎日深夜3時に証明書の健康診断を実施
0 3 /usr/local/bin/puppet-cert-watcher.sh >> /var/log/puppet/cert-watcher.log 2>&1
—
5. 結び:制約こそがインフラストラクチャーを美しくする
「インターネットがない」という制約は、一見すると足枷に思えるかもしれない。
しかし、ここまで解説したように、
1. リポジトリの完全なローカルミラーリング
2. トークン駆動のセキュアな自動署名(Autosign)
3. R10KとVS Codeによる厳格なコード管理
4. 証明書の寿命を自律的に担保する監視・更新ループ
これらをコードとして紡ぎ上げ、Puppetに流し込むことで、「クラウド環境以上に強固で、人間のミスが介入する余地のない、完全自律型のインフラストラクチャー」が完成する。
手動作業の誘惑に負けるな。すべてをコード化し、閉域網の王となれ。