AWS Client VPN 設定メモ

先日、試行錯誤しつつ設定する機会があったので、すでに同種の記事は多数出回っていますがメモとして残しておきます。

# 1. AWS Client VPN について

AWS の説明ページ、

 * **[AWS Client VPN とは](https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-admin/what-is.html)**

によると、

> AWS Client VPN は、AWS リソースや、オンプレミスネットワーク内のリソースに安全にアクセスできるようにする、クライアントベースのマネージド VPN サービスです。クライアント VPN を使用すると、OpenVPN ベースの VPN クライアントを使用して、どこからでもリソースにアクセスできます。

です。

 * AWS リソースに安全にアクセスできる
 * OpenVPN ベースの VPN クライアントを使用

が大事なポイントですが、

 * オンプレミスネットワーク内のリソースにも安全にアクセスできる

というのも場合によっては重要なポイントになってきます（後述）。

また、クライアントからアクセスする際の認証オプションとして、

 * Active Directory 認証
 * 証明書による相互認証

のいずれかまたは両方（併用）を選ぶことが可能です。

今回のメモでは、併用を前提に話を進めます。

---

**補足：**

**「Active Directory 認証」** では、サーバ（VPN エンドポイント）に接続するクライアントを、ユーザ名とパスワードによって認証することができます（クライアント側からは証明書で接続先サーバの正当性を確認）。

この場合、ユーザ名＋パスワードの漏洩がない限りユーザの正当性の確認をすることはできますが、クライアント機器が何なのかを判別することはできませんので、例えば VPN 利用ルールとして「私用端末からの接続を禁止」を定めていたとしても、仕組みで防ぐことはできません。

一方、**「証明書による相互認証」** では、サーバから見たクライアントと、クライアントから見たサーバを相互に認証することができます。

 * 接続してきたクライアント機器が正当なものであることをサーバが認証
 * 接続先が正当なサーバであることをクライアント機器が認証（不正なサーバへの誤接続防止）

ができる一方、ユーザの正当性を確認することはできませんので、クライアント機器が盗まれた場合、管理者がそれを知ってサーバ側で証明書の無効化をするまでの間はそのクライアント機器から不正なユーザがサーバに接続してしまう可能性があります。

これらの点を考慮して、いずれか一方を選択するか併用するかを判断します。

なお、一般的な OpenVPN サーバで利用可能な「クライアント証明書にパスワードを設定する」方法は Client VPN では利用できません。

---

# 2. オレオレ認証局（CA）を立てる

いずれの認証オプションを選ぶ場合も、認証局（CA）から発行されたサーバ証明書とキーを ACM（AWS Certificate Manager）にインポートする必要があります。

加えて、**「証明書による相互認証」** を行う場合は、クライアント証明書とキーを ACM にインポートする必要があります。

---

**2022/04/08 追記：**
クライアント証明書とキーを ACM にインポートする必要があるのは、サーバ証明書とは別の認証局で証明書を発行した場合のみです。

---

通常、Client VPN は閉じた組織内での利用に限られると思いますので、今回はコスト節約の観点からオレオレ認証局を立てて発行することにします。

以降、Amazon Linux 2 上での実行例です。EasyRSA3 をインストールしています（後の証明書の発行についても EasyRSA3 前提です）。

```bash:EasyRSA3インストール
$ sudo yum install git
※git未インストールの場合
$ cd /home/ec2-user/
※置き場所は任意で
$ git clone https://github.com/OpenVPN/easy-rsa.git
$ cd easy-rsa/easyrsa3/
$ ./easyrsa init-pki
$ ./easyrsa build-ca nopass
```

なお、以下の点に注意が必要です。

 * 認証局（CA）証明書・サーバ証明書・クライアント証明書には有効期限がある
 * 有効期限到来前に証明書の更新が必要
 * ACM ではインポートした証明書の自動更新は行ってくれない

証明書は自身で更新し、ACM に再インポートする必要があります。

また、認証局（CA）証明書は設定ファイル（.ovpn）に埋め込まれるため、認証局証明書を更新したときは設定ファイルの再ダウンロードが必要です。

---

**補足：**

Windows で認証局を立てる場合は、こちらを参考にします。

 * **[vpnux PKI Manager – 証明書管理ツール](https://www.openvpn.jp/vpnux-pki/)（OpenVPN.JP）**

---

# 3. 証明書を発行し、ACM にインポートする

以降、引き続き EasyRSA3 を使います。

なお、コマンドラインで ACM にインポートする場合は、あらかじめ EasyRSA3 をインストールした環境に ACM の証明書をアップロードするのに必要な権限を付けておきます（例：**「AWSCertificateManagerFullAccess」** ポリシー付きのロールを EC2 に IAM Roleで割り当て）。

## 3-1. サーバ証明書発行・インポート

認証局インストールからの続きです。

```bash:サーバ証明書発行・インポート
$ ./easyrsa build-server-full server nopass
$ mkdir published
$ cp pki/ca.crt published/
$ cp pki/issued/server.crt published/
$ cp pki/private/server.key published/
$ cd published/
$ aws acm import-certificate --certificate file://server.crt --private-key file://server.key --certificate-chain file://ca.crt --region ap-northeast-1
```

インポートしたサーバ証明書をコンソールで確認し、名前（タグ）をつけておくと良いでしょう。

## 3-2. クライアント証明書発行・インポート（相互認証の場合）

---

**2022/04/08 追記：**
サーバと同じ認証局で発行したクライアント証明書はインポート不要です。

---

```bash:クライアント証明書発行・インポート
$ cd /home/ec2-user/easy-rsa/easyrsa3/
$ ./easyrsa build-client-full hmatsu47 nopass
※クライアント名「hmatsu47」は適宜変更。以降同じ。
$ cp pki/issued/hmatsu47.crt published/
$ cp pki/private/hmatsu47.key published/
$ cd published/
$ aws acm import-certificate --certificate file://hmatsu47.crt --private-key file://hmatsu47.key --certificate-chain file://ca.crt --region ap-northeast-1
```

インポートしたクライアント証明書をコンソールで確認し、こちらも名前（タグ）をつけて判別しやすくしておくと良いでしょう。

# 4. Client VPN エンドポイントを作成する

以降、コンソールからの設定例です。

なお、ちょうど今回の準備が終わった 2019/06/14 に、CloudFormation での作成がサポートされました（ああ、タイミング悪い…）。

 * **[AWS Client VPN now includes support for AWS CloudFormation](https://aws.amazon.com/jp/about-aws/whats-new/2019/06/aws-client-vpn-includes-support-for-aws-cloudformation/)**
 * **[AWS::EC2::ClientVpnAuthorizationRule](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-clientvpnauthorizationrule.html)**
 * **[AWS::EC2::ClientVpnEndpoint](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-clientvpnendpoint.html)**
 * **[AWS::EC2::ClientVpnRoute](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-clientvpnroute.html)**
 * **[AWS::EC2::ClientVpnTargetNetworkAssociation
](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-clientvpntargetnetworkassociation.html)**

## 4-1. VPC →クライアント VPN エンドポイント→クライアント VPN エンドポイント作成

![01_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/8119b083-1fcb-56eb-1659-45cfa95e4030.png)

## 4-2. 必要項目を入力・選択してエンドポイントを作成

![01_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/3762710a-614e-9b08-11f2-fd10f7ff0e85.png)

![01_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/d7ec4c67-0650-5cfc-1c63-cf4a4d6b4b71.png)

---

**2021/01/07 追記：**
VPN 接続中、クライアントから VPN を経由せずに直接インターネットに通信させたい場合は**スプリットトンネル**を有効にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/6188c83c-86e0-88a2-d599-b64ea7249305.png)

---

## 4-3. 関連付けタブ→ Associate（関連付け）

![02_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/622f236b-8f79-3b01-4101-9e2fec8a2f2e.png)

## 4-4. ターゲットネットワークへの関連付けを作成

![02_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/124f3aa4-d6b0-3f1c-a6e2-cd0ebeb18818.png)

※必要なサブネットを全て関連付けします。

![02_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/404286b6-e450-75dd-deda-f4bd47e0e178.png)

## 4-5. 認証タブ→ Authorize Ingress（受信の承認）

![04_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/2f377eec-6e29-f340-117b-fea3bbac2f43.png)

## 4-6. 認証ルールを追加

![04_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/f723b10f-e272-91e5-136a-bf585c6bfc78.png)

## 4-7. その他

 * クライアントからの接続に対してアクセス制御を行う場合、セキュリティグループを設定し、セキュリティグループタブで適用します。
 * **スプリットトンネルが無効**の場合、OpenVPN クライアントで Client VPN エンドポイントに接続している間、クライアントからの全てのトラフィックが VPN エンドポイントにルーティングされます。
 * Client VPN では、AWS のネットワークとしては例外的に **「外部からの接続を AWS 外にルーティングする」** ことが可能です。つまり、（スプリットトンネル無効の設定で）接続中のクライアントからインターネットアクセスをさせたい場合、以下の設定を追加します。
  * 認証タブで 0.0.0.0/0 の受信を承認
  * ルートテーブルタブで 0.0.0.0/0 を追加（このとき指定するサブネットはインターネットアクセス可能な状態にしておく）
  * 詳しくは **6. 参考情報**の DevelopersIO の記事を参照
 * 同様に、認証（受信の承認）＋ルーティング設定により、（この記事の冒頭で触れたとおり）オンプレミスネットワークへのアクセスも可能です。

# 5. クライアントを設定する

クライアントソフトウェアには、テキストファイルに設定の読み込みが可能なものを使うと便利です。

ここでは、Windows 版の OpenVPN（2.4.7）を使います。

 * **[OpenVPNのインストール（Windows編）](https://www.openvpn.jp/document/install-windows/)（OpenVPN.JP）**

## 5-1. VPC →クライアント VPN エンドポイント→クライアント VPN エンドポイント選択→クライアント設定のダウンロード

![07_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/2dbf25de-0201-5d80-f996-f516f4b2822d.png)

※参照権限のみのユーザで管理コンソールにログインしている場合、ダウンロードボタンをクリックしても設定ファイルはダウンロードできません（無反応）。

## 5-2. 設定ファイルの末尾に「cert」「key」のファイル名を追記

※証明書による相互認証を使う場合。5-3. の証明書・キーファイルも同様。

![08_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/646c23c7-41e1-07d8-307c-2077346021fb.png)


---

**2021/01/07 追記：**
スプリットトンネルがサポートされる前にダウンロードした設定ファイル（.ovpn）を使うと、後からエンドポイントのスプリットトンネルを有効にした場合にエラーで接続できなくなるようです。その場合、あらためて設定ファイルをダウンロードし直して「cert」「key」のファイル名を追記すると良いでしょう。

---

## 5-3. 設定（.ovpn）・証明書（.crt）・キー（.key）ファイルを設定フォルダに保存

証明書・キーファイルは、認証局から安全な方法でダウンロードします。

保存先フォルダは以下のうちのいずれか 1 つを選択します。

 * 個人（特定ユーザ）で利用する場合
  * 【ユーザフォルダ】\OpenVPN\config
 * 全ユーザで利用する場合
  * Program Files\OpenVPN\config

# 5-4. 接続／切断

タスクバーにある OpenVPN のアイコンを右クリックして出てきたメニューで **「接続」「切断」** をクリックします。

![09_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/123899/a59d0331-e7d8-c67e-635a-d67ed23009c5.png)

Active Directory 認証を使う場合、途中でユーザ名とパスワードの入力を求められます（入力するユーザ名はドメイン名不要）。

# 6. 参考情報

 * **[AWS Client VPNを試してみる](https://qiita.com/atsumjp/items/837d8ea5763bb985ff8d)（atsumjp さん）**
  * Mac でクライアントに Tunnelblick を使う例です。
 * **[AWS Client VPN で VPC を経由してインターネットへアクセスする](https://dev.classmethod.jp/cloud/aws/internet-access-with-client-vpn/)（DevelopersIO）**
  * VPN 接続中のクライアントからインターネットアクセスを許可する例です。
 * **[[AWS Client VPN] VPC を経由して固定のIPでインターネットへアクセスする](https://dev.classmethod.jp/cloud/aws/aws-client-vpn-with-static-ip/)（同上）**
  * こちらは固定 グローバル IP アドレスでインターネットアクセスさせる例です。