curl は 200 を返した。ターミナルに 200 の一文字目が出た瞬間に、隣の瀬戸が小さくガッツポーズをして、ヘッドセットの片耳を外した。
「やっと返ってきましたね。じゃあ、もう直ったって顧客接点部門に伝えていいですか」
あなたは口を開きかけて、止めた。手元の画面はまだ走り終わっていない。show policy-map type inspect interface outside の出力は、向こうの装置からゆっくりカウンタが転がってくる途中で、Web サイトトップ側に投げた二つ目の curl はまだ応答を待っている。tcpdump のキャプチャは Application Data の手前で止まっていて、最後の数行が読めるところまで来ていない。
「200 が一回返っただけよ」
横の席から、氷室の声が落ちてきた。新人研修担当として運用1チームに常駐している設計課のシニアで、いつもの低くて速い声だ。
「黙って落ちてた通信を表に出すって、curl を一発打って戻したことじゃない。今ここで終わると、Web サイトトップ側に何かしらの副作用が出ても気づかない、利用者側がまだ別エンドポイントを叩いていても気づかない、次に同じ silent drop が来てもまた気づかない。あなたが氷室の机に置く報告書を、その三つ全部に答えられる形で残すまでが今日の仕事よ」
瀬戸が「つまり、curl が 200 を返したのは、復旧確認の一行目ってことですか」と言いかけて、「言い切ろうとしないで」と即座に叩かれている。


直す前にそろえる条件
申告は顧客接点部門の窓口経由で続いている。顧客側は別エンドポイントで業務を回している状態で、今日の通常運用への戻しを判断するために、こちら側で何が起きていて、何を変えて、本当に戻したのかを返す必要がある。EI までで、packet capture が TLS の Client Hello のあとで止まること、show policy-map type inspect tls の drop counter が「unsupported-extension」として 47 増えていること、show conn long で対象 session が inspect tls active のまま応答方向だけ止まっていること、openssl s_client の errno=104 でクライアント側からも同じ瞬間に切れていること、対象 IP/port 向け ACL は permit hit だけで deny エントリも logging 抑制もないことを観測した。Web サイトトップ側は同じ Firewall を経由しているのに通っている。
ここまでで言えるのは、Firewall の inspect が、対象 API への TLS handshake を許可ログを残さず drop している可能性が高い、という観測の確度だ。「inspect rule の更新版が必要なのか、対象通信を inspect から除外する例外設定で十分なのか」は要件側との合意が残っていて、drop が始まった時期とベンダー側 inspect 仕様変更履歴の対応も、まだ突合できていない。
瀬戸が、メモを開いたまま言った。
「unsupported-extension まで原因が見えてるんですよね。だったら、inspect rule そのものを新しい仕様に更新しちゃえば、もう同じ silent drop は起きないし、報告書も『更新しました』で一行で済むんじゃないですか」
「で、Web サイトトップ側にも影響が出たら、あなたが朝礼で泣くのね」
氷室はディスプレイから目を離さずに返した。
「inspect rule 全体の更新は、Firewall 配下の全通信が一回 inspect の評価ロジックを通り直すことになるの。Web サイトトップを含む他通信に対しても影響範囲が伸びる。ベンダーの更新告知と運用側 inspect 設定の突合、要件側合意、検証環境での事前確認が要る。何分後にそれ全部そろうの」
「……今日中には無理ですね」
「無理だから、今日やるのは別の選択肢。観測されてる silent drop は、対象 API への TLS 接続というすごく狭い範囲だけ。だったら今日の修正範囲も、その狭い範囲に合わせる。policy-map type inspect pm-outbound の中で、対象 API(10.20.30.10:443)向けの TLS 接続だけを inspect 対象外として pass させる class を、cm-https よりも前に置く。これだけ。Firewall 全体を再起動するのは論外。単一通信パターン起因の障害に、配下通信をまとめて落とす変更は当てない」
瀬戸が口を閉じた。ここで、変更前に決めておくものを並べた。承認者は運用リーダー。修正は対象 API への TLS 接続を inspect 対象外とする例外設定だけで、inspect rule 全体の更新や class-map 改変はやらない。pm-outbound の class 評価順は、新しく追加する cm-api-bypass を cm-https より前に置く。
切り戻し条件も先に書き出す。変更後3分以内に、対象 API への curl が応答を返さない、社内開発端末からの再現でも応答が戻らない、Web サイトトップ側で新たな影響が出る、別の inspect 経路で drop counter が想定外に増える、このどれかが起きたら、追加した class、class-map、ACL を no で順に取り消して、変更前状態へ戻したうえで上位へ上げる。
「3分って短すぎないですか」と瀬戸が言う。
「新規 TCP 接続の class 振り分けは、新しい session が来てから走るの。3分のあいだに curl を投げて応答が戻らなかったら、新規 session でも inspect 側で止まるってこと。戻る兆しがない時間を5分も10分も伸ばしたら、その間ずっと利用者は別エンドポイントを叩き続ける」
「つまり、3分は戻る兆しが見えるかどうかの制限時間ってことですか」
「やっとまともな文になったじゃない」
承認は、運用リーダーを 30 秒だけ呼んで取った。
例外設定を、対象通信のぶんだけ
変更前の running-config を保存してから、pm-outbound の評価順を確認した。cm-https の class が inspect を呼んでいて、対象 API への TLS handshake もここに入って drop されている。
configure terminal
ip access-list extended ACL-API-BYPASS
permit tcp any host 10.20.30.10 eq 443
class-map type inspect match-all cm-api-bypass
match access-group name ACL-API-BYPASS
policy-map type inspect pm-outbound
class type inspect cm-api-bypass
pass
end
ACL-API-BYPASS は対象 API への TCP 443 だけを permit する。cm-api-bypass はその ACL に match した通信だけを拾う。pm-outbound の中で cm-api-bypass を cm-https より前に置くと、対象 API への TLS 接続は pass 側へ抜け、それ以外の HTTPS 通信は引き続き cm-https 側で inspect を受ける。
「pass ってことは、対象通信については inspect の保護も外れますよね」
瀬戸が、画面のコマンドを指で追いながら言った。
「外れるわよ」
氷室は当たり前のように頷いた。
「だから ACL を permit tcp any host 10.20.30.10 eq 443 に絞ってあるの。対象 API への TCP 443 だけ。それ以外の通信は何ひとつ pass しない。bypass の影響範囲を、観測された silent drop の範囲とぴったり合わせる。広げない」
「つまり、『inspect の例外を作る』っていうのは、装置の inspect を弱めるんじゃなくて、観測で見えた drop の範囲だけを inspect から外に出す、ってことですか」
「言いたいことは合ってる。次は、本当に外に出てるかを確かめる番」
投入後、変更後の running-config を再保存して、時計を見た。3分のうちに、戻る兆しが見えるかどうかだ。
戻った、を言える材料
手元のターミナルに、すぐに必要な確認を並べた。
$ curl -sS -o /dev/null -w '%{http_code}\n' --max-time 10 https://api.example.jp/v2/health
200
$ curl -sS -o /dev/null -w '%{http_code}\n' --max-time 10 https://www.example.jp/
200
対象 API は 200 を返した。Web サイトトップも 200 を返したままだ。同じ Firewall を通っている他の HTTPS 通信が、変更直後に新たに止まるような兆しは出ていない。
「200 が二つ出ましたよ」と瀬戸。
「それ、一回ずつね。許可ログだけ見て Firewall を白とみなしてた運用と、ほぼ同じ穴を空けようとしてるの、自覚ある?」
「あ……一回成功しただけで終わりにしようとしてました」
「次は装置側を見る」
show policy-map type inspect interface outside をもう一度打った。cm-api-bypass の class のところで Packets / Bytes が増えていて、対象通信が cm-api-bypass 経由で pass 側へ流れていることが、装置側から確認できる。cm-https の drop counter は、変更前に 47 まで増えていた数値からこれ以上動いていない。対象 API へ向けて新しく投げた curl の通信が、もう inspect の drop 側に積み上がっていない、という意味だ。
「Web サイトトップ側はどうなんですか。あっち側の通信が cm-https から外れたわけじゃないですよね」
「外してない。cm-https の inspect は今もそのまま動いてるから、Web サイトトップ側の TLS は引き続き inspect されてる。cm-https の Packets / Bytes は変更前と同じペースで増えてる。drop counter は新規ぶんが乗ってない。Web サイトトップ側だけ別経路の class を新設して評価順を組み替えた、というような細工はしてない。だから、Web サイトトップ側に副作用は出ないはず、じゃなくて、出ていないと装置のカウンタで言える」
続けて、対象 API 向けの packet capture を取り直した。Client Hello、Server Hello、その後の Application Data まで、handshake が最後まで通っているのが見える。EI の段階では Client Hello のあとで止まっていたところだ。
最後に、顧客接点部門の窓口へチャットを返した。「顧客側で公開 API への呼び出しが応答を返すようになったか」「別エンドポイントなどの暫定運用を通常運用へ戻してよいか」を聞いた。窓口からは「応答が戻ったことを確認」「通常運用へ戻す」と短く返ってきた。
監視側の追加申告は 0 件のまま、Web サイトトップを含む他通信への追加影響も観測されていない。
「これ、curl だけだったら言えなかったやつですよね」
瀬戸が、画面を行ったり来たりしながら言った。
「対象 API の curl が 200 になっただけだと、Web サイトトップ側に副作用が出てないか、cm-https 側で別の drop が起きてないか、対象通信が本当に inspect を抜けて handshake 最後まで通ってるのか、顧客側で別エンドポイントを通常運用へ戻していいのか、ひとつも言えないですね」
「やっとまともな文になったじゃない」
「一回 200 が返った瞬間に終わりにしたくなった自分、ちょっとどうかしてました」
「自覚があれば次は同じ穴を踏まない。次は氷室の机に持ってくるやつね」
氷室の机に残す
報告書の親階層は、復旧した事実 / 原因として言える範囲 / まだ言えない範囲 / 実施した修正 / 検証結果 / 記録へ戻す事項、の六つで固定する。顧客接点部門には、業務影響が解消したこと、暫定運用から通常運用へ戻してよいこと、追加連絡の有無だけを返してある。
「復旧した事実」のところには、対象 API への TLS handshake が応答を返すようになったこと、Web サイトトップを含む他通信が変更直後も通っていること、顧客接点部門窓口に通常運用への戻しを確認したことを書く。「直った」とだけ書くと、何をもって直ったとしたのかが落ちる。
「原因として言える範囲」は、Firewall の inspect 設定が、対象 TLS handshake パターンに対して許可ログを残さず drop していた、という観測の範囲までだ。装置側 drop counter の last drop reason: unsupported-extension、変更前の packet capture が Client Hello の後で止まっていたこと、変更前の cm-https 経由通過時に drop counter が同期して 47 増えたこと、対象 API への ACL は permit のみで deny も logging 抑制もなかったこと、この四つで原理面の説明を支える。
「まだ言えない範囲」を空欄にしない。drop が始まった時期と、ベンダー側の inspect 仕様変更履歴との対応関係は、突合が終わっていない。今日の段階で「ベンダーが何月何日のリリースで挙動を変えた」と書くと、確かめていないことを書いたことになる。
「実施した修正」は、暫定対処として、対象 API への TLS 接続を inspect 対象外とする例外設定を最小範囲で追加した、と書く。cm-https の inspect rule そのものは触っていない、ということも同じ段で言う。恒久対処の候補として、ベンダー更新告知との突合を踏まえた inspect rule の更新、許可ログ依存の運用に対する drop counter / capture 併用確認手順の運用手順整備、inspect 仕様追従プロセス(ベンダー更新告知と運用側 inspect 設定の突合)の変更管理組み込みを、別途調整する方向として残す。
「検証結果」のところは、対象 API への curl 応答、Web サイトトップへの curl の維持、show policy-map type inspect のカウンタ(cm-api-bypass の通過、cm-https の drop が新規に積み上がっていないこと)、対象通信の packet capture が handshake 最後まで観測できたこと、顧客接点部門窓口の利用者確認、追加申告と他通信影響の監視を並べる。
「記録へ戻す事項」を、最後に書く。許可ログだけで Firewall を白とみなす運用を見直し、drop counter と capture を併用する確認手順を運用手順に追加する。inspect 仕様の追従プロセス(ベンダー更新告知と inspect 設定の突合)を変更管理の恒久対処候補として残す。今日の暫定対処と、ベンダー突合・恒久対処候補の境界を明示する。
瀬戸が、報告書の下書きを横から覗き込んできた。
「『まだ言えない範囲』のところ、本当に書いておくんですか。復旧してるのに、知らないって書いてるみたいで」
「知らないんじゃなくて、まだ確かめてない、と書いてるの」
氷室は手元のペンで「まだ言えない範囲」の段を軽く叩いた。
「ここを空欄にしたら、次に同じ silent drop が来たときに、報告書を読んだ人が『前回ここまで分かってたなら、今回はもっと早く特定できるはず』って動く。実際は誰も突合してないのに、突合済みみたいに見えてしまう。次の障害対応の足を引っ張るのよ」
「つまり、『まだ言えない範囲』を残すのは、次の同じ silent drop に対して、次の人が早く動けるようにするためですか」
「だから消さない。『記録へ戻す事項』に許可ログ依存の運用見直しを書くのも同じ理由。次に同じ silent drop が来たときに、誰かが drop counter と capture を当たり前に重ねて見るようにする。今日の経験を、運用手順と変更管理の側に戻すのはそのため」
「やっとまともな文になりました」
「それ、私が言うやつね」
報告書の下書きをもう一度、上から読み直した。
復旧した事実、原因として言える範囲、まだ言えない範囲、実施した修正、検証結果、記録へ戻す事項。六つの段が、それぞれ別の問いに答えていて、ひとつ消すと、その問いに対して次に動く人が判断できなくなる。
curl が 200 を返した一行目から、ここまで。unsupported-extension の drop が止まっていることは、装置の counter で確かめた。Web サイトトップに副作用が出ていないことは、装置の counter と curl で確かめた。顧客側で通常運用へ戻してよいことは、窓口確認で確かめた。ベンダー仕様変更がいつ起きたのかは、今日は確かめていない。確かめていないことは、確かめていないと書いた。
それを、氷室の机の上に置いた。

