majormonitoring-gapverifiedfirsthand
スクリプトを編集したらファイアウォールが無言でpushを3日止めた
vaultのgitミラーを毎時pushする定期ジョブが、16ミリ秒で即座に接続失敗するようになり、9件のコミットが3日間リモートに届かなかった。
原因
Little Snitchは、通信するプロセスの系譜にあるシェルスクリプトのハッシュを同一性の根拠にする。スクリプトを編集したことでハッシュが変わり、確認のダイアログも出さずにその系譜の通信がすべて拒否された。さらにジョブは新しい変更があるときしかpushを試みない作りで、失敗したpushを再試行していなかった。
結果
9件のコミットが3日間pushされずに滞留した。その間、作業メモには「失敗しても次の回で自動的に再pushされる」という誤った記述が残っていた。
対策
pushをスクリプトから切り離し、launchdから署名済みの /usr/bin/git を直接実行する別ジョブにした。スクリプト側は通信せず、未pushのコミット数が閾値を超えたら通知するだけにした。
何が起きたか
Obsidianのvaultをgitミラーに写してpushする定期ジョブから、 「push失敗 要確認」の通知が来た。ログにはこうあった。
Failed to connect to github.com port 443 after 16 ms
16ミリ秒。ネットワークが瞬断したなら、タイムアウトまで待たされるはずだ。 実際、3日前に本物の瞬断でpushが落ちたときは151秒かかっていた。 今回は、接続を試みた瞬間に断られていた。
現場の混乱
3日前の失敗は、AIコーディングエージェントとの作業で「一過性の瞬断」と判定され、 作業メモには「push失敗時は次の毎時実行で自動的に再pushされるので、瞬断には自動で耐える」 と書かれた。これが誤りだった。
旧実装は git diff --cached --quiet の時点で、変更が無ければ無条件に exit 0 していた。
新しい変更が来ない限り、pushは二度と試みられない。しかもその日の「再試行で成功」は、
ターミナルから手でpushしたものだった。スクリプト経由のpushは、その時点ですでに拒否されていた。
犯人はLittle Snitchだった。
The identity check detected a modification of the program.
Therefore all of its connections were denied as a precaution.
Little Snitchは通信プロセスの系譜に含まれるシェルスクリプトのハッシュで同一性を判断する。 スクリプトは3日前の12時16分に編集されていた。rsyncの障害を直すためにスクリプトを書き換えた日だ。 その瞬間から、ダイアログも出さずに全通信が拒否されていた。
効かなかった対処は3つある。
- 該当するgithub.comの拒否ルールを削除した。
- Silent Modeがオフであることを確かめた。元からオフだった。
git-remote-httpの実行ファイルに許可ルールを作った。
子プロセスに許可を与えても、系譜の途中にある改変済みスクリプトによる拒否が優先された。
原因
2つの沈黙が重なった。ファイアウォールは、スクリプトの編集を「改変」とみなして 何も言わずに通信を止めた。ジョブは、失敗したpushを再試行しない作りなのに、 再試行すると信じられていた。拒否は無言で、再試行も無い。止まったことを知らせる経路がどこにも無かった。
対策
- pushを別ジョブに分け、launchdから
/usr/bin/gitを直接実行するようにした(15分ごと)。 gitはApple署名済みで中身が変わらないため、同一性の確認が崩れない。 ラッパースクリプトを噛ませると再発するので、必ずバイナリを直接呼ぶ。 - 元のスクリプトはrsync、秘密情報の検査、コミットまでを担当し、通信はしない。 未pushのコミット数を数え、閾値を超えたら通知する監視だけを行う。
- 教訓はひとつ。Little Snitchの環境では、通信するスクリプトを運用しない。 編集のたびに無警告で止まる。通信は署名済みのバイナリにやらせる。
出典
- 運営者自身が起こした事故の一次記録です。外部に公開された元記事はありません。