AWS API Gateway+EC2でセキュアなREST APIを構築する

# 背景

「AWSを使ってAPIを構築したい」という要望は山程あると思います。
その場合、API GatewayとLambdaを用いてサーバレスに構築することがほとんどです。
しかし、Lambdaにはメモリ・実行時間の制限があったり、Lambdaベースでコーディングしなければならなかったりといった不自由な点も存在します。
もしかしたらこれらの条件が理由でLambdaではなくEC2上にサーバを構築したいということがあるかもしれません。AWSに不慣れな場合、この方が手っ取り早い場合もあります。

そこで、今回はAPI Gatewayを通してEC2内に立てた任意のポートにリクエストを送る手順をご紹介します。

# 設計

API Gateway+EC2といいましたが、間にLambdaを噛ませています。
LambdaからこのEC2サーバにHTTP経由でリクエストを送信します。
通常、Lambdaはネットワーク接続不可能に設定されていますが、private subnet内に設置することによって同VPC内へのアクセスを実現しています。

![Screen Shot 2020-04-22 at 16.42.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/9ec21f67-b6e8-af7e-37a2-2f09e0d400e7.png)

<br>

# 前提

- public subnet内に設置されたEC2サーバはすでに構築済みであること
- まずはLambdaを張るためのprivate subnetが作成されていること(VPCのCIDRがa.b.0.0/14ならsubnetのCIDRをa.b.16.0/20とか)

<br>



# Lambda

まずEC2にリクエストを送信するためのLambda関数を構築します。

## 関数の作成

関数を作成します。今回はNode.jsを選択しました(好きにやってください)。

![Screen Shot 2020-04-22 at 16.18.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/615d6d5c-62ef-49e1-fb34-2555ee5319db.png)

関数は以下のように記述しています(好きにやってください)。
この場合、環境変数のURLにEC2のエンドポイントURLを設定してください。

```javascript:index.js
var request = require('request-promise');

exports.handler = function(event, context) {
  console.log(process.env["URL"])
  
  var options = {
    uri: process.env["URL"],
    headers: {
      "Content-Type": "application/json",
      "Authorization": process.env["BASIC_TOKEN"]
    },
    body: JSON.stringify(event),
    method: 'POST'
  }
  
  return new Promise((resolve, reject) => {
    request(options)
      .then((res) => {
        resolve(res);
      })
      .catch((err) => {
        resolve(err);
      });
  });
}

```


## IAM Roleの付与

LambdaからVPCエンドポイントに接続するには以下のIAMポリシーが必要になります。
- DescribeNetworkInterface
- CreateNetworkInterface
- DeleteNetworkInterface

よって、LambdaのIAM Roleにこれを付与します。僕は`EC2EnrollAccess`というポリシーを作成し、LambdaのIAM Roleにアタッチしました。

![Screen Shot 2020-04-22 at 16.20.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/67f530c7-093a-7538-b3f4-f64d054c4d10.png)


## VPCの設定

関数のVPCの設定からカスタムVPCを選択します。以下のように設定します。

VPC: EC2に使用されているもの
サブネット: 作成したprivate subnet(2つつけろって言われてるけど1つでもいけます)
セキュリティグループ: なんでもいいです(僕はEC2と同じルールをアタッチしています)

以前の項でIAM Roleを付与し忘れているとエラーになるので注意してください。

![Screen Shot 2020-04-22 at 16.20.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/22eadf06-4d98-e42a-a42c-5c2fff758b53.png)


## EC2のセキュリティルールの設定

EC2のセキュリティグループから、EC2で構築している任意のポート(僕の場合は80)を設定します。IPアドレスにLambdaのあるprivate subnetのCIDRブロックを設定してください。
セキュアにするため、他のソースからのアクセスは許可しないでください。

![Screen Shot 2020-04-22 at 16.21.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/9e51723f-4b2c-ced3-a08b-7bd2222cd268.png)

## テスト

この時点でLambdaからテストしてみてください。LambdaからEC2への接続が確認できると思います。

![Screen Shot 2020-04-22 at 16.22.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/243995/46b3c287-8664-66f4-c73c-3a2e941497fc.png)

<br>

# API Gateway

次にAPI GatewayからLambdaにリクエストを送信するREST APIを構築します。[以下](https://dev.classmethod.jp/articles/getting-start-api-gateway/)を参考にAPIキーの設定まで行なってください。
統合リクエストに作成したLambda関数を設定することによって、API Gatewayのエンドポイントにリクエストを送信すればEC2のサーバにAPIを叩き、レスポンスが返ってきます。

<br>

# 最後に

これでAPI Gateway以外のエンドポイントからアクセスしようとしてもEC2までにはたどり着けず、かつAPI Gatewayの認証によって鍵を持っていないとアクセスできないセキュアなREST APIが設計できました。
もしこのAPIを使用する時間が限られているならば、EC2を極限までサーバレスにするために起動・停止もLambdaでコントロールしてあげましょう。