Timee Product Team Blog

タイミー開発者ブログ

VPC Flow Logs で「6379 番ポートに外部 IP が…」と一瞬ひやっとした話

はじめに

こんにちは。プラットフォームエンジニアリングチームに所属している徳富(@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 点です。

  1. 対向のインバウンドフロー(外部:6379 → 内部:443)が同じ ENI・同じ時間帯に存在するか
  2. その 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 という流れと、各段で何が書き換わるのかが頭に入っていたからです。どのレイヤーのログを見れば決着がつくのかを選べるかどうかは、経路の全体像を持っているかで決まると実感しました。