はじめに
こんにちは。プラットフォームエンジニアリングチームに所属している徳富(@yannKazu1)です。
VPC Flow Logs に、VPC 内のサーバーがインターネット上の IP の 6379 番ポートと通信している行が記録されているのを見つけました。6379 は Redis のポートなので、字面だけ読むと「インターネット側に Redis がいて、そこに繋ぎに行っているのか?」と読めてしまいます。
この記事では、このログをパケットのヘッダーとしてどう読むか、そしてそこから立てた仮説を Flow Logs だけでどう裏付けたかを書いていきます。
きっかけ
VPC Flow Logs を眺めていたときのことです。
Redis のポートである 6379 に絞ってフィルタをかけたら、見慣れないパブリック IP が出てきました。
本記事の IP アドレスについて
実際の値は伏せ、すべてダミーに差し替えています。グローバル IP はドキュメント用に予約されている RFC 5737 のレンジ(
192.0.2.0/24、198.51.100.0/24、203.0.113.0/24)、CGNAT 内部のアドレスは RFC 6598 の100.64.0.0/10、VPC 内と宅内のアドレスは RFC 1918 のプライベートレンジから適当な値を置いています。ENI ID と ISP 名も伏せました。
srcaddr dstaddr dstport action 10.0.1.100 203.0.113.45 6379 ACCEPT
10.0.1.100 は VPC 内のプライベート IP、203.0.113.45 はインターネット上のパブリック IP です。つまりこの行をそのまま読むと、VPC 内から外部の IP の 6379 番ポートへ通信していることになります。
6379 は Redis のポートです。となると「知らないうちに外部の Redis に繋ぎに行っているのか?」「何かに乗っ取られて外のキャッシュにデータを投げているのか?」という絵が浮かんでくるログです。少なくとも、VPC 内で使っている Redis は VPC に閉じているはずなので、この行の存在自体が説明できない。思わず手が止まる 1 行だと思います。
パケットレベルで考えれば、当たり前だった
ここで思い出すべきは、Flow Logs がどのレイヤーの記録なのかです。
Flow Logs に残るのは L3/L4、つまり IP ヘッダーと TCP ヘッダーの中身だけです。入っているのは「送信元 IP / 送信元ポート / 宛先 IP / 宛先ポート」の 4 つ組といったメタ情報だけです。
この目で読み直すと、結論は簡単です。
この ENI が ALB のものだと仮定すると、クライアントの送信元ポートが 6379 なら、戻りのパケットはその 6379 宛になります(ALB であることは後述のログで確認しています)。改めて送信元・宛先のポートを両方一緒に出力してみると、内部側は 443 番でした。TCP は行きと戻りで送信元と宛先が入れ替わるだけなので、この行は
203.0.113.45:6379 → 10.0.1.100:443
の戻りと考えれば一目瞭然です。6379 を名乗っているのはクライアント側で、6379 は宛先ポートでなく送信元ポート。接続ごとに振られる使い捨ての番号で、Redis とは関係ありません。
しかもクライアントの送信元ポートは、端末から ALB に届くまでに何度も NAT で付け替えられます。そのどこかで 6379 が選ばれるのは、十分にあり得るし、何も不思議ではないシナリオです。
この流れを整理しておきます。
端末から ALB の ENI までに起きていること
一般ユーザーがブラウザでサイトを開いてから、そのパケットが ALB の ENI に届くまで、いくつもの機器を経由します。そしてそのたびにヘッダーが書き換えられます。
flowchart TD
A["端末(スマホ・PC)<br/>src 192.168.1.5:51000<br/>dst 192.0.2.10:443"]
B["宅内ルーター(NAT)<br/>src 100.64.12.7:40277<br/>dst 192.0.2.10:443"]
C["ISP の CGNAT<br/>src 203.0.113.45:6379<br/>dst 192.0.2.10:443"]
D["Internet Gateway<br/>src 203.0.113.45:6379<br/>dst 10.0.1.100:443"]
E["ALB の ENI<br/>ここで Flow Logs に記録される"]
A -->|"送信元を書き換え"| B
B -->|"送信元を書き換え → 6379 が誕生"| C
C -->|"宛先を書き換え"| D
D --> E
各段階を順に見ていきます。
1. 端末
ブラウザが接続を開くとき、OS が送信元ポートを 1 つ払い出します。これは「この通信を識別するための整理番号」であって、サービスの種類とは無関係です。Linux なら 32768〜60999、Windows なら 49152〜65535 から選ばれます(設定で変更可能)。
宛先は DNS で引いた ALB のパブリック IP と 443 番です。
2. 宅内ルーター
家庭内のプライベート IP のままでは外に出られないので、ルーターが NAT で送信元を書き換えます。このとき送信元ポートも付け替えられます。
3. ISP の CGNAT ← ここで 6379 が生まれる
IPv4 アドレスが枯渇しているため、ISP は複数の利用者で 1 つのグローバル IP を共有する構成を取ります。CGNAT と呼ばれる仕組みです。
同じグローバル IP を共有するので、「誰の通信か」はグローバル IP とポート番号の組み合わせで見分けることになり、ここでもう一度ポートが付け替えられます。しかも多数の利用者分をさばくため、well-known ポート以外の広い範囲を、ポートのプールとして使う実装があります。OS がエフェメラルポートとして使う 49152〜65535 の範囲に収まらず、登録済みポート帯の番号が払い出されることもあります。
そして今回、たまたま 6379 が払い出されました。この瞬間から、この通信はずっと「6379 番」を名乗って進むことになります。
4. Internet Gateway
VPC の入口です。ここでは宛先側が書き換えられます。端末は ALB のパブリック IP 宛に送っていますが、VPC 内部ではプライベート IP に変換されます。
5. ALB の ENI
到着。この時点のヘッダーがそのまま Flow Logs の 1 行になります。そして ALB が返事を返すと、送信元と宛先が反転したもう 1 行が記録されます。
sequenceDiagram
participant C as クライアント(CGNAT 通過後)<br/>203.0.113.45
participant E as ALB の ENI<br/>10.0.1.100
C->>E: 203.0.113.45:6379 → 10.0.1.100:443
Note over E: Flow Logs 1 行目(インバウンド)
E->>C: 10.0.1.100:443 → 203.0.113.45:6379
Note over E: Flow Logs 2 行目(アウトバウンド)
最初に目に入ったのは、この 2 行目でした。
行きのパケットの送信元ポートが、そのまま戻りのパケットの宛先ポートになる。TCP としてはごく当たり前の挙動ですが、これをポート番号だけで「6379 宛の通信」と読んでしまうと、一気に怪しい通信に見えてきます。
ログで裏付ける
とはいえ、ヘッダーの読み方だけでは「そう解釈できる」までです。ログで裏付けを取りました。確認したのは 2 点です。
- 対向のインバウンドフロー(
外部:6379 → 内部:443)が同じ ENI・同じ時間帯に存在するか - その ENI が ALB のものかどうか
対向フローは全件見つかった
該当した 3 件すべてに、ペアとなるインバウンドが存在しました。
| 方向 | 送信元 | 宛先 | パケット | バイト |
|---|---|---|---|---|
| → リクエスト | 203.0.113.45:6379 | 10.0.1.100:443 | 31 | 10,164 |
| ← レスポンス | 10.0.1.100:443 | 203.0.113.45:6379 | 39 | 18,401 |
| → リクエスト | 198.51.100.72:6379 | 10.0.3.200:443 | 4 | 285 |
| ← レスポンス | 10.0.3.200:443 | 198.51.100.72:6379 | 7 | 405 |
| → リクエスト | 198.51.100.72:6379 | 10.0.3.200:443 | 14 | 4,027 |
| ← レスポンス | 10.0.3.200:443 | 198.51.100.72:6379 | 16 | 7,670 |
4 つ組がきれいに反転していて、1 対 1 で対応しています。完全な TCP セッションのペアです。
通信は外から始まっている。VPC 内部から外の 6379 に繋ぎに行ったわけではない、という有力な裏付けになりました。
ENI は ALB のものだった
該当した 2 つの ENI に流れている他のトラフィックを見てみると、すべてが 外部IP:エフェメラルポート → 10.0.x.x:443 の形でした。送信元は国内の大手 ISP ばかり。
要するに、これは ALB の ENI です。一般ユーザーが HTTPS でサイトを見に来ている入口そのものでした。
送信元 IP を引いてみる
念のため GeoIP で送信元を確認しました。
| IP | 種別 |
|---|---|
| 203.0.113.45 | 国内コンシューマー ISP(モバイル回線) |
| 198.51.100.72 | 国内コンシューマー ISP(固定回線) |
どちらも一般家庭・モバイル向けの ISP です。攻撃の出所になりやすいデータセンターや VPS 事業者の IP ではありません。
ただし、WHOIS や GeoIP で分かるのは「どの ISP の、コンシューマー向けらしいレンジか」までです。その IP が CGNAT の出口かどうかは、外から見て断定できません。ここで言えるのは「一般利用者の回線から来ていると考えて矛盾がない」という裏付けです。
バイト数の傾向も HTTPS らしく、レスポンスのほうがリクエストより大きい(10,164 → 18,401)。Web ページを返しているなら自然な形です。
結論
何も起きていませんでした。
ISP の CGNAT が送信元ポートを付け替える際に、たまたま 6379 を割り当てただけ。それ以降その通信はずっと「6379 番」を名乗って進み、ALB がそこへ返事を返した、というだけの話でした。
そしてこれは「たまたま今日起きた珍事」ではなく、仕組み上、アクセスがある限り起こり続ける現象です。6379 に限った話でもなく、3306(MySQL)でも 5432(PostgreSQL)でも同じことが起きます。
この仕組みを知らないとハマる落とし穴
今回は調査で済みましたが、理解していないと実害が出るケースもあります。
NACL の outbound DENY で本番障害になる
これが一番怖いパターンです。セキュリティ強化のつもりで NACL に
outbound: 6379 DENY
を入れていると、たまたま送信元ポート 6379 を引いたユーザーだけサイトが見られないという障害になります。
しかも症状が最悪です。再現しない。特定の人だけ。時間が経つと(NAT のポートが変わると)直る。問い合わせを受けても「こちらでは再現しません」という回答になりやすい類の事象です。
セキュリティグループはステートフルなので戻りが自動で通り、この問題は起きません。ハマるのはステートレスな NACL のほうです。同じ理由で、ステートレスなファイアウォールや、ポートベースの ACL も同様に要注意です。
Flow Logs の限界を知らないと結論を出しすぎる
Flow Logs は ENI 単位の記録なので、見えるのは「ENI に届いた時点のヘッダー」だけです。
flowchart LR
A[端末] --> B[宅内ルーター] --> C[ISP の CGNAT] --> D[Internet Gateway]
D --> E
subgraph OBS["VPC Flow Logs が観測できる範囲"]
E["ALB の ENI"]
end
見える — 送信元/宛先の IP とポート、プロトコル、パケット数、バイト数、ACCEPT/REJECT、TCP フラグ(
tcp-flags。SYN / SYN-ACK から接続を開始した向きも分かる)見えない — 端末や宅内ルーターの実 IP(NAT で消えている)、ALB のパブリック IP(IGW が書き換えた後しか届かない)、通信の中身、パケット 1 つごとの TCP フラグ(
tcp-flagsは集約単位のビットマスクなので、ACK や PSH は含まれない)
先ほど 5 段階で経路を追いましたが、Flow Logs に残るのは最後の 1 段階だけです。手前で何度 NAT されたかは記録に一切現れません。
なので今回証明できたのは「この通信は ALB 宛の正常な HTTPS である」ところまでです。その中身が正当かを問うなら、見に行くべきログのレイヤーが変わります。どのパスにリクエストが来て何を返したのかならアクセスログ、そのリクエストがシステムの中で何をしたのかならアプリケーションのログ。知りたいことがどのレイヤーの話なのかで、見るログを選ぶ必要があります。今回の論点は L3/L4 の話だったので、Flow Logs だけで決着がつきました。
おわりに
結果は空振りでしたが、ヘッダーの向きでついた見当を、対向フロー・ENI・送信元 IP で裏付けて「正常です」と言い切れたのは良かったと思います。
そしてその見当がついたのは、端末 → 宅内ルーター → CGNAT → Internet Gateway → ENI という流れと、各段で何が書き換わるのかが頭に入っていたからです。どのレイヤーのログを見れば決着がつくのかを選べるかどうかは、経路の全体像を持っているかで決まると実感しました。