Rootless Edge Deployments: Architecting Daemonless CI/CD Pipelines with Podman and Buildah

Quick answer
Bypassing docker.sock: Daemonless Pipeline Orchestration: webhook testing answer
For local webhook testing, run your app locally, expose it with a public HTTPS tunnel, and paste the stable callback URL into the provider dashboard.
How do I test webhooks on localhost?
Start your local server, open a public HTTPS tunnel to that port, configure the provider webhook URL, and inspect events in your local logs.
Why does a stable webhook URL matter?
Stable URLs prevent provider dashboards from needing manual callback updates every time you restart a tunnel.
Exposing a rootレベルの Docker ソケットをパイプラインに直接アクセスできる状態で公開することは、サプライチェーンのタイムボムです。デーモンを完全に切り離す時が来ました。ここでは、Podman と Buildah を使った完全に rootless かつ daemonless なデプロイパイプラインの設計方法と、2026年中に rootless コンテナエコシステムに実際に何が変わったのかを解説します。
1. 共有ソケットの脆弱性
従来の CI/CD 環境では、Docker に依存することが一般的なアンチパターンを生み出しています:パイプラインランナーに /var/run/docker.sock をマウントし、ホストの Docker デーモンに直接アクセスさせる方法です。これは Docker-in-Docker (DinD) やソケットマウントと呼ばれ、ランナーがホストエンジンに完全な管理者権限を持つことを意味します。OCI イメージのビルドやタグ付け、プッシュには便利ですが、ローカルハードウェアやエッジゲートウェイ、ローカルプロキシのプロビジョニングには非常に危険です。
[ 攻撃された CI/CD ランナーコンポーネント ]
│
▼ (悪意のあるペイロードを実行)
[ /var/run/docker.sock API 呼び出し ]
│
▼ (即座にホストの root 権限に昇格)
[ 物理エッジハードウェア / ホスト OS 資産が侵害 ]
物理機器に直接アクセスできる環境 — IIoT インターフェース、SCADA ネットワーク、医療システム、エッジ AI ノード — では、コンテナの脱出は単なるソフトウェアの障害ではなく、サイバーフィジカルな被害のベクトルとなります。dockerd はネイティブに root で動作するため、暗号化されていない UNIX ソケットにアクセスできる任意のプロセスは、API コールを発行可能です:docker run --privileged -v /:/host などはホスト OS を直接侵害するのに十分です。
これは仮説ではありません。CVE-2024-21626(「Leaky Vessels」)は2024年1月に公開され、実際にこのクラスの脆弱性を示しました。runc(バージョン 1.1.11 まで)の内部ファイルディスクリプタリークにより、悪意のあるイメージや runc exec ペイロードがコンテナの作業ディレクトリをホストのファイルディスクリプタに設定し、ホストファイルシステムの外にコンテナプロセスを配置し、ホストバイナリを書き換える可能性がありました。これらの問題は runc 1.1.12 で修正されましたが、ランタイムの脱出の「爆発範囲」が重要であることを示す良い例です。root で動作するデーモンはこの種のバグをホスト全体の侵害に変えますが、適切に設定された rootless パイプラインは、システムバイナリやカーネルモジュール、物理デバイスにアクセスできない低権限のサービスアカウントにとどまります。
産業インフラを保護するには、現代のアーキテクチャは、コンテナ作成とイメージビルドを非特権ユーザ名前空間内でネイティブに実行する仕組みへと完全に移行する必要があります。これが Podman と Buildah の設計の根幹です。
2. 技術的解説:Podman と Buildah のアーキテクチャ
従来の Docker アーキテクチャ:
[ クライアント CLI ] ──(UNIX socket/TCP)──► [ 中央のモノリシックデーモン (dockerd) ] ──► [ OCI ランタイム (runc) ]
デーモンレスアーキテクチャ:
[ Podman / Buildah CLI ] ──(直接 Linux の fork/exec と名前空間)──► [ OCI ランタイム (crun / runc) ]
Docker のクライアント-サーバーモデルは、CLI 入力を REST コールに変換し、ソケット経由で dockerd に送信します。dockerd は常駐する root プロセスで、すべてのコンテナを子として管理します。デーモンが失敗すると、ホスト上のすべてのコンテナのオーケストレーションも破綻します。
一方、Podman と Buildah は、Linux のプロセスライフサイクルに近い fork-exec モデルを採用しています。バイナリは一時的なユーティリティで、OCI 準拠のランタイム(crun や runc)を通じて kernel と直接通信し、containers/storage ライブラリを使って状態を追跡し、タスク完了後に終了します。長期間動作するバックグラウンドサービスや公開された API ソケットはありません。
コアコンポーネント
- Podman(Pod 管理) — デーモンレスでポッドや OCI コンテナの管理・実行・デバッグ(
run,stop,ps,exec) - Buildah — OCI イメージ構築に特化したユーティリティ。
podman buildには Buildah のライブラリを内部的に利用し、スタンドアロンの Buildah は詳細なスクリプト化やレイヤーごとのビルドコマンドを提供します。 - Skopeo — イメージの検査、署名、レジストリ間のコピーを行い、レイヤーの完全なプルや展開なしに操作可能。
ユーザ名前空間:rootless 実行の仕組み
Rootless コンテナは Linux のユーザ名前空間(user_namespaces(7))に依存しています。ホストの UID/GID の範囲をコンテナ内にマッピングし、非特権ユーザが root で動作する Podman インスタンスを起動すると、カーネルは実際の UID(例:1001)を UID 0 にマッピングします。これにより、そのプロセスがコンテナ外に出ようとすると、ホスト側の UID 1001 に変換されます。
もしサプライチェーン攻撃で悪意のあるペイロードが rootless パイプライン内のコンテナで動作し、その脆弱性を突いて脱出に成功した場合、攻撃者は低権限のサービスアカウントとしてホストに侵入します。システムバイナリやカーネルモジュール、物理デバイスには直接アクセスできません。
3. ハードウェア提供のためのユーザ名前空間設定
/etc/subuid と /etc/subgid ファイルは、ホスト側の未特権ユーザにサブUID/GIDのブロックを割り当てるために使われます。
gitlab-runner:100000:65536
3つのコロン区切りフィールド:
- 識別子 — ホストのサービスアカウント(例:gitlab-runner)
- 初期サブUID — 割り当て開始番号(例:100000)
- 範囲数 — 割り当てられるUIDの数(例:65536)
| コンテナ内 UID | ホストの UID マッピング | 説明 |
|---|---|---|
| 0(内部 root) | 1001 | 実際のホストの UID |
| 1 | 100000 | 最初のサブUID |
| 2 | 100001 | 2番目のサブUID |
| 65535 | 165535 | 最終サブUID |
現代のパッケージマネージャはこれを自動的に設定します — sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 <ユーザ> — しかし、インフラストラクチャ・アズ・コードツールによって作成されたパイプラインサービスアカウントには明示的に設定する必要があります。grep <ユーザ> /etc/subuid /etc/subgid で確認しましょう。
ストレージドライバ:ネイティブ overlay が fuse-overlayfs をほぼ置き換え
最初は制約がありましたが、現在は変わっています。rootful コンテナは kernel の overlay ファイルシステムを直接使用します。長らく非特権ユーザは overlay のマウントができませんでしたが、fuse-overlayfs(FUSE ベースのユーザースペース実装)がその代替として使われてきました。
しかし、Linux 5.13 以降、ネイティブの rootless overlay マウントがサポートされるようになり、fuse-overlayfs は fallback になっています。podman info で Native Overlay Diff: true が出ている場合は、ネイティブ overlay が使われています。設定ファイルも不要です。
[storage]
driver = "overlay"
[storage.options.overlay]
# Linux kernel 5.13+ ではネイティブ rootless overlay が自動的に使用されます。
# 古いカーネルや NFS 共有のホームディレクトリ、ネストされたコンテナシナリオでは、
# 以下の行をアンコメントしてください。
# mount_program = "/usr/bin/fuse-overlayfs"
mountopt = "nodev,metacopy=on"
metacopy=on は、下層のレイヤーデータを実際に変更されるまでコピーしない設定で、ビルドの高速化に寄与します。
4. Buildah を使ったデーモンレスイメージ構築の設計
Buildah は静的な Dockerfile パースよりも、プログラム的・スクリプト的なビルドに適しています。ローカル設定のマトリクスを取得したり、ファームウェアバイナリをコンパイルしたり、セキュアなイメージを中間層なしで組み立てるのに便利です。
#!/usr/bin/env bash
set -o errexit
set -o pipefail
set -o nounset
# ステップ1:最小限のベースイメージからクリーンな作業環境を初期化
echo "=== OCIコンテナの初期化 ==="
container=$(buildah from alpine:3.19)
# ステップ2:コンテナのルートファイルシステムをホストの未特権空間にマウント
echo "=== コンテナファイルシステム層の公開 ==="
container_mount=$(buildah mount "${container}")
# ステップ3:アプリケーション層の準備
echo "=== アプリケーション層とエッジアーティファクトの構築 ==="
mkdir -p "${container_mount}/opt/edge/proxy"
mkdir -p "${container_mount}/etc/edge"
echo "ハードニングされた通信ループの初期化" "${container_mount}/opt/edge/proxy/core.bin"
chmod 755 "${container_mount}/opt/edge/proxy/core.bin"
# ステップ4:コンテナの設定
echo "=== コンテナ設定の適用 ==="
buildah config --workingdir "/opt/edge/proxy" "${container}"
buildah config --entrypoint '["/opt/edge/proxy/core.bin"]' "${container}"
buildah config --user "1001:1001" "${container}"
buildah config --label architecture="edge-hardened" "${container}"
buildah config --env PROXY_PORT="8080" "${container}"
# ステップ5:イメージのコミット
echo "=== OCIイメージの確定 ==="
buildah commit --rm "${container}" "localhost/edge-proxy:v1.0.0"
echo "=== ビルド完了:localhost/edge-proxy:v1.0.0 ==="
実践的な意義:
- ソケット非公開 — ビルドはパイプライン内で完結し、デーモンは不要です。
- ファイル制御の粒度向上 — buildah mount で直接ファイルシステムにアクセスでき、RUN cp や ADD の層の肥大化を防ぎます。
- 非 root でのターゲット設定 — buildah config --user "1001:1001" により、イメージは非特権で動作します。
2026年中に Buildah は v1.44.0(2026年5月リリース)、Podman の安定版は v5.8.x(v5.8.2、2026年4月)です。CI では :latest よりもバージョン固定がおすすめです(頻繁にセキュリティ修正が行われるため)。
5. 実装ガイド:安全なエッジプロビジョニングパイプライン
以下は GitLab CI/CD の例です。GitHub Actions、Tekton、Jenkins でもほぼ同じ構造です。
stages:
- lint
- build
- provision
variables:
# ローカルランナーの作業空間内にストレージ層を隔離
CONTAINERS_STORAGE_CONF: "$CI_PROJECT_DIR/.containers/storage.conf"
REGISTRY_AUTH_FILE: "$CI_PROJECT_DIR/.containers/auth.json"
STORAGE_DRIVER: "overlay"
default:
before_script:
- mkdir -p $(dirname "$CONTAINERS_STORAGE_CONF")
- |
echo '[storage]' "$CONTAINERS_STORAGE_CONF"
echo 'driver = "overlay"' "$CONTAINERS_STORAGE_CONF"
# kernel 5.13+ ではネイティブ overlay が自動的に使用される
# 旧カーネルや NFS 共有のホームディレクトリでは以下をアンコメント
# echo '[storage.options.overlay]' "$CONTAINERS_STORAGE_CONF"
# echo 'mount_program = "/usr/bin/fuse-overlayfs"' "$CONTAINERS_STORAGE_CONF"
- podman login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
build_edge_image:
stage: build
tags:
- rootless-edge-runner
script:
- echo "エッジ資産のビルド(rootless)..."
- podman build --storage-driver=$STORAGE_DRIVER --layers -t "$CI_REGISTRY_IMAGE/edge-node:$CI_COMMIT_SHA" .
- echo "レジストリにプッシュ..."
- podman push "$CI_REGISTRY_IMAGE/edge-node:$CI_COMMIT_SHA"
provision_local_hardware:
stage: provision
tags:
- rootless-edge-runner
script:
- echo "SSH経由でターゲットエッジデバイスにプロビジョン..."
- |
ssh -i "$EDGE_PROXY_DEPLOY_KEY" -o StrictHostKeyChecking=require "$EDGE_PROXY_USER@$EDGE_PROXY_IP" <<EOF
set -e
echo "物理展開ゲートウェイに接続しました。"
podman login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
podman pull "$CI_REGISTRY_IMAGE/edge-node:$CI_COMMIT_SHA"
podman stop telemetry-collector || true
podman rm telemetry-collector || true
# --privileged なし、ホストのデバイスを直接マウントしない
podman run -d \
--name telemetry-collector \
--restart=always \
--user=1001:1001 \
--security-opt label=disable \
--device /dev/ttyUSB0:/dev/ttyUSB0:rw \
-p 8443:8443 \
"$CI_REGISTRY_IMAGE/edge-node:$CI_COMMIT_SHA"
echo "ハードウェアインターフェースが初期化され、ストリーミングを開始しました。"
EOF
only:
- main
このパイプラインに組み込まれるセキュリティ対策:
- 隔離されたストレージ設定 — CONTAINERS_STORAGE_CONF を $CI_PROJECT_DIR に書き換えることで、共有ランナー上のテナント間のレイヤー漏洩を防止
- 明示的な --device マッピング — --privileged の代わりに、必要なデバイスだけを --device でマッピング
- ユーザースペースのネットワーク — rootless コンテナは、root 所有のブリッジや iptables ルールではなく、ユーザースペースのネットワークヘルパーを利用(詳細は以下) — これも2024年以降大きく変わったポイントです。
6. 2026年の変化点:ネットワーク、ストレージ、バージョンの最新情報
このセクションは rootless コンテナの記事で陳腐化しやすいため、2026年中の現状を明示します。
パスタが slirp4netns に代わり、rootless ネットワークのデフォルトバックエンドに。長年、rootless Podman は slirp4netns に依存していました。これは仮想インターフェースを作り、パケットを NAT 経由でユーザースペースに翻訳します。パフォーマンスにペナルティがありましたが、Podman 5.0(2026年リリース)以降は pasta(passt)に置き換わっています。pasta はホストのネットワーク設定を直接反映し、NAT を排除します。これにより、ネットワークのオーバーヘッドが大きく削減されました。
ただし、slirp4netns は今後の Podman 6.0 で完全に廃止予定です(2026年中に Fedora の Change Proposal で進行中)。network=slirp4netns の設定をしているパイプラインは移行を検討してください。
低ポートバインディングの制限は変わらず。 net.ipv4.ip_unprivileged_port_start のデフォルトは 1024 で、これ未満のポートには非特権ユーザはバインドできません。設定例:
# /etc/sysctl.d/99-rootless-ports.conf
net.ipv4.ip_unprivileged_port_start=443
これにより、HTTPS の標準ポート443は解放され、その他の低ポートは引き続き制限されます。
ホストの UID とコンテナ内の UID のパーミッションミスマッチも、:Z/:z の SELinux relabeling フラグや --userns=keep-id で解決します。
アイドル時のメモリフットプリント: これは実測値であり、「0 MB」とは少し誇張です。dockerd + containerd は、バージョンや設定により約 55〜200 MB の RSS を消費しますが、Podman はほぼゼロに近いです。ただし、コンテナごとに軽量な conmon モニタープロセスと、ネットワーク名前空間を持つ場合は pasta プロセス(約29MB)が動作します。これらを考慮すると、実際のオーバーヘッドは低いままです。Docker のオーバーヘッドは常に存在しますが、Podman の場合はほぼありません。
7. アーキテクチャのチェックリスト:コンテナスタックの強化
┌────────────────────────────────────────┐
│ デーモンレス強化監査完了 │
└────────────────────────────────────────┘
│
┌────────────────────────────────┼────────────────────────────────┐
▼ ▼ ▼
[ ユーザ名前空間 ] [ アクティブなデーモンなし ] [ 細粒なデバイス制御 ]
`/etc/subuid` のマッピング | dockerd の稼働なし | `--device` で明示的にパス
検証済み | ホスト上で動作 |
- [ ] ユーザ名前空間の境界を検証 — すべてのランナーアカウントは
/etc/subuidと/etc/subgidに少なくとも 65,536 のIDが割り当てられていること - [ ] デーモンの完全排除 —
dockerdは完全に削除または無効化(systemctl disable --now docker)されていること - [ ] イメージの由来を検証 —
skopeoとcosignで署名を検証。2026年には、ADD/COPYのパストラバーサル(CVE-2026-44517)や、悪意のあるイメージの環境変数リーク(CVE-2026-57231)などの脆弱性が報告されています。これらを防ぐために署名検証とレジストリのホワイトリストを活用します。 - [ ] デバイスには最小権限を適用 —
--privilegedの代わりに、必要なデバイスだけを--deviceでマッピングし、--cap-addも必要最小限に - [ ] 非 root でのコンテナ実行 — すべてのペイロードを非 root
USER(例:1001)でビルド・実行 - [ ] ランタイムも最新に保つ —
runcは複数の CVE(例:CVE-2024-21626、CVE-2025-31133、CVE-2025-52565、CVE-2025-52881)により脆弱性が報告されており、Buildah 1.42 でのruncバージョンアップにより修正済みです。CI でバージョン固定を行い、常に最新のパッチを適用しましょう。
結論
サプライチェーンに直接アクセスできるパイプラインで root レベルの Docker ソケットを公開することは、重大な脆弱性です。これを避けるには、Podman と Buildah を用いた分散型・daemonless なモデルに移行することが最善策です。これにより、上流の攻撃が未特権ユーザに留まり、システム全体の侵害リスクを低減します。2026年の最新情報では、ネイティブ overlay のサポートや pasta の導入により、パフォーマンスの向上と運用の容易さも実現しています。根本的なセキュリティ対策は、ユーザ名前空間の隔離、明示的なデバイスマッピング、イメージ署名の検証、そしてランタイムの継続的なパッチ適用にあります。これらを徹底することが、エッジ展開の堅牢性を確保する鍵です。
変更履歴
メタデータは削除 — 元の下書きのメタ情報とフォーマットは公開用に整理済み。
事実確認と修正:
- 「Docker-in-Docker (DinD) や socket-mouting」→「socket-mounting」
- パイプライン例の変数名やタグの不整合を修正
- net.ipv4.ip_unprivileged_port_start のデフォルト値(1024)と sysctl の設定例を確認
- CONTAINERS_STORAGE_CONF と REGISTRY_AUTH_FILE の実在性と役割を確認
2026年の最新情報を追加: - CVE-2024-21626 / 「Leaky Vessels」の具体例 - ストレージドライバの解説を更新:ネイティブ overlay が推奨されること - pasta が slirp4netns に代わり、Podman のデフォルトネットワークバックエンドになったこと - Podman 6.0 での slirp4netns 廃止予定 - 実測値に基づくメモリフットプリントの比較 - 現行バージョンの明示(Podman 5.8.2、Buildah 1.44.0) - セキュリティ脆弱性(CVE-2026-44517 / CVE-2026-57231)とランタイム CVE の具体例
フォーマット: - インライン図やコードブロックはフェンス付きの Markdown コードブロックに変換 - チェックリストは Markdown タスクリスト形式に - UID マッピング例の表を追加
Related InstaTunnel pages
Continue from this article into the most relevant product guides and workflows.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.