トレースの取得
Takumi Runner は、ワークフロー実行中に発生するプロセス・ネットワーク・ファイル操作を eBPF で網羅的にキャプチャします。このページでは、取得されるイベントの種類、データ形式、および生データへのアクセス方法を説明します。
取得の仕組み
Takumi Runner のトレース取得は、Linux カーネルの eBPF(extended Berkeley Packet Filter)テクノロジーを基盤としています。eBPF を利用することで、ワークフロー内で実行されるユーザーコードに一切の変更を加えることなく、カーネルレイヤで VM 内での動作をキャプチャできます。
キャプチャされるイベント
Takumi Runner の eBPF トレーサーは、以下のシステムコールレベルのイベントを記録します。
| イベント種別 | 説明 | 記録されるフィールド |
|---|---|---|
process_exec | プロセスの実行 | PID、コマンド名、ファイルパス、引数 |
net_connect | ネットワーク接続 | PID、接続先アドレス、ポート、プロトコル |
dns_query | DNS ルックアップ | PID、ホスト名 |
file_open | ファイルのオープン | PID、パス、フラグ |
file_write | ファイルへの書き込み | PID、パス、書き込みバイト数 |
1 回のジョブ実行で、数千から数万件のイベントが記録されます。
トレースの用途
収集されたトレースは、以下のようなセキュリティ分析に活用できます。
- サプライチェーン攻撃の検知 として、
npm installやpip install中に不審な外部ホストへの通信が発生していないかを確認する - インシデント調査 として、特定のジョブ実行中に
curlやwgetが実行されたか、~/.netrcなどの認証情報を含むファイルにアクセスがあったかを追跡する - ベースライン分析 として、正常なビルドのトレースと異常なビルドのトレースを比較し、差分を特定する
トレースのデータ形式
トレースデータは JSONL(改行区切り JSON) 形式で保存されます。1 行が 1 イベントに対応し、各行は独立した JSON オブジェクトです。すべてのイベントには共通フィールドとして type(イベント種別)と timestamp(発生時刻、ISO 8601 形式)が含まれます。
以下に示すフィールド定義は、現時点での実装に基づく参考情報です。フィールドの追加・変更・削除が事前の通知なく行われる場合があります。
process_exec
プロセスの実行を記録します。
| フィールド | 型 | 説明 |
|---|---|---|
pid | number | プロセス ID |
comm | string | コマンド名 |
filename | string | 実行ファイルのパス |
args | string[] | コマンドライン引数 |
{
"type": "process_exec",
"timestamp": "2025-01-15T10:30:01Z",
"pid": 1234,
"comm": "npm",
"filename": "/usr/bin/npm",
"args": ["npm", "install"]
}
net_connect
ネットワーク接続を記録します。
| フィールド | 型 | 説明 |
|---|---|---|
pid | number | プロセス ID |
dst_addr | string | 接続先 IP アドレス |
dst_port | number | 接続先ポート番号 |
protocol | string | プロトコル(tcp / udp) |
{
"type": "net_connect",
"timestamp": "2025-01-15T10:30:02Z",
"pid": 1235,
"dst_addr": "104.16.23.35",
"dst_port": 443,
"protocol": "tcp"
}
dns_query
DNS ルックアップを記録します。
| フィールド | 型 | 説明 |
|---|---|---|
pid | number | プロセス ID |
hostname | string | 問い合わせたホスト名 |
{
"type": "dns_query",
"timestamp": "2025-01-15T10:30:02Z",
"pid": 1235,
"hostname": "registry.npmjs.org"
}
file_open
ファイルのオープンを記録します。
| フィールド | 型 | 説明 |
|---|---|---|
pid | number | プロセス ID |
path | string | ファイルパス |
flags | string | オープンフラグ(O_RDONLY など) |
{
"type": "file_open",
"timestamp": "2025-01-15T10:30:03Z",
"pid": 1235,
"path": "/home/runner/work/app/package.json",
"flags": "O_RDONLY"
}
file_write
ファイルへの書き込みを記録します。
| フィールド | 型 | 説明 |
|---|---|---|
pid | number | プロセス ID |
path | string | ファイルパス |
bytes | number | 書き込みバイト数 |
{
"type": "file_write",
"timestamp": "2025-01-15T10:30:04Z",
"pid": 1235,
"path": "/home/runner/work/app/node_modules/.package-lock.json",
"bytes": 2048
}
トレースの保管期間
トレースデータは、取得日から最低 90 日間 保管されます。90 日を経過したトレースデータは削除される場合があります。
サブスクリプションの解約時には、利用規約その他の定めに従ってトレースデータが削除されます。
保持期間の延長を希望される場合は、アカウント担当までお問い合わせくださ い。ご契約やご利用の状況により、ご要望にお応えできない場合があります。
生データへのアクセス
Shisho Cloud コンソールのジョブ詳細画面にある クエリ タブでは、トレースデータに対して SQL(DuckDB)を使った自由なクエリを実行できます。

また、各ジョブ実行のトレースデータを JSONL ファイルとしてダウンロードすることもできます。ダウンロードした生データは、独自のスクリプトやツールで自由に分析できます。

生データは JSONL 形式です。jq 等のツールで処理できます。
cat trace.jsonl | jq 'select(.type == "net_connect")'
クエリ例
以下のクエリはジョブ詳細画面の クエリ タブにそのままコピー&ペーストして実行できます。トレースデータはすべて単一の events テーブルに格納されており、イベント種別や各フィールドへは json_extract_string(data, '$.type') のように $.<フィールド名> で参照します。
Runner.Worker プロセスメモリの読み取り検出
GitHub Actions の Runner.Worker プロセスは、ジョブで利用する秘密情報(secrets.* や ${{ ... }} で展開される値など)を自身のアドレス空間上に保持しています。GitHub Actions を狙ったサプライチェーン攻撃(特に 2025 年 3 月の tj-actions/changed-files 事案)では、同じジョブ内で実行される悪意あるアクションが /proc/<Runner.Worker の pid>/mem や /proc/<Runner.Worker の pid>/maps を読み取ることでシークレットを窃取していました。正常なワークフロー実行では Runner.Worker の PID に対して /proc/<pid>/mem や /proc/<pid>/maps へアクセスするプロセスは基本的に存在しないため、このクエリにヒットがあればシークレット窃取の強い兆候と判断できます。
以下のクエリは、まず process_exec イベントから Runner.Worker の PID を取得し、その PID に対して /proc/<pid>/mem または /proc/<pid>/maps にアクセスしたイベントをすべて返します。/proc/self/maps はプロセスが自分自身のメモリマップを読み取る正当な用途(ランタイムによる自己イントロスペクションなど)で頻繁に参照されるため、明示的に除外しています。
WITH runner_pid AS (
SELECT json_extract_string(data, '$.pid') AS pid
FROM events
WHERE json_extract_string(data, '$.type') == 'process_exec'
AND json_extract_string(data, '$.filename') LIKE '%Runner.Worker%'
)
SELECT * FROM events
WHERE json_extract_string(data, '$.path') = concat('/proc/', (SELECT pid FROM runner_pid) ,'/mem')
OR (
json_extract_string(data, '$.path') = concat('/proc/', (SELECT pid FROM runner_pid) ,'/maps')
AND json_extract_string(data, '$.path') != '/proc/self/maps'
)