使い捨て公開 runner

English | 日本語

公開用 runner は、ジョブごとに新しい QEMU/KVM 仮想マシンを使います。 PR 用 Docker runner とは別であり、それ自体はタグや Release を公開しません。 Issue #988 の実装の一部です。 イメージ作成、ゲスト内の開始前チェック、単一ジョブの起動処理を提供します。 常駐サービスの設置や日次公開の有効化は行いません。 手動実行だけの rc.yml は読み取り権限でゲストの判定記録を確認します。 checkout、第三者 Action、公開ステップはありません。実行指示と runner 登録は 別途承認する運用操作です。

作成と確認

KVM を利用できる Linux、Python 3.11 以降、QEMU、qemu-img、 cloud-localds、-N 対応の OpenBSD nc、setpriv、memory と process の cgroup 制御を利用できる systemd user manager が必要です。実行ファイルは root 所有で、一般ユーザーが 書き換えられない経路から解決します。Python は継承した loader 環境変数なしで起動できる 必要があります。LD_LIBRARY_PATH に依存する toolcache の Python は VM 起動前に拒否します。 ネイティブ Linux ストレージにイメージの 専用コピーと 20 GiB のゲストディスクを置ける空き領域を用意します。 VM は 2 vCPU、4 GiB RAM を使います。実ジョブの実行時は PC の起動と通信が必要です。

QEMU 起動前に、一時的な user scope 内で非 root の全 UID、capability がゼロ、 no-new-privileges、最大 128 タスク、合計 6 GiB メモリ、swap 無効を確認します。 この上限には proxy 子プロセスも含みます。ゲスト RAM は QEMU の core dump から除外します。転送に必要な setsid を許すため、 proxy 利用時の QEMU の権限 syscall 制限は、検証済みの OS 制約で補います。 ゲストの 2 vCPU はホスト全体の CPU 使用量上限ではありません。ホストの実行ユーザーと QEMU は信頼境界の内側であり、侵害された QEMU から同じユーザーのファイルを隔離する 仕組みではありません。

python3 scripts/build_release_vm.py --output /absolute/new/release.qcow2
python3 scripts/release_vm.py --image /absolute/new/release.qcow2 \
  --image-sha256 IMAGE_DIGEST --workflow-sha ACCEPTED_WORKFLOW_COMMIT

scripts/smoke_release_vm_network.py --image IMAGE --image-sha256 DIGEST は 一時的なループバック HTTP サーバーを使い、認証情報なしのゲストで通信を試します。 指定した proxy 経路だけが通り、ホストやインターネットへの直接接続が拒否されることを 確認します。

作成結果の JSON にあるイメージ、Ubuntu ベース、runner、チェック処理、 起動処理のダイジェストを保存し、その成果物を登録前にレビューします。 日付指定の Ubuntu ベースと runner アーカイブをハッシュで検証し、依存パッケージは 日付指定の Ubuntu snapshot から入れます。毎回同一バイト列のイメージになるという 保証ではなく、作成したイメージそのもののハッシュを起動時の識別子とします。

通常の起動は認証情報なしの動作確認です。GitHub への登録は行いません。 許可・拒否の入力をゲスト内で試し、権限を落とした runner ユーザーでチェックを 実行し、証拠ファイルを書き換えられないことを確認して、停止・破棄します。 実 Actions ジョブ、proxy の可用性、自動公開の成功を証明するものではありません。

別途承認する単一ジョブの起動

イメージ、承認済み rc.yml の正確なコミット、proxy を確認した後に使います。 次の操作は GitHub runner の登録を変更するため、その操作の承認が必要です。

python3 scripts/release_vm.py --image /absolute/new/release.qcow2 \
  --image-sha256 IMAGE_DIGEST --workflow-sha ACCEPTED_WORKFLOW_COMMIT \
  --proxy TRUSTED_PROXY_IPV4:PORT --register-one-job

ホストの既存 gh 認証は JIT 登録とその正確な削除だけに使い、ゲストには コピーしません。一意の ai-teams-release-INSTANCE 名と公開専用ラベルを使います。 JIT 値は専用の一時ディスクで渡し、コマンド引数やコンソールへ出力しません。 ジョブの GITHUB_TOKEN とその権限は引き続き GitHub と workflow が管理します。

ゲストの通信は QEMU の転送で明示したホスト側 proxy だけに制限します。 通常の外向き通信、ホストの待受ポート、共有ディレクトリ、Docker socket は 与えません。proxy の接続先ポリシーは別途確認します。認証情報のないイメージ作成時 だけは通常の通信を使い、登録済みゲストにはその通信設定を使いません。

ホストが VM・登録・イメージ・workflow・期限を結び付けた証拠を作り、ゲスト内で root 所有の読み取り専用ファイルとして渡します。runner に sudo 権限はありません。 開始前チェックは、このリポジトリの main 上の rc.yml、承認済み workflow SHA、 schedule または workflow_dispatch、正確な runner 名、初回実行だけを受け入れます。 チェックは一度だけ通過でき、公開権限は与えません。ラベルや外から渡した JSON だけを隔離の証拠にはしません。

runner v2.336.0 は開始前チェックより先に Action アーカイブを準備します。 拒否するのは workflow のステップ実行であり、未信頼のデータを一切ダウンロード しない保証ではありません。拒否された場合も含め、一つのジョブが終わるたびに ゲスト全体を破棄します。GitHub 側の runner group による制限ではありません。

失敗と復旧

一回の起動は VM と登録が各一つ、最長 30 分、出力 4 MiB、ゲストディスク 20 GiB までです。既存の Linux プロセス管理が QEMU と proxy の子プロセスを停止させてから 一時領域を削除します。期限切れ、中断、イメージ不一致、未対応の外部ディスク参照、 古い証拠、ゲストの失敗は成功扱いにしません。登録の削除は既存の runner 管理が担当します。 登録の結果が不明な場合や停止・削除が未確認の場合は、再試行前に正確な状態を 照合してください。不在を推測して新しい登録を作ってはいけません。

常駐化、pilot、live activation は別の段階です。#988 の workflow/provider 検証と 認証情報を分離した 10 窓の試験が終わるまでは、手動公開を維持します。