クラウド Mac CI のプロセス環境変数漏えい監査と秘密情報の分離

クラウド Mac CI のプロセス環境変数漏えい監査と秘密情報の分離

1 台のクラウド Mac でコードの取得、ビルド、テスト、リリースをまとめて実行する場合、最も見落とされやすい境界はディレクトリではなくプロセス環境です。パイプラインがトークンを環境変数に設定すると、xcodebuild、スクリプト、テストホスト、バックグラウンドの子プロセスは、いずれもデフォルトでその値を継承します。メインタスクが終了していても、終了していない補助プロセスにすべての変数が残っている可能性があります。重要なのは環境変数を禁止することではなく、秘密情報が見える時間とプロセスを最小限に抑えることです。

変数の伝播経路を可視化する

runner から起動される各プロセスを、ビルド、テスト、リリース、補助ツールの 4 種類に分類します。通常、ビルドとテストにリリーストークンは不要であり、補助ツールにも継承させるべきではありません。実際の値は記録せず、変数名、注入する工程、使用するコマンド、想定する破棄時点だけを記録します。

確認対象 一般的なリスク 検証方法
runner の親プロセス 起動時にすべての秘密情報を取得する 変数名が存在するかだけを確認する
ビルドの子プロセス 親プロセスの環境を無条件で継承する ランダムなマーカーで継承テストを実行する
launchd ドメイン setenv 後もタスクをまたいで残る タスクの前後で確認し、削除する
一時ファイル 権限が広すぎる、または異常終了後に残る 権限を 600 に設定し、trap で削除する
ログ Shell トレースによってコマンド引数が出力される 秘密情報を扱う工程では set -x を無効にする

監査スクリプトで確認すべきなのは「特定のマーカーが見えるかどうか」だけです。環境のスナップショットをビルド成果物として保存してはいけません。完全な env、export、ps eww の出力自体が、新たな漏えい源になる可能性があります。

ランダムなマーカーで継承を検証する

実際のトークンを検証に使ってはいけません。使い捨てのマーカーを生成し、短時間だけ動作する子プロセスを起動して、そのマーカーが見えるかどうかを出力せずに判定します。次の検査ではマーカーの内容はログに出ませんが、終了コードによってパイプライン内で継承が発生したかどうかを判断できます。

set -euo pipefail

marker="$(/usr/bin/uuidgen)"
export CI_SECRET_PROBE="$marker"

/bin/sleep 20 &
child_pid=$!

if /bin/ps eww -p "$child_pid" -o command= | /usr/bin/grep -q "$marker"; then
  result=1
else
  result=0
fi

kill "$child_pid" 2>/dev/null || true
wait "$child_pid" 2>/dev/null || true
unset CI_SECRET_PROBE

test "$result" -eq 1

このテストで確認できるのは、デフォルトの継承が実際に発生するという事実です。本番ログで全プロセスを繰り返しスキャンすべきだという意味ではありません。正式な検証では、固定の変数名とランダムな値を組み合わせ、記録する結果は「成功」または「失敗」だけにします。

launchd の残留を確認する

タスク用の秘密情報を launchctl setenv で注入してはいけません。そのライフサイクルが 1 回の Shell セッションより長くなる可能性があるためです。過去のスクリプトでこの方法を使用していた場合は、値を出力せずに状態を確認してから、次を実行します。

launchctl unsetenv CI_RELEASE_TOKEN
test -z "$(launchctl getenv CI_RELEASE_TOKEN)"

この確認は、タスクの開始時とクリーンアップ時にそれぞれ 1 回ずつ実行します。runner が専用ユーザーとして動作している場合は、現在のリモートセッションだけでなく、そのユーザーに対応する launchd ドメイン内で実行する必要があります。

ビルド用の最小環境を構築する

env -i を使えば空の環境からコマンドを起動できますが、無条件にすべてを消去してはいけません。Xcode ツールチェーンでは通常、HOME、PATH、TMPDIR、ロケール設定、明示的な開発者ディレクトリが必要です。まずバージョン照会で環境を検証し、その後に完全なビルドを実行します。

set -euo pipefail

job_tmp="$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/mac-ci.XXXXXX")"
trap '/bin/rm -rf "$job_tmp"' EXIT HUP INT TERM

/usr/bin/env -i \
  HOME="$HOME" \
  PATH="/usr/bin:/bin:/usr/sbin:/sbin" \
  TMPDIR="$job_tmp/" \
  LANG="en_US.UTF-8" \
  DEVELOPER_DIR="/Applications/Xcode.app/Contents/Developer" \
  /usr/bin/xcrun xcodebuild -version

実際のプロジェクトが Ruby、Node.js、パッケージマネージャーに依存する場合は、それぞれの実行ファイルがあるディレクトリを PATH に個別に追加します。ログイン Shell の環境全体を復元してはいけません。変数を追加するたびに、どのコマンドがその変数を必要とするのかを明記します。

ビルドとリリースを別々の権限領域に分離する

ビルド工程ではアーカイブと検証用ファイルだけを生成し、リリース用の資格情報には触れないようにします。リリース工程では、検証済みの成果物を読み込み、アップロードコマンドに必要な変数だけを注入します。タスクの開始時に export CI_RELEASE_TOKEN を実行してはいけません。その後に動くすべてのスクリプトが値を継承してしまうためです。

set +x
CI_RELEASE_TOKEN="$release_token" \
  /usr/bin/env \
  RELEASE_ARTIFACT="$artifact_path" \
  ./ci/publish.sh
unset release_token
set -x

さらに安全なのは、publish.sh からアップロード用プロセスを 1 つだけ起動し、その終了を待って直ちにスクリプト自体も終了させる方法です。ツールが資格情報ファイルを必要とする場合は、タスク専用の一時ディレクトリを使用し、作成前に umask 077 を設定して、書き込み後の権限が 600 であることを確認します。ファイルをリポジトリ、ユーザーのデスクトップ、長期保存されるキャッシュディレクトリに置いてはいけません。

失敗時のハンドラーを制限する

失敗時のフックでは、環境、プロセス、ワークスペースの情報が収集されがちです。フィルタリングされていない env を実行したり、完全なコマンドラインをアーカイブしたりしてはいけません。収集を許可するのは、終了コード、ツールのバージョン、成果物のパス、ディスクの空き容量、許可リストで絞り込んだ変数名です。秘密情報を確認する必要がある場合は、ハッシュまたはランダムなプローブが現れたかどうかだけを比較し、元の値は保存しません。

クリーンアップ結果をタスクのゲートにする

タスクが成功しても、クリーンアップまで成功したとは限りません。終了工程では少なくとも、リリース変数が unset されていること、launchd に該当する名前が残っていないこと、一時ディレクトリが存在しないこと、テストおよびアップロードの子プロセスが終了していること、ログでコマンドトレースが有効になっていないことを確認します。1 つでも失敗した場合は、現在の runner を要確認状態としてマークし、秘密情報を伴うリリースタスクをそれ以上受け付けないようにします。

タスクごとに専用の TMPDIR を生成し、プロセスグループ単位でクリーンアップすることを推奨します。メインスクリプトだけを終了すると、シミュレータの補助プログラム、アップローダー、独自のデーモンが残る可能性があります。クリーンアップ後、実際の値を含まないランダムなプローブをもう一度実行し、新しい子プロセスから前工程のマーカーが見えないことを確認します。

最終的な目標は、パイプラインで環境変数を一切使わないことではありません。ビルド工程にはリリース用の秘密情報が存在せず、リリース工程では必要な 1 つのプロセスだけが秘密情報を参照でき、タスク終了後はプロセス、launchd、ディスクのいずれにも残らないという明確な境界を確立することです。この検査を runner の初期化スクリプトと終了スクリプトに組み込めば、以後のプロジェクトで作業者の記憶に頼る必要はなくなります。

よくある質問

CI の秘密情報を環境変数だけで管理すれば安全ですか?

十分ではありません。環境変数は子プロセスへ継承され、診断コマンドやデバッグ出力、常駐サービスから参照される可能性があります。必要な単一コマンドにだけ渡してください。

ジョブ終了後に環境変数が残っていないことを確認する方法は?

runner の親プロセス、launchd ドメイン、残存する子プロセスを変数名で検査し、ジョブ用一時ディレクトリの削除も確認します。実際の秘密値はログへ出力しません。

env -i を使うと xcodebuild が動かなくなりますか?

既定環境が消えるため、HOME、PATH、TMPDIR、LANG、DEVELOPER_DIR を明示する必要があります。バージョン確認と小規模ビルドで許可リストを検証してから適用します。

専用物理ノード

次のビルドをクラウド Mac mini で実行

構成、請求期間、ノードのリージョンを選び、専用物理マシンで開発・ビルド・自動化タスクを実行できます。

Mac miniを今すぐレンタル