プロキシとVPCエンドポイントは混ぜるな危険!!

# はじめに
お疲れ様です。矢儀 @yuki_ink です。

**プロキシとVPCエンドポイント、使ってますか？？**

このような構成で、AWS上でプロキシとVPCエンドポイントを利用されているケースは結構多いと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/962befd9-ef82-24ab-46dd-e691de4c9002.png)

- プライベートサブネットにサーバ（EC2）を配置
- AWSサービス（S3など）への通信はVPCエンドポイントを通す
- そのほかの通信はプロキシ経由でインターネットに通す

今回はそんな構成に対する問題提起をしてみたいと思います。

:::note
なお、この記事は、過去のLTにてご紹介したTipsの1つを、記事として解説・深堀りするものです。
スライドを一通り見ていただくことで理解が進む点もあると思いますので、よろしければご参照いただければと思います。
（出典）[VPCエンドポイントを巡る名前解決とルーティングの話](https://speakerdeck.com/yuki_ink/vpcendopointowoxun-ruming-qian-jie-jue-toruteingunohua)
:::


# 問題提起
**プロキシ側にもVPCエンドポイント置いてませんか????**


# 結論
**プロキシを置いているようなパブリックサブネットには、VPCエンドポイントは置かないほうがよい!!（特にゲートウェイ型!!）**

その理由は、以下のような状況が考えられるからです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/98551a70-f2d0-47c8-9c5a-7f3374221aa3.png)

プロキシに流したAWSサービス向けの通信が、意図せずVPCエンドポイントに流れ、VPCエンドポイントに設定されたポリシーやアクセス先S3バケットのバケットポリシーの制御を受けて、結果としてサービスへのアクセスが不可となる事象が発生する可能性があります。

:::note
「環境の構成が理解できていればそんなこと起こらないのでは??」と思いますが、特にエンタープライズの大規模な（"共通プロキシ"的なものを置いているような…）環境では、構成の全体像を把握できる人間は結構少ないと思います。
:::



# 詳細
なぜ上記のようになってしまうのか。
順を追って整理してみます。

## ◆ゲートウェイ型VPCエンドポイントの仕組み
まず、ゲートウェイ型VPCエンドポイントの仕組みを振り返ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/13949113-a8d7-4009-8e90-3fa8255888f4.png)

EC2から東京リージョンのS3のエンドポイントにアクセスしようとするとき、流れとしては次のようになります。

＝＝＝＝＝＝＝＝＝＝
1. Route53 Resolverに対してDNSリクエスト（ `s3.ap-northeast-1.amazonaws.com` を問い合わせ）
1. レスポンスで **パブリックIP** が返ってくる
1. ルートテーブルを参照
⇒**ルートテーブルにより、S3のパブリックIP向けの通信がVPCエンドポイントに捻じ曲げられる（デフォルトゲートウェイには行かない）**

＝＝＝＝＝＝＝＝＝＝

ルートテーブルの設定によって、通信がVPCエンドポイントに流れ、プライベート通信が成立という点がポイントです。
逆に言えば、ゲートウェイ型VPCエンドポイントだけ作っても、ルートテーブルの設定がなければ意味がありません！

## ◆インターフェイス型VPCエンドポイントの仕組み
次に、インターフェイス型VPCエンドポイントの仕組みを振り返ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/0d464c8d-be85-44df-a5ee-089cac385cae.png)

EC2から東京リージョンのSSMのエンドポイントにアクセスしようとするとき、流れとしては次のようになります。

＝＝＝＝＝＝＝＝＝＝
1. Route53 Resolverに対してDNSリクエスト（ `ssm.ap-northeast-1.amazonaws.com` を問い合わせ）
1. レスポンスでサービスエンドポイントに紐づく **プライベートIP** が返ってくる
1. ルートテーブルを参照
⇒**VPC内のローカル通信としてインターフェイス（ENI）にアクセス**

＝＝＝＝＝＝＝＝＝＝

インターフェイス型VPCエンドポイントを利用している環境では、紐づくサービスのエンドポイントは **プライベートIPで名前解決** され、VPC内のローカル通信として処理される点がポイントです。


## ◆プロキシ環境下での名前解決はプロキシが担う
AWSサービスの利用に限らず、URLを使った全ての通信は最初に名前解決を行います。
では、プロキシを使って通信を行う場合、クライアント、あるいはプロキシのどちらが名前解決を担うのか。

答えはプロキシです。
例えば、クライアントから投げられた `s3.ap-northeast-1.amazonaws.com` へのリクエストは、そのままプロキシに流れ、プロキシ側で名前解決がなされます。

非常にイメージしやすい図がありましたので、紹介させていただきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/731275/a98ed835-9407-4420-86db-04323b5992c8.png)
（出典）[【図解】httpプロキシサーバの仕組み(http GET/https CONNECTメソッド)や必要性・役割・メリットデメリット・DNSの名前解決の順序](https://milestone-of-se.nesuke.com/nw-basic/grasp-nw/proxy/)



## ということは・・・
AWSサービスへのリクエストがプロキシに投げ込まれた場合、プロキシ側でエンドポイントの名前解決がされ、その結果、プロキシが存在するサブネットのルートテーブルに従ってVPCエンドポイントに通信が向かうということが、ご理解いただけたと思います。

特にゲートウェイ型VPCエンドポイントは、VPCエンドポイントポリシーで接続先のリソースを制限している場合があり、そこにハマるとトラブルに直結します。
なかなかマイナーな設定かと思いますが、だからこそ、注意が必要です。


:::note info
VPCエンドポイントポリシーについては、こちらの記事で深く考察されていましたので、是非ご一読ください。
（参考）[VPCエンドポイントポリシーの運用について本気出して考えてみた](https://blog.serverworks.co.jp/2021/06/02/202746)
:::



# 深堀り
上記でも何度か例示しましたが、AWSサービスのエンドポイントは `s3.ap-northeast-1.amazonaws.com` という形になっており、つまり **リージョンごとにAWSサービスのエンドポイントは違います**。

**では、クロスリージョンのAWSサービス間通信でプロキシとVPCエンドポイントを利用したらどうなるの・・・??**

ここで次のケースを考えてみたいと思います。

＝＝＝＝＝＝＝＝＝＝

**【プロキシサーバ】**
- 大阪リージョンのパブリックサブネットにプロキシサーバを設置
- そのサブネットにはS3用のゲートウェイ型VPCエンドポイントも設定

**【クライアント（接続元EC2）】**
- プロキシサーバへ通信を流すクライアント（接続元EC2）は、プライベートサブネットに配置
- 大阪リージョンだけでなく、東京リージョンにも存在する
※東京リージョンと大阪リージョンに散在するEC2が、大阪リージョンの共通プロキシサーバを経て、インターネットに接続するような構成を想定
- 接続元EC2が配置されているサブネットにもS3用VPCエンドポイント（ゲートウェイ型）は作成されている

＝＝＝＝＝＝＝＝＝＝



この環境でのS3向け通信の経路について、様々なパターンを考えてみます。
結論としては、以下のようになります。

## ◆プロキシサーバから直接S3にアクセスするパターン

<INS>①プロキシサーバ（大阪）からS3（大阪）にアクセス</INS>
　プロキシサーバ（大阪） ⇒ VPCエンドポイント ⇒ S3（大阪）

<INS>②プロキシサーバ（大阪）からS3（東京）にアクセス</INS>
　プロキシサーバ（大阪） ⇒ インターネットゲートウェイ ⇒ S3（東京）

:::note warn
**★ポイント1★**
他リージョンのS3へのアクセスは、VPCエンドポイントに向かわず、インターネットゲートウェイに向かう
:::

## ◆EC2（大阪）からプロキシサーバ（大阪）経由でS3へアクセスするパターン

<INS>③EC2（大阪・**S3エンドポイントのプロキシ除外設定無し**）からS3（大阪）にアクセス</INS>
　EC2（大阪） ⇒ プロキシサーバ（大阪） ⇒ VPCエンドポイント（プロキシ側） ⇒ S3（大阪）

<INS>④EC2（大阪・**S3エンドポイントのプロキシ除外設定あり**）からS3（大阪）にアクセス</INS>
　EC2（大阪） ⇒ VPCエンドポイント（クライアントEC2側） ⇒ S3（大阪）

<INS>⑤EC2（大阪・**S3エンドポイントのプロキシ除外設定無し**）からS3（東京）にアクセス**</INS>
　EC2（大阪） ⇒ プロキシサーバ（大阪） ⇒ インターネットゲートウェイ ⇒ S3（東京）

<INS>⑥EC2（大阪・**S3エンドポイントのプロキシ除外設定あり**）からS3（東京）にアクセス</INS>
　対象の通信のルートが無いことにより通信が失敗

:::note warn
**★ポイント2★**
プロキシ除外設定をしてはじめて、通信がVPCエンドポイントに行く!!
プロキシに向かわせたくない通信は`NO_PROXY`に書いておく
:::

:::note warn
**★ポイント3★**
VPCエンドポイントを利用すると、リージョンまたぎのリソースアクセスができない!!
（参考）[[AWS] VPC Endpoint で S3 にアクセスするときはリージョン指定に注意が必要な件](https://dev.classmethod.jp/articles/s3-region-with-vpc-endpoint/)
:::


## ◆大阪以外のEC2から大阪のプロキシ経由でS3へアクセスするパターン

<INS>⑦EC2（東京・**S3エンドポイントのプロキシ除外設定無し**）からS3（大阪）にアクセス</INS>
　EC2（東京） ⇒ プロキシサーバ（大阪） ⇒ VPCエンドポイント（プロキシ側） ⇒ S3（大阪）

<INS>⑧EC2（東京・**S3エンドポイントのプロキシ除外設定あり**）でS3（大阪）にアクセス</INS>
　対象の通信のルートが無いことにより通信が失敗

<INS>**⑨EC2（東京・**S3エンドポイントのプロキシ除外設定無し**）からS3（東京）にアクセス**</INS>
　EC2（東京） ⇒ プロキシサーバ（大阪） ⇒ インターネットゲートウェイ ⇒ S3（東京）

<INS>**⑩EC2（東京・**S3エンドポイントのプロキシ除外設定あり**）から、S3（東京）にアクセス**</INS>
　EC2（東京） ⇒ VPCエンドポイント（クライアントEC2側） ⇒ S3（東京）

:::note warn
**★ポイント4★**
プロキシと同じリージョンのS3にプロキシ経由でアクセスする場合、プロキシからプロキシ側のVPCエンドポイントに通信が捻じ曲げられる
:::


# 終わりに
以上、プロキシ環境下でVPCエンドポイントを使うときは注意が必要！ということをご紹介しました。

VPCエンドポイントは気軽に作成でき、ゲートウェイ型VPCエンドポイントに至っては無料で利用できるので、何も考えずに「とりあえず作っとけ！」となることもあると思います。
しかし、そこにプロキシが入ると、通信経路が思わぬ形になり、トラブルの種となる可能性がありますので、十分にご注意ください。

最後までお目通しいただき、ありがとうございました！