エンジニアリング記事

クラウドMac CIの外向き通信を監査し、再確認できる基準を作る

クラウドMac CIの外向き通信を監査し、再確認できる基準を作る

これまで安定していた CI パイプラインが突然見慣れないアドレスへアクセスし始めても、すぐに遮断してはいけません。ビルドツール、依存関係リゾルバー、テストプロセス、バックグラウンド更新タスクはいずれも外向き接続を発生させる可能性があります。効果的に調査するには、再現可能なビルドを 1 回選び、時間範囲を限定したうえで「どのプロセスが動作していたか」「接続先はどこか」「いつ接続が発生したか」を同時に記録し、その結果を基準として整理します。

比較可能なサンプリング期間を固定する

依存関係のインストールが完了し、ワークスペースがクリーンで、ほかのチームのジョブが動いていない時間帯を選びます。コミット ID、ビルドコマンド、macOS のバージョン、Xcode のバージョン、開始時刻を記録してください。最初のサンプリングではジョブを 1 つだけ実行し、複数のパイプラインがプロセスを共有して接続元を特定できなくなる事態を避けます。

データは 2 組用意することを推奨します。一方では最小構成のビルドを実行し、もう一方ではすべてのテストを実行します。前者では依存関係の解決やコンパイルに伴う接続を確認でき、後者ではさらにシミュレータ、テストサービス、成果物のアップロードに関する通信も対象になります。各組を 2 回ずつ実行すると、通常は 2 回目の結果から、初回だけ発生するダウンロードと定常的なビルド通信を区別できます。

外向き通信の基準は、恒久的に有効な IP アドレスの一覧ではありません。「既知のジョブが、既知の段階で、なぜ特定種類のサービスへアクセスするのか」を示す証拠です。

3 層の証拠で接続を観測する

プロセスと現在の接続

lsof は、サンプリングした瞬間にどのプロセスが接続を維持しているかを確認するのに適しています。アドレスを数値で表示すれば、逆引きによって余分な DNS 通信が発生するのを防げます。

sudo lsof -nP -iTCP -sTCP:ESTABLISHED \
  | awk 'NR == 1 || $1 ~ /xcode|swift|git|ruby|node|curl/'

プロセス名だけで絞り込み、元の結果を破棄してはいけません。まず完全なスナップショットを保存し、その後で読みやすいサブセットを作成します。ビルドスクリプトがシステムツールを介してダウンロードを行う場合、プロセス名にプロジェクト固有のキーワードが含まれるとは限りません。

トラフィックと短時間の接続

nettop はプロセス別に集計したリアルタイムの情報を表示し、tcpdump は数十ミリ秒で終了する TCP ハンドシェイクも記録できます。リモートから実行する場合は外向き SYN のみを取得し、ペイロードを収集しないことで、ログに含まれる業務データを減らせます。

nettop -P -L 1
sudo tcpdump -i any -nn -l \
  'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'

パケットキャプチャを始める前に、現在の SSH セッションのリモートアドレスを確認してください。フィルタ条件を誤ると大量のノイズが生じる可能性はありますが、観測コマンド自体がファイアウォールルールを変更することはありません。

アーカイブ可能な収集バンドルを作成する

次のスクリプトは、システム情報、ルーティング、接続スナップショット、60 秒間のハンドシェイク記録を保存します。実行前に、ビルドを開始直前の状態まで準備してください。スクリプトを起動した後、別のセッションでビルドを実行します。

#!/bin/zsh
set -euo pipefail

OUT="${1:-$HOME/ci-egress-$(date -u +%Y%m%dT%H%M%SZ)}"
mkdir -p "$OUT"

date -u > "$OUT/time.txt"
sw_vers > "$OUT/system.txt"
xcodebuild -version > "$OUT/xcode.txt"
route -n get default > "$OUT/default-route.txt"
scutil --dns > "$OUT/dns.txt"
sudo lsof -nP -i > "$OUT/lsof-before.txt"

sudo tcpdump -i any -nn \
  'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0' \
  -w "$OUT/outbound-syn.pcap" &
CAPTURE_PID=$!

sleep 60
sudo kill -INT "$CAPTURE_PID"
wait "$CAPTURE_PID" || true

sudo lsof -nP -i > "$OUT/lsof-after.txt"
nettop -P -L 1 > "$OUT/nettop.txt"
printf '%s
' "$OUT"

ビルドが 60 秒を超える場合は、1 回のキャプチャを際限なく延長するのではなく、「依存関係の解決、コンパイル、テスト、アーカイブ」の各段階に分けてサンプリングします。段階別の結果があれば、新しい接続がどの処理から発生したのかを特定しやすくなります。

接続先を通信基準として整理する

見慣れない IP アドレスを見つけただけで、異常だと判断してはいけません。まず、プロセス、ポート、接続が発生した段階、プロジェクト設定を組み合わせて分類します。動的な配信ネットワークではアドレスが頻繁に変わることがあるため、基準では用途と検証可能なドメイン名を中心に扱い、IP アドレスはそのサンプリング時点の証拠としてのみ残します。

| カテゴリ | 記録すべき証拠 | 対応方法 | |---|---|---| | ソースコードと依存関係 | プロセス、ドメイン名、ビルド段階 | ロックファイルとリポジトリ設定を照合 | | テストサービス | テストケース、ポート、継続時間 | 想定された統合テストか確認 | | 成果物の転送 | アップロードスクリプト、送信先、ファイル形式 | アーカイブ段階だけで発生するか確認 | | システムのバックグラウンド通信 | システムプロセス、発生時刻 | ビルド通信とは分けて記録 | | 不明な接続 | 完全なコマンドライン、親プロセス、キャプチャ時刻 | マージを保留し、再現して証拠を収集 |

不明なプロセスについては、さらに ps -o pid,ppid,user,start,command -p PID を実行し、親プロセスも確認します。接続が一度しか発生しない場合は、人の記憶に頼るのではなく、パケットキャプチャと CI ログの時刻を UTC に統一して照合します。

よくある誤判定と安全上の境界

最もよくある誤判定は、動的 IP アドレスを固定された識別子として扱うことです。次に多いのは、ビルド終了後の lsof だけを取得することで、その時点では短時間の接続がすでに消えています。3 つ目は、リモートノードで拒否ルールを直接有効にすることです。ローカルコンソールで検証していないルールは、SSH、DNS、依存関係のダウンロードまで同時に遮断するおそれがあります。

より安全な順序は、観測、分類、再現、レビューを行い、最後に制限を適用することです。外向き通信を厳しく制限する必要がある場合は、既存の管理用接続と DNS 経路を先に確保し、ロールバック用コマンドを用意したうえで、重要度の低い単一ジョブで検証します。ファイアウォールルール、プロキシ設定、ビルドスクリプトは必ずバージョン管理に含め、ノード上だけに残してはいけません。

日常的な確認に基準を組み込む

ツールチェーン、依存関係の取得元、アップロード手順を変更するたびに、同じサンプリングを再実行します。比較時には IP アドレスの行数ではなく、新しく追加された「プロセス—接続先—段階」の組み合わせに注目します。許可項目には、担当者、用途、再確認の条件を明記してください。説明できない接続を、注記だけで恒久的に許可してはいけません。

最終的には、収集スクリプト、元のファイル、分類表、対応するコミット ID を保存します。これにより、異常なダウンロード、ビルドの遅延、認証情報の外部送信が疑われる事象が発生した際、チームは通常の通信を一から推測するのではなく、実際の差分から調査を始められます。

よくある質問

取得した送信先IPをそのまま許可リストにできますか?

推奨しません。配布基盤や依存関係の取得先は動的IPを使うことがあるため、まず用途とドメイン単位で分類し、安定した出口制御方式を選びます。

lsofとtcpdumpで接続数が一致しないのはなぜですか?

lsofは取得時点で開いている接続だけを示します。tcpdumpは計測中に終了した短い接続の開始も記録するため、通常は結果が一致しません。

OrbVPS クラウドMac

専用のApple Silicon物理ノードでビルドを実行

機種、リージョン、レンタル期間を選んでノードを設定できます。実際の利用可否は、管理コンソールにリアルタイムで表示される情報をご確認ください。

クラウドMacを今すぐレンタル