Cloudflare の費用は毎日自動でチェックしている。閾値を超えたら Slack に来る。
でも、設定の変更は誰も見ていなかった。 DNS のレコードを書き換えられても、Access のポリシーに bypass を足されても、気づく手段が無い。
実際、あるサービスの Access に「Bypass from Japan」というルールが残っていたのを、偶然見つけた。いつ足したのか分からない。偶然見つかるものは、偶然見つからないこともある。
監査ログは、読めなかった
まず考えたのは、Cloudflare のアカウント監査ログを API で読むことだ。誰が・いつ・何を変えたかが出る。
トークンの権限カタログに、それを読む項目が無い。 Access: Audit Logs という似た名前の権限はあるが、これは Access 専用で別物だ。account 階層の権限を全部並べて確認したが、アカウント監査ログを読む権限は選べない。
読むには Global API Key が要る。それは権限を絞ったトークンに移行するときに手放したものだ。監査ログのために全権の鍵を戻すのは本末転倒だった。
代わりに tofu plan
Cloudflare の設定は OpenTofu(Terraform)で管理している。61リソース。
tofu plan は、コードに書いてある状態と、Cloudflare 側の実物を全部突き合わせる。差分があれば出る。「誰が」は分からないが、「何が変わったか」は確実に出る。そして権限は既にある。plan に必要な読み取り権限は、apply のために持っているものと同じだ。
つまり監査ログの代わりに、状態の差分を毎朝取ればいい。
終了コードで判定する
tofu plan には -detailed-exitcode というオプションがある。
| 終了コード | 意味 |
|---|---|
| 0 | 差分なし |
| 2 | 差分あり |
| 1 | plan 自体が失敗(認証エラー、API 障害など) |
これをそのまま判定に使う。
- 2(差分あり)→ 🚨 を Slack へ。リソース名を添える
- 1(検知失敗)→ ⚠️ を Slack へ。監視自体が動いていないので、これも知らせる
- 0(差分なし)→ 黙る
「差分なしのときは黙る」は、そのままだと監視が死んでいるのと区別がつかない。だから最後の成功時刻をメトリクスに出して、36時間更新が無ければ Grafana が鳴らす。値のアラートと鮮度のアラートを1組にする、いつもの型だ。
タイマーは毎朝08:40。費用チェックの08:10とはずらしてある。
通知にはリソース名だけを載せる
差分があったとき、plan の出力をそのまま Slack に流したくなる。やらない。
plan の出力には、値が混ざる。 DNS のレコード値、Access のポリシーの中身、トークンの一部。Slack に流れると、そこに残る。
通知に載せるのはリソース名だけにした。cloudflare_record.grafana_omikuji_dev のような名前が来れば、何が変わったかは分かる。中身は自分で plan を回して見る。
実証した
作ったら、必ず発火させる。
DNS レコードの comment を API で書き換えて、わざとドリフトを作った。plan を回すと差分を検知し、🚨 が Slack に届き、リソース名が出た。tofu apply で戻したら、検知が0に戻った。
両方向を確認した。 差分があれば鳴る、無くなれば止まる。片方だけ確認して安心すると、鳴りっぱなしか、鳴らないかのどちらかになる。
運用の注意
自分で tofu apply する前後は、当然 🚨 が出る。 心当たりがあるかは人間が判断する。これは仕様で、apply の前に「これから変える」と分かっているので、通知が来ても驚かない。
直し方は「Cloudflare 側を手で戻す」ではなく tofu apply。 意図した状態はコードのほうで、Cloudflare 側の実物はそれに合わせるものだ。手で戻すと、次の plan でまた差分が出る。
まだ見えていないもの
Terraform で管理していないものは、この方法では見えない。権限が足りず、未監視のまま残っているものがある。
| 監視したいもの | 必要な権限 |
|---|---|
| API トークンの新規作成 | User → API Tokens Read / Account → Account API Tokens Read |
| アカウントメンバーの追加 | User → Memberships Read |
どちらも「攻撃者がまずやりそうなこと」だ。トークンを1つ作られたら、以後はそのトークンで何でもできる。必要になったら権限を足す。
まとめ
- 費用の監視と、設定の監視は別物。 費用を毎日見ていても、DNS を書き換えられたことには気づけない
- Cloudflare のアカウント監査ログは、API トークンの権限カタログに読む項目が無い。Global API Key が要る
- 代わりに
tofu plan -detailed-exitcodeを毎朝回す。「誰が」は分からないが「何が変わったか」は確実に出る。権限は既にある - 終了コード 0 / 1 / 2 で「差分なし / 検知失敗 / 差分あり」を分ける。検知失敗も知らせる
- 黙るアラートには鮮度のアラートを添える
- 通知にはリソース名だけ。plan の出力には値が混ざる
- 作ったら発火させる。差分を作って鳴らし、戻して止まるまで確認する
- 直し方は
tofu apply。コードが正で、実物を合わせる
監査ログが読めないなら、状態の差分で代替する。「誰が」は諦めて「何が」だけ取る。それで十分だった。