ALB から S3 にアクセス(CDKのコード付き)

# 参考にしたのはこちら

https://aws.amazon.com/jp/blogs/networking-and-content-delivery/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/

ただ、この例は`Internal`なALBを用意し、そこから、`Private Subnet` → `VPC Endpoint`+ privatelink → S3なので、別に`Public Subnet`に置いたALBでもいけたのが、今回の記事の内容です。

# 結論
VPC Endpoint(Interface)を使うとうまくいきました。

ALB → `Vpc Endpoint`(Interface) → S3

https://docs.aws.amazon.com/ja_jp/vpc/latest/privatelink/gateway-endpoints.html

# 何が嬉しいのか

- ALB → S3 間で認証を完結できる

SSO（シングルサインオン）等で、社内のみで見れるようにしたい社内サイトとか、認証をALBに集約したい時はとても嬉しいと思います。
OIDCを使用して、ALBに認証後、S3にルーティングすると、アプリ側で認証を実装する手間が減ります。
ALB → S3で認証が完結するということは、特にwordpressなどで構築されたサイトだと、プラグインや自作で認証を実装すると思うのですが、それが不要になります。


# 概要図

下記の図でいう 4 → 2 です

![ALB to s3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/183059/c651b90c-51f6-6b44-e3b9-473df05035b9.png)

# 前提

- VPC(`Public Subnet`)
- ALB
- S3(ホスト名と同一)

今回は、`Public Subnet`の`VPC Endpoint`をアタッチする例です。

`Private Subnet`の場合は、別途 [`Privatelink`](https://aws.amazon.com/jp/blogs/networking-and-content-delivery/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/) 使用するようです。

# 手順

前提のALBとVPCがあれば、VPC EndpointのIPアドレスをターゲットグループに設定しましょう。

1. VPC Endpointを作成
1. VPC Endpoint作成後、IPv4 アドレスをコピー
1. ALBのターゲットグループ作成（ターゲットの種類 `IP`、 アドレスタイプ `IPv4`）
1. ターゲットグループの`IPアドレス`に`2`でコピーした`IPアドレス`を設定
1. ターゲットグループのヘルスチェックを`307,405`にする
1. 終了

これで、ALB → S3が設定できました。

# ユースケース

- ユーザー数少ないけど、認証が必要なアプリ。
- サーバーサイドで認証実装できない（or 面倒）

# パフォーマンス向上

- 規模が大きくなってALBから出ていく通信料が気になるようになったら、ALBの前にCloudFront
- ALB - S3間に [FileCache](https://aws.amazon.com/jp/filecache/)、[Storage Gateway](https://aws.amazon.com/jp/storagegateway/)をキャッシュサーバーとして検討（未検証。条件：ALBのターゲットグループ設定、CDK対応）

# 注意

- S3のバケット名
手順どおりやっても、S3のバケット名が、ホスト名と同一にならないと見つからないです。
例えば、`https://sample-test.co.jp` であれば、バケット名は `sample-test.co.jp`です。


- 通信料
もし動画や大きなファイルを扱うのであれば、通信料も大きくなっていくので、ALBの前段に`CloudFront`を置くか、`ALB - S3`間の間にファイルキャッシュできるようなサービスをおくと通信料もレスポンスタイムも早くなるのでしょう。

# CDKのコード

実は`Vpc Endpoint`から、IPアドレスとるの難しいんだなと。。。
`Vpc Endpoint`から簡単にとれるように貢献したいところ。。。

```ts

    // vpc endpoint 作成
    const vpcEndpoint = vpc.addInterfaceEndpoint("InterfaceVpcEndpointForS3", {
      service: InterfaceVpcEndpointAwsService.S3,
      subnets: { subnetType: SubnetType.PUBLIC },
      privateDnsEnabled: false,
    });

    // vpc endpoint から IP Addressを取得
    const getEndpointPrivateIpAddress = (index: number) => {
      const privateIpAddressField = `NetworkInterfaces.${index}.PrivateIpAddress`;
      const resource = new AwsCustomResource(this, `GetEndpointIp${index}`, {
        onUpdate: {
          service: "EC2",
          action: "describeNetworkInterfaces",
          outputPaths: [privateIpAddressField],
          parameters: { NetworkInterfaceIds: vpcEndpoint.vpcEndpointNetworkInterfaceIds },
          physicalResourceId: PhysicalResourceId.of(privateIpAddressField),
        },
        policy: AwsCustomResourcePolicy.fromSdkCalls({ resources: AwsCustomResourcePolicy.ANY_RESOURCE }),
      });
      return resource.getResponseField(privateIpAddressField);
    };

    // publicSubnets を取得
    const { publicSubnets } = vpcEndpoint.node.scope as Vpc;

    // publicSubnets の個数 を取得
    const ipAddresses = publicSubnets.map((_, index) => Token.asString(getEndpointPrivateIpAddress(index)));

    // ALB の ターゲットグループを作成
    const vpcEndpointTargetGroup = new ApplicationTargetGroup(this, "VpcEndpointTargetGroup", {
      vpc,
      targetType: TargetType.IP,
      port: 443,
      protocol: ApplicationProtocol.HTTPS,
      healthCheck: {
        healthyHttpCodes: "307,405",
      },
    });

    // vpc endpoint の IPアドレス ALB の ターゲットグループに設定
    for (const ipAddress of ipAddresses) {
      vpcEndpointTargetGroup.addTarget(new IpTarget(ipAddress));
    }
```

CDKのほうが大変でした。。。
もっと簡単に、作った`VPC endpoint`からIPアドレス取得できる方法あったら教えてくださいmm

# 参考

- https://aws.amazon.com/jp/blogs/networking-and-content-delivery/hosting-internal-https-static-websites-with-alb-s3-and-privatelink/
- https://docs.aws.amazon.com/whitepapers/latest/best-practices-wordpress/reference-architecture.html