メインコンテンツまでスキップ

トレースの取得

Takumi Runner は、ワークフロー実行中に発生するプロセス・ネットワーク・ファイル操作を eBPF で網羅的にキャプチャします。このページでは、取得されるイベントの種類、データ形式、および生データへのアクセス方法を説明します。

取得の仕組み

Takumi Runner のトレース取得は、Linux カーネルの eBPF(extended Berkeley Packet Filter)テクノロジーを基盤としています。eBPF を利用することで、ワークフロー内で実行されるユーザーコードに一切の変更を加えることなく、カーネルレイヤで VM 内での動作をキャプチャできます。

キャプチャされるイベント

Takumi Runner の eBPF トレーサーは、以下のシステムコールレベルのイベントを記録します。

イベント種別説明記録されるフィールド
process_execプロセスの実行PID、コマンド名、ファイルパス、引数
net_connectネットワーク接続PID、接続先アドレス、ポート、プロトコル
dns_queryDNS ルックアップPID、ホスト名
file_openファイルのオープンPID、パス、フラグ
file_writeファイルへの書き込みPID、パス、書き込みバイト数

1 回のジョブ実行で、数千から数万件のイベントが記録されます。

トレースの用途

収集されたトレースは、以下のようなセキュリティ分析に活用できます。

  • サプライチェーン攻撃の検知 として、npm installpip install 中に不審な外部ホストへの通信が発生していないかを確認する
  • インシデント調査 として、特定のジョブ実行中に curlwget が実行されたか、~/.netrc などの認証情報を含むファイルにアクセスがあったかを追跡する
  • ベースライン分析 として、正常なビルドのトレースと異常なビルドのトレースを比較し、差分を特定する

トレースのデータ形式

トレースデータは JSONL(改行区切り JSON) 形式で保存されます。1 行が 1 イベントに対応し、各行は独立した JSON オブジェクトです。すべてのイベントには共通フィールドとして type(イベント種別)と timestamp(発生時刻、ISO 8601 形式)が含まれます。

警告

以下に示すフィールド定義は、現時点での実装に基づく参考情報です。フィールドの追加・変更・削除が事前の通知なく行われる場合があります。

process_exec

プロセスの実行を記録します。

フィールド説明
pidnumberプロセス ID
commstringコマンド名
filenamestring実行ファイルのパス
argsstring[]コマンドライン引数
{
"type": "process_exec",
"timestamp": "2025-01-15T10:30:01Z",
"pid": 1234,
"comm": "npm",
"filename": "/usr/bin/npm",
"args": ["npm", "install"]
}

net_connect

ネットワーク接続を記録します。

フィールド説明
pidnumberプロセス ID
dst_addrstring接続先 IP アドレス
dst_portnumber接続先ポート番号
protocolstringプロトコル(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 ルックアップを記録します。

フィールド説明
pidnumberプロセス ID
hostnamestring問い合わせたホスト名
{
"type": "dns_query",
"timestamp": "2025-01-15T10:30:02Z",
"pid": 1235,
"hostname": "registry.npmjs.org"
}

file_open

ファイルのオープンを記録します。

フィールド説明
pidnumberプロセス ID
pathstringファイルパス
flagsstringオープンフラグ(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

ファイルへの書き込みを記録します。

フィールド説明
pidnumberプロセス ID
pathstringファイルパス
bytesnumber書き込みバイト数
{
"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 日を経過したトレースデータは削除される場合があります。

サブスクリプションの解約時には、利用規約その他の定めに従ってトレースデータが削除されます。

info

保持期間の延長を希望される場合は、アカウント担当までお問い合わせください。ご契約やご利用の状況により、ご要望にお応えできない場合があります。

生データへのアクセス

Shisho Cloud コンソールのジョブ詳細画面にある クエリ タブでは、トレースデータに対して SQL(DuckDB)を使った自由なクエリを実行できます。

クエリタブ

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

info

生データは gzip 圧縮された JSONL 形式です。gunzip で展開してから jq 等で処理できます。

gunzip trace.jsonl.gz
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'
)