[AWS]閉域オンプレ環境からのDirect Connect経由でのS3接続についてちゃんと書く 

# 概要
エンタープライズでは、閉域でオンプレ+AWSのハイブリッド環境を構築することがままあります。
Solution Architect Professional取得にあたってこの辺は勉強したものの、
実際やってみると自分がなにもわかってないことがわかったので皆様にも共有します。

まず最初に、**単にDirect Connectを繋いだだけでは、閉域環境のオンプレからAWSサービスを利用することはできません。**
原因の1つは、VPC外のAWSサービスを利用するためにインターネットが必要なこと。
もう1つは、現状[^1]一部のサービス[^2]はDirect Connect経由でVPC エンドポイントに到達できないこと。

これらの問題の解決策としては、VPC エンドポイントと、[パブリック接続のDirect Connect](https://aws.typepad.com/sajp/2014/12/aws-direct-connect-public.html)の組み合わせがあります。
しかし、パブリック接続は、敷設まで敷居が少々高く利用できないシチュエーションもあると思います。
そこで、AWS内にHTTPプロキシ on EC2を設置し、このプロキシ経由でS3に接続する構成をご紹介します。

# 最終構成イメージ
![squid model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358513/c0482e45-eafc-e49c-a639-aa5301e2ab4b.png)


- オンプレ環境、AWS環境ともに閉域
- オンプレ環境とAWS環境間はDirect Connectによって接続
- オンプレ環境内の端末からCLIやSDKでS3に接続したい
-  （アプリケーション自体や冗長化策については割愛）

# 作業概要
ざっくり次の作業が必要になります。

- AWS内から閉域でS3を使えるようにする
- オンプレから閉域でS3を使えるようにする

# 具体的な作業

## AWS内から閉域でS3を使えるようにする
今回は、閉域オンプレからS3を使う記事ですが、これを実現するためにはまずAWS内からS3にアクセスする必要があります。
実際に閉域環境を構築するまであまり意識していませんでしたが、AWS内(例えばEC2)からAWSサービス(S3等)にアクセスする際は、インターネット接続が必須です。
**閉域に限らず、プライベートサブネットにNATゲートウェイを持たせていない場合も、S3やCloudWatchに接続できません。**
このようなときに助けになるのが、[VPC エンドポイント](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-endpoints.html)です。

はい。わかります。なんとなくめんどくさいイメージがありますよね。
大丈夫です。できます。素晴らしい記事がクラスメソッドさんにあるので[こちら](https://dev.classmethod.jp/cloud/vpc-endpoint-for-s3/)に従って作ってください。

## オンプレから閉域でS3を使えるようにする
先のフェーズでAWS内からAWSサービスに、インターネットを介さず接続することはできました。
しかし、S3等の一部のサービスでは、これだけでは閉域オンプレ環境からS3にアクセスすることはできません。
（インターネットに接続可能なオンプレ環境であれば可能です）
そこで、このフェーズでは次の作業を行います。

 - AWS側にHTTPプロキシを構築する
 - HTTPプロキシサーバをNLBに組み込む（任意）
 - オンプレ側がHTTPプロキシを使えるようにする
 
### AWS側にHTTPプロキシを構築する
S3等の一部のサービスに、閉域オンプレから直接接続することはできませんが、CLIやJDKはHTTPプロキシに対応しているため、これを利用することができます。

以下のような手順でササッとHTTPプロキシをインストールしましょう。
ここではSquidを利用しますがなんでも大丈夫です。
※以下、Amazon Linux2と仮定して進めます

```
# yum install -y squid
# systemctl start squid
# netstat -anp | grep 3128

# systemctl enable squid
```

Squidの設定についてここでは詳細を記しませんが、以下を設定するものとします。

- 3128ポートでの待受（デフォルト）
- オンプレ環境からのアクセス許可
- キャッシュの無効化

```shell:/etc/squid/squid.conf
# Example rule allowing access from your local networks.
# Adapt to list your (internal) IP networks from where browsing
# should be allowed
#acl localnet src all                # すべての接続を許可する場合はこちら（テスト用）
acl localnet src XXX.XXX.XXX.XXX/24  # オンプレ環境の接続元IPアドレスを入力

# Squid normally listens to port 3128
http_port 3128                       # デフォルト設定

# Disable cache
acl no_cache_acl src all             # すべての接続でキャッシュを無効化
cache deny no_cache_acl
```

### HTTPプロキシサーバをNLBに組み込む（任意）
後々の冗長性確保とプロキシのホスト名を名前で解決できるようNLBを作成します。
転送する情報はHTTP(S)なので、ALBでもいけそうな気がしますが、**必ずNLBで作成する必要があります。**
これは、ALBがHTTPリクエストを転送する際に、FQDN部分を握り潰してしまうためです。
ここハマったので2回書きます。**ALBは使えません。NLBで作成する必要があります。**

この作業は必須ではありませんが、実施することをおすすめします。
実施しない場合は、次の作業でNLBのエンドポイントの代わりに、HTTPプロキシサーバのIPアドレスを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358513/be1a1a7d-b868-b326-4e76-2435d66b8ccd.png)

![sampl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358513/f971cce3-b8ec-36e0-0539-003eac915e44.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358513/bcccdfec-1c1b-1902-778e-bf2d31f968e9.png)

ターゲットの登録は、オートスケーリンググループとの連携による自動登録を想定しているため割愛します。

### オンプレ側がHTTPプロキシを使えるようにする
前段でAWS側の準備は整いました。
次は、オンプレ側がHTTPプロキシを使うよう設定する必要があります。
ここでは、例としてCLIでの設定方法を記載します。

CLIは環境変数でプロキシを利用してくれるため、以下を設定するだけです。[(参考)](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-proxy.html)

```bash:Mac/Linux
# ホスト名はNLBのエンドポイント。NLB未作成の場合はHTTPプロキシサーバのIPアドレス
$ export HTTPS_PROXY=squid-xxxxxx.elb.ap-northeast-1.amazonaws.com:3128
$ export HTTP_PROXY=squid-xxxxxx.elb.ap-northeast-1.amazonaws.com:3128
```

Windows環境の方は、安易に上記「参考」を参考にしてsetxを使うと[叱られる](https://qiita.com/jeyei/items/05ce2739501832463b3b)のでお気をつけください。
以下のようにPowerShellで書くほうが安全ですね。

```powershell:Windows
# 実行ユーザのユーザ環境変数に追加
PS C:\> [System.Environment]::SetEnvironmentVariable('HTTP_PROXY', 'squid-xxxxxx.elb.ap-northeast-1.amazonaws.com:3128',[System.EnvironmentVariableTarget]::User)
PS C:\> [System.Environment]::SetEnvironmentVariable('HTTPS_PROXY', 'squid-xxxxxx.elb.ap-northeast-1.amazonaws.com:3128',[System.EnvironmentVariableTarget]::User)
```


もちろんCredentialの設定は必要です。合わせて設定したCredentialにS3バケットのListBucket権限があることを確認してください。

```
aws configure
```

また、オンプレ側をEC2で擬似的に再現してみたい方は、インスタンスメタデータへのアクセスはプロキシを使用しないよう除外設定を入れてください。
詳細は上記[「参考」](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-proxy.html)のリンクの最後に記載されています。


SDKでも同様にプロキシを設定できます。
例えば、Pythonのboto3ですと、botocore.configで設定できるようです。[(参考)](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html#botocore.config.Config)

```python:Python
import boto3
from botocore.config import Config

boto3.resource('s3', config=Config(proxies={'https': 'squid-xxxxxx.elb.ap-northeast-1.amazonaws.com:3128'}))
```

## 確認
お疲れ様でした。
CLIでS3に接続できることを確認しましょう。

```shell
aws s3 ls s3://squid-test-bucket/
```


[^1]: ~~AWSによると多数の要望があるそうなのでいつかは…？~~ 2021.2よりPrivateLink対応しています。本記事も近日中に更新します。
[^2]: 正確には、ゲートウェイタイプのVPCエンドポイントを利用するAWSサービス。具体的にはS3とDynamoDB。