AWS Client VPN で1つのエンドポイントで複数のクライアント証明書を利用する方法

相互認証利用し、1つのエンドポイントでクライアント証明書を各ユーザーごとに分けたい場合の設定

1. 相互認証用のサーバーとクライアントの証明書とキーを生成
2. クライアント VPN エンドポイントの作成
3. クライアントの設定
2. ログの確認
3. クライアント証明書失効リストを利用した、接続の取り消し

## 相互認証用のサーバーとクライアントの証明書とキーを生成
手順は、一部を除いてこの通りに作っていきます
https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-admin/authentication-authrization.html#mutual

__OpenVPN Easy-RSA レポジトリのクローンを作成し、新しい PKI 環境を初期化します__
以後、証明書や鍵の管理はこの環境で実施します。

```bash
$ git clone https://github.com/OpenVPN/easy-rsa.git
$ cd easy-rsa/easyrsa3
$ ./easyrsa init-pki
```

__新しい認証機関 (CA) を構築します__
Common Name を聞かれるので、管理しやすい名前を指定します。デフォルトでも構いません。

```bash
$ ./easyrsa build-ca nopass
Using SSL: openssl LibreSSL 2.8.3
Generating RSA private key, 2048 bit long modulus
..........................................+++
...................................................................................................................+++
e is 65537 (0x10001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]:Private CA

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/xxx/yyyy/easy-rsa/easyrsa3/pki/ca.crt
```
__サーバー証明書とキーを生成します__

```bash
$ ./easyrsa build-server-full server.example.com nopass
```

__クライアント証明書とキーを生成します__
今回はクライアントごとに証明書とキーを作成するので、名前を変えて複数枚作ります
とりあえず Common Name を `client1.example.com` と `client2.example.com` にします
１００ユーザー分欲しいなら１００個作ってください

パスフレーズなしでないとインポートできないので、いったん nopass で作ります

```bash
$ ./easyrsa build-client-full client1.example.com nopass
$ ./easyrsa build-client-full client2.example.com nopass
```

__サーバー証明書とキー、およびクライアント証明書とキーを適当なフォルダにコピーして移動します__

```bash
$ cp pki/ca.crt /custom_folder/
$ cp pki/issued/server.crt /custom_folder/
$ cp pki/private/server.key /custom_folder/
$ cp pki/issued/client1.domain.tld.crt /custom_folder
$ cp pki/private/client1.domain.tld.key /custom_folder/
$ cd /custom_folder/
```

__サーバー証明書とキーを ACM にアップロードします__

```bash
$ aws import-certificate --certificate file://server.example.com.crt --private-key file://server.example.com.key --certificate-chain file://ca.crt --region us-east-1
```

__クライアント証明書とキーを ACM にアップロードします__

注意：`client1.example.com` の証明書とキーのみアップロードします。何枚作成したとしてもそのうち1枚のみで構いません。

```bash
$ aws acm import-certificate --certificate file://client1.example.com.crt --private-key file://client1.example.com.key --certificate-chain file://ca.crt --region us-east-1
```

以上で、サーバーとクライアントの証明書とキー準備が完了です。
ポイントは、VPN エンドポイント作成時に必要な1つだけアップロードすれば良いところです。

## クライアント VPN エンドポイントの作成
Management Console でエンドポイントを作成してきます。

こちらの公式blogを参考にしてます。差異は、認証の部分です。公式の記事ではAD認証を利用しています。
https://aws.amazon.com/jp/blogs/news/introducing-aws-client-vpn-to-securely-access-aws-and-on-premises-resources/


- 下記以外の項目はデフォルト
    - そのほかのオプションパラメーターは任意で設定ください
- CloudWatch Logs のロググループとログストリームは事前に作成してください

|設定項目|値|
|:---|:---|
|名前タグ|適当に|
|説明|適当に|
|クライアント IPv4 CIDR|172.24.0.0/22|
|サーバ証明書 ARN|先ほどアップロードした server.example.com を指定|
|認証オプション|相互認証の使用|
|クライアント証明書 ARN|先ほどアップロードした client1.example.com を指定|
|クライアント接続の詳細を記録しますか?|はい|
|CloudWatch Logs ロググループ名|/aws/clientvpn|
|CloudWatch Logs ログストリーム名|example|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/fbf4b09e-11a6-c07c-71cf-8701a942cf7e.png)


__VPN エンドポイントに、VPC を紐付けます__
これにより VPC 内に ENI が作成されます

![スクリーンショット 0002-04-17 14.44.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/a910c4a5-bb00-409b-66d7-d88312c2e513.png)

__既存のVPCとそのサブネットを選択して関連付けします__

![スクリーンショット 0002-04-17 15.19.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/d6b1136a-72ef-53a8-74b6-57e1c0766cab.png)

環境によって違いますが、今回は4つほど関連づけてみました。
![スクリーンショット 0002-04-17 15.25.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/d6961cf6-403e-8417-3a7f-4dd360cedb13.png)

__VPCへのエンドユーザーアクセスを有効ににします（承認ルールを追加）__
承認ルールは、クライアントVPNエンドポイントを介して指定されたネットワークにアクセスできるユーザーのセットを制御します。
いったん 0.0.0.0/0 を追加して、全てアクセスできるようにしてみます。

<img width="669" alt="スクリーンショット 2020-04-21 9.09.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/69c10090-3c9c-51a0-80e7-2220226933b8.png">

<img width="691" alt="スクリーンショット 2020-04-21 9.04.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/80adf103-16cf-4f9d-ec76-d4241b4e19a9.png">



__セキュリティグループの設定をします__

[公式サイトの情報](https://aws.amazon.com/jp/blogs/news/introducing-aws-client-vpn-to-securely-access-aws-and-on-premises-resources/)によると、このセキュリティグループは以下のような動きになっているようです。

>クライアントVPNエンドポイントはセキュリティグループをサポートします。セキュリティグループを使用して、アプリケーションへのアクセスを制限できます。セキュリティグループは、VPCに関連付けられたENIからの下りトラフィックのみを制御することに注意してください。

今回は検証用なので、いったんアウトバウンドを全て許可する設定の Security Group を利用してみます。
基本的な Security Group を作って設定するだけなので、手順は割愛させていただきます。

__ルートを作成します__
関連付けられたVPCのルートは、クライアントVPNルートテーブルに自動的に追加されます。
今回は4つ関連づけたので、４つ追加されています。
<img width="900" alt="スクリーンショット 2020-04-20 22.52.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/1e5fe29c-4f91-53af-6613-0d84e4486f13.png">

もし、このVPCがVPNクライアントにインターネット接続を提供するようにしたい場合は、0.0.0.0/0 を追加し、[ターゲットVPCのサブネットID] はそれぞれ関連づけたVPCサブネットにして登録します。今回の場合であれば都合4つです。

 <img width="797" alt="スクリーンショット 2020-04-20 22.56.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/97d2985c-4479-e325-b132-94167a9b5ded.png">

４つ追加したあと

<img width="868" alt="スクリーンショット 2020-04-20 23.03.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/2025fbba-80f8-3a08-6dd3-e43be09767f6.png">

いったんこれで VPN エンドポイント の設定を終わります。

## クライアントの設定
[公式サイトの情報](https://aws.amazon.com/jp/blogs/news/introducing-aws-client-vpn-to-securely-access-aws-and-on-premises-resources/)では、[Tunnelblick](https://tunnelblick.net/downloads.html) を使っていたので、VPN接続クライアントはこれを使ってみます。

__クライアント構成（設定ファイル）をダウンロードします__

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_323706_422ce541-4b85-3444-f49c-69caaa2b7134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/98e98011-32a4-cd30-9aba-b220455a7106.png)

<img width="639" alt="スクリーンショット 2020-04-20 23.12.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/e7172148-fe76-2b94-9679-9fd6f9377a74.png">


__作成したクライアントキーにパスフレーズを追加します__
`client1.example.com` と `client2.example.com` の秘密鍵作成時に、パスフレーズを設定していませんでしたので、パスフレーズを追加しておきます

```
openssl rsa -aes256 -in client1.example.com.key -out client1.example.com.pass-ari.key
openssl rsa -aes256 -in client2.example.com.key -out client2.example.com.pass-ari.key
```

__設定ファイルの最後に、クライアントクライアント証明書とキーの設定を追加します__
`client1.example.com` と `client2.example.com` の2つの設定ファイルを作るので、ダウンロードした設定ファイルはコピーして、それぞれ次のような設定を追加します。

```bash:client1.example.com
cert /path/client1.example.com.crt
key /path/client1.example.com.pass-ari.key
```

```bash:client2.example.com
cert /path/client2.example.com.crt
key /path/client2.example.com.pass-ari.key
```

あとは、出来上がった設定ファイルを Tunnelblick で読み込んで接続するだけです。
クライアントの秘密鍵にパスフレーズを設定している場合は、次のようにパスフレーズを要求されます。

<img width="574" alt="スクリーンショット 2020-04-20 23.34.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/223d9719-8a14-e0ec-e069-1a9bddc5d484.png">


## 接続の確認
実際に接続したあと、管理画面から `共通名` の項目を見ると Common Name を確認できます。
<img width="1075" alt="スクリーンショット 2020-04-20 23.45.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/d6d1fbde-d030-3269-80e9-e92a34969399.png">

CloudWatch Logs でログの内容を確認してみます
common-name が記録されるので、接続したクライアントを確認することができます。

```json
{
    "connection-log-type": "connection-reset",
    "connection-attempt-status": "NA",
    "connection-attempt-failure-reason": "NA",
    "connection-id": "cvpn-connection-xxxxxxxxxxxxxxxxx",
    "client-vpn-endpoint-id": "cvpn-endpoint-yyyyyyyyyyyyyyyyy",
    "transport-protocol": "udp",
    "connection-start-time": "2020-04-20 14:30:00",
    "connection-last-update-time": "2020-04-20 14:31:02",
    "client-ip": "172.22.3.2",
    "common-name": "client2.example.com",
    "device-type": "mac",
    "device-ip": "xxx.xx.xx.xx",
    "port": "23771",
    "ingress-bytes": "3051",
    "egress-bytes": "2629",
    "ingress-packets": "9",
    "egress-packets": "6",
    "connection-end-time": "2020-04-20 14:31:02",
    "connection-reset-status": "NA"
}
```


## クライアント証明書失効リストを利用した、接続の取り消し

何らかの理由で、クライアント証明書を紛失した場合やアクセスをブロックしたい場合に備え、クライアント証明書失効リスリストの動作も確認しておきます。

[こちら](https://aws.amazon.com/jp/premiumsupport/knowledge-center/client-vpn-revoke-access-specific-client/)の方法で、`client2.example.com` を失効リストに追加し、特定クライアントのクライアント VPN エンドポイントへのアクセスを取り消してみます。

__証明書作成時に使った、easyrsa の環境で失効作業を実施します__

```bash
./easyrsa revoke client2.example.com
```
プロンプトが表示されたら、yes と入力します。

__証明書失効リストを作成します__

```bash

$ ./easyrsa gen-crl
Using SSL: openssl LibreSSL 2.8.3
Using configuration from /Users/h-matsu/Documents/code/easy-rsa/easyrsa3/pki/easy-rsa-40453.izPBCL/tmp.BwkfBg

An updated CRL has been created.
CRL file: /path/to/easy-rsa/easyrsa3/pki/crl.pem
```

__クライアント証明書失効リストをインポートします__

VPN エンドポイントをチェックしたら、[アクション]で[クライアント証明書CRLのインポート]を選択します
<img width="817" alt="スクリーンショット 2020-04-21 0.10.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/33bfcd43-5d7a-7c85-a630-503857c53216.png">

先ほど作成した `/path/to/easy-rsa/easyrsa3/pki/crl.pem` の中身をテキストで貼り付けインポートします。
<img width="930" alt="スクリーンショット 2020-04-21 0.14.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/323706/6b192adb-ef2c-ce7c-d9c7-e3050f01ba61.png">



__`client2.example.com` で再度接続してみます__

Tunnelblick で、次のようなログが出て接続に失敗しているのが確認できます。

```text
2020-04-21 00:17:46.684079 TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
2020-04-21 00:17:46.684190 TLS Error: TLS handshake failed
```

以上です。