Amazon API Gatewayを利用して相互TLS認証通信の実現

API Gatewayは、REST、HTTP及びWebSocketのAPIを作成・公開・維持・モニタリングするためのAWSマネージドサービスで、豊富な機能は提供してあり、簡単にAPIの構築ができます。
最近Amazon API Gatewayを触る機会がありまして、相互TLS認証の実現にあたって、サーバー証明書（OpenSSLやプライベートCA）のインポート、カスタムドメインの作成とVPCリンクの作成など色々試してみました。

mTLSとはMutual Transport Layer Security (相互トランスポート層セキュリティ)の略称で、さらに、略して「相互TLS認証」と言います。クライアントとサーバーの両方が互いに証明書を利用して認証することで、セキュアに認証できる方式のことを指します。
※ 今記事はAPI Gatewayの説明とさせてください。mtlsの詳細紹介、証明書の発行周りは別途記事を用意する予定です。

Amazon API Gatewayは2020年ごろに、[mTLSをサポート](https://www.amazonaws.cn/en/new/2020/amazon-api-gateway-now-supports-mutual-tls-authentication/)することはアナウンスしました。これでAPI Gateway利用してMutual TLSの実現ができるようになりました。

* まずは全体的にAPIの流れのイメージですが、下図に示している通りになります。
    1. 外部サーバから相互TLS認証を利用して、Amazon API Gatewayと通信を行います。
    2. インターナールNLBを通して、プライベートサーブネットにあるAPIエンドポイントを呼び出します。
    3. APIエンドポイントからのレスポンスを外部に返します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/3cf6fb98-6644-1a87-dda7-cf9829d67e16.png)

では、早速作っていきます。
## AWS API Gatewayの作成
* AWS Console->API Gatewayのダッシュボードに遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/f6425984-51ce-3c88-a8d1-6160be014892.png)
* 今回はREST APIの構築を利用します（※Swagger（OpenAPI）の形式のファイルをインポートも可能です）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/93863156-d952-6fed-bcaf-e455341039a1.png)
* 作成画面では４つの選択あります。今回は新しいAPIを一から作成します。
    * APIエンドポイントタイプ３種類あります。
        * リージョン <- 今回はこちら
        * エッジ最適化
        * プライベート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/fd12a10e-c124-c261-a421-d64d4a547374.png)
* 以下の感じで作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/7f73f1c2-a08e-6f54-a159-4c8916076593.png)

## AWS API Gatewayカスタムドメインの作成
※ 左のサイトメニューよりカスタムドメインの作成を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/eadb0b98-898b-f006-7d67-cf98a65c862e.png)
* カスタムドメイン名の画面で作成ボタンを押下します.
* カスタムドメイン名の作成画面が表示されます。
    * ドメイン名（事前にドメインを取得済みが必要であること, hogehoge.com）
        * サブドメインでtest.hogehoge.comと入力します。
    * TLSのバージョン情報
        * TLS１.2を選びます。
    * 相互TLS認証：スイフトして有効にします。
        * トラストストラURIの入力が求められています。S3バケットを作成し、トラストストアをS3にアップロードし、URIを記載します。
        * S3バージョンニングを有効することはお勧めします。
            * 例：s3://hogehoge/test.truststore
    * エンドポイントタイプ設定します。
        * 先ほどのステップにあわせて「リージョン」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/414c2d94-d416-02b7-cfe3-afa97588e83f.png)
    * 証明書タイプ
        * インポートされた証明書またはプライベート証明書を選択
            * 証明書
                * 事前にTLS通信用のサーバー証明書を用意して、ACMにインポートします。
            * 　所有権証明書
                * ドメインの所有権を証明するための証明書で、事前にACMでパブリック証明書をリクエストします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/a5b581e1-947b-4eb1-8e37-90328c7c7f0f.png)
※今回の記事では証明書周りの説明は割愛させてください。後日別途記事作成する予定あります。
* 全て入力して終わったら「ドメイン名を作成」ボタン押下でカスタムドメインを作成します。
* カスタムドメインで通信するために、デフォルトのAPIエンドポイントを無効化にします。
    * APIの設定をクリックして設定画面に遷移します。
    * デフォルトのエンドポイントを「非アクティブ」に選択します。「変更を保存」ボタンをクリックします。
    * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/dae64479-0ea0-410d-c242-dd18dd0fd2a2.png)

これでカスタムドメインの作成が完了で、外部からAPI Gatewayとの間に、TLS相互通信認証ができるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/2d15a90f-4351-a0b5-c6c4-464f2336da4d.png)

## VPCリンクの作成
続きまして、API GatewayからプライベートサブネットにあるAPIエンドポイントとの通信方法について説明します。
プライベートサブネットのAPIエンドポイントを呼び出すためにVPCリンクを作成する必要あります。
* 左サイトメニューの「VPCリンク」をクリックします。
    * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/a2d8c26a-93f2-852e-eaed-53a378c10525.png)
* VPCリンクバージョンを選択
    * 「REST APIのVPCリンク」を選択します。
* VPCリンクの詳細
    * 名前：例：APIDEMO
    * ターゲットNLB
        * 内部通信用インタナールNLBは必要で、事前に用意したものを選択します。（内部NLBについては別記事で紹介する予定あります。）
    * タグは適宜入力し、最後「作成」ボタンを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/156c2eb7-7e68-9d52-9df9-5cb4318892b5.png)

* API Gatewayでのマッピング
    * 「リソースを作成」ボタンを押下します
        * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/a07916de-6227-b0b5-309d-c15757fecae7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/34e45491-95dc-86ae-b5ad-0ae942036aa5.png)
    * 続きまして、「メソッドを作成」ボタンを押下します
        * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/e23851f0-0a42-8be7-172c-85273ed591bb.png)
    * メソッド作成画面で以下の感じで情報を選択・入力します
        * メソッドタイプ
            * POST(例)
        * 統合タイプ
            * VPCリンク
        * HTTPメソッド
            * POST（例）
        * VPCリンク
            * 先ほど作成したVPCリンクを選択します
        * エンドポイントURL
            * EC2サーバー上起動しているAPIのエンドポイントURL（内部NLBのアドレス＋APIのパス）を入力します。
            *  例：http://hogehoge-nlb-xxxxx.elb.ap-northeast-1.amazonaws.com/apis/healthcheck
         * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/d3e5d43c-e86c-a8b3-03dc-a4855673a8df.png)
     * 「メソッドを作成」ボタンを押下、メソッドを作成します。
     * 以下の感じで作成完了です。 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/69c3c0f6-5ce2-a40d-474a-8eb9eb186b96.png)

* 上記を持ちまして、API GatewayからプライベートサブネットにあるAPIエンドポイントへの通信設定が完了しました。
    * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239717/5eaf2570-d2e9-7692-3d07-a7b21e56d3da.png)


最後は全体的の動作確認を実施します。
クライアント側に以下のCurlコマンドを利用して確認できます。

```
$ curl --key hogehoge_client.key --cert hogehoge_client.pem -X GET https://test.hogehoge.com/healthcheck/
```
`OKの場合：　http status code 200, レスポンス本文（例）　：{"DEMO":"Health Check OK"}`
`NGの場合： http status code 403など, レスポンス本文（例) ：{"message":"Forbidden"}`

NGの場合に、Cloudwatchのログより原因調査したりが必要なので、ログの詳細出力も設定していきましょう。

* 参考したサイト
    * AWS Compute Blog: [Introducing mutual TLS authentication for Amazon API Gateway](https://aws.amazon.com/jp/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/)