AWS x OpenVPNでP2P接続できる環境を作る

# 目的

####***遠隔地の端末XにNATを挟まずに接続したい！***####
####***端末Xの既存環境はいじりたくない！***####
(ついでに、クラウドと連携できると夢が広がるね！)
******

上記実現のために、

1. VPN接続、かつルーティングのみで構成する
2. 端末Xが接続するルーター(ex.ラズパイ等)に、VPNクライアント環境を構築する
3. AWSパブリックサブネット上にVPNサーバーを構築する

想定するネットワークは以下の通り

![image](https://qiita-image-store.s3.amazonaws.com/0/126345/5620027f-c088-b94d-eb50-cd3db2fde58a.png)

赤、青、黒色のIPアドレスはそれぞれのサブネット内で割り振られたもので、紫色のIPアドレスはVPNネットワーク上で割り振られるアドレスとする

ここで、赤のVPNクライアント(10.8.0.6)から、青のVPNクライアントサブネット上の端末X(192.168.20.5)に、***プライベートアドレス指定でアクセス可能な環境を構築する***

# 具体的にやること

上記環境を構築するために、以下の手順で作業を行う

1. AWSパブリックインスタンスにOpenVPNを導入する
2. OpenVPN接続用の証明書を発行する
3. サーバーの準備をする
4. クライアントの準備をする
	1. mac編
	2. ubuntu編
5. 各サブネットに接続確認を行う

なお、VPNクライアントすべてに対して、OpenVPN環境は未導入、VPNクライアント兼ルーターにはRaspberryPiを使用し、すでにルーターとして利用可能な状態であるとする

また、赤のVPNクライアントのOSはmacとする

# 1. AWSにOpenVPNを導入する
## 前提条件
AWS上のVPC、及びその内部のパブリックサブネットとプライベートサブネットは作成済みであるものとし、作成方法などの解説は行わない

以下に最低限必要な設定を示す

AWSパブリックサブネット上でVPNサーバーとして利用するインスタンスのOSには、Amazon Linux AMI 2016.09.1(HVM)を使用する

ボリュームサイズや処理性能は特に指定しないが、セキュリティグループ設定の際に、ssh及びVPN接続用に以下のポートを空けておく

* ssh接続用ポート
	* タイプ：SSH
	* プロトコル：TCP
	* ポート範囲：22
	* 送信元：任意
* VPN接続用ポート
	* タイプ：カスタムUDPルール
	* プロトコル：UDP
	* ポート範囲：1194
	* 送信元：任意

VPN接続用のポート設定は以降で利用する

他、必要に応じてソフトウェアの更新などを行っておく

合わせて、接続確認用にプライベートサブネット上にインスタンス(10.0.1.228)を立ち上げておく

パブリックサブネット上のインスタンスをOpenVPNサーバーとして動かすに当たり、導入が必要なものは以下の二つである

***1. OpenVPN(v2.3.12)***
***2. easy-rsa(証明書方式の場合)(v3.0.0)***

まずはsshでインスタンスに接続し、これらを導入する

## OpenVPNのインストール
OpenVPNは`yum`でインストールできる

```
$ sudo yum install -y openvpn
...
完了しました!
```

基本的に、自動で自動起動に登録される

## easy-rsaのインストール
拡張性、安全性、そして複数のクライアントを管理するVPNを構成するために、easy-rsaを利用する

easy-rsaはバイナリを取得、解凍して導入する

また、以降の作業は`/usr/local/EasyRSA/`ディレクトリ下で行うので移動しておく

```
$ wget https://github.com/OpenVPN/easy-rsa/releases/download/v3.0.0-rc2/EasyRSA-3.0.0-rc2.tgz
...
2017-03-28 02:22:13 (107 KB/s) - `EasyRSA-3.0.0-rc2.tgz' へ保存完了 [34886/34886]
$ tar -xvzf EasyRSA-3.0.0-rc2.tgz
...
EasyRSA-3.0.0-rc2/easyrsa
$ sudo mv EasyRSA-3.0.0-rc2 /usr/local/EasyRSA
$ cd /usr/local/EasyRSA/
```


# 2. OpenVPN接続用の証明書を発行する
## easy-rsaの初期設定
easy-rsaを導入した際には、以下のコマンドで認証情報を生成するための初期設定を行う必要がある

1. init-pki
2. build-ca
3. gen-dh

### init-pki
`init-pki`を実行し、初期化を行う

```
$ ./easyrsa init-pki
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /usr/local/EasyRSA/pki
```

ここで生成された`/usr/local/EasyRSA/pki/`ディレクトリに、以降の手順で生成する認証用ファイルが配置される

### build-ca
`build-ca`で認証局を作成する

```
$ ./easyrsa build-ca
Generating a 2048 bit RSA private key
.+++
..+++
writing new private key to '/usr/local/EasyRSA/pki/private/ca.key'
Enter PEM pass phrase:<パスフレーズ>
Verifying - Enter PEM pass phrase:<パスフレーズ(確認)>
-----
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]:<ホスト名等>
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/usr/local/EasyRSA/pki/ca.crt
```

* <パスフレーズ>：任意のパスワード
	* **証明書の発行の際に必要、覚えておくこと**
* <ホスト名等>：認証局を識別するための名称
	* 本気で運用するならばつけるべき
	* **今回はVPN接続目的のみのため適当な名称 or デフォルトで可**

### gen-dh
`gen-dh`でDHパラメータを生成する

* DH:Diffie-Hellman
	* 公開鍵暗号方式の具体的な方法の一つ
	* DHは公開鍵暗号方式の概念を初めて公開した人たちの名前

```
$  ./easyrsa gen-dh
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
...

DH parameters of size 2048 created at /usr/local/EasyRSA/pki/dh.pem
```

以上で、証明書の発行準備は完了である

次の手順から、実際に使用するサーバー/クライアント向けの認証ファイルの生成を行っていく

## サーバー用秘密鍵・証明書の生成
`build-server-full`でサーバー用秘密鍵と証明書の作成、及び署名を行う

```
$ ./easyrsa build-server-full server nopass
Generating a 2048 bit RSA private key
..........................+++
..................................+++
writing new private key to '/usr/local/EasyRSA/pki/private/server.key'
-----
Using configuration from /usr/local/EasyRSA/openssl-1.0.cnf
Enter pass phrase for /usr/local/EasyRSA/pki/private/ca.key:<登録したパスフレーズ>
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'server'
Certificate is to be certified until Mar 26 02:26:31 2027 GMT (3650 days)

Write out database with 1 new entries
Data Base Updated
```

* 証明書読み込み時にパスフレーズ不要とする場合は`nopass`オプションを使用する
	* **オプションを使用しない場合、VPN接続時に毎回、パスフレーズの入力が求められる**
* 途中でCA証明書生成時に設定したパスフレーズの入力を求められるので入力する

## クライアント用秘密鍵・証明書の生成
`build-client-full`でクライアント用秘密鍵と証明書の作成、及び署名を行う

以下に、ファイルを生成するコマンドの例を示す

```
$  ./easyrsa build-client-full client1 nopass

Generating a 2048 bit RSA private key
.....................................................................+++
.........+++
writing new private key to '/usr/local/EasyRSA/pki/private/client1.key'
-----
Using configuration from /usr/local/EasyRSA/openssl-1.0.cnf
Enter pass phrase for /usr/local/EasyRSA/pki/private/ca.key:<登録したパスフレーズ>
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'client1'
Certificate is to be certified until Mar  8 07:31:37 2025 GMT (3650 days)
 
Write out database with 1 new entries
Data Base Updated
```

* `client1`には任意のクライアント名を指定する
	* ex, client1,client2,client3,and more...
	* ex, alpha,beta,and more...
* パスフレーズ不要とする場合は`nopass`オプションを使用する

同様に、必要なだけクライアント用の秘密鍵、証明書の生成を行う

今回はclient1に加え、ルーター兼用クライアント向けにclient2も作成しておく

その際のコマンドは以下のようになる

```
$  ./easyrsa build-client-full client2 nopass
```

以上で、認証に必要なファイルの生成は終了である

次に、これらのファイルを配置し、VPN接続を行うための設定を行っていく

# 3. サーバーの準備をする
これまでに生成したファイルのうち、以下のファイルを`/etc/openvpn/`に移動する

* ca証明書：ca.crt
* サーバー用証明書：server.crt
* サーバー用秘密鍵：server.key
* DHパラメータ：dh2048.pem

```
$ sudo cp pki/ca.crt /etc/openvpn/
$ sudo cp pki/issued/server.crt /etc/openvpn/
$ sudo cp pki/private/server.key /etc/openvpn/
$ sudo cp pki/dh.pem /etc/openvpn/dh2048.pem
```

VPNサーバー設定ファイルのサンプルをコピーし、それをもとに実際にサーバーとして動かす際の設定を行う

サンプルの置かれているディレクトリは以下の通りである

```
/usr/share/doc/openvpn-2.3.12/sample/sample-config-files/server.conf
```

このサーバー設定ファイルも`/etc/openvpn/`に配置する

```
$ sudo cp /usr/share/doc/openvpn-2.3.12/sample/sample-config-files/server.conf /etc/openvpn/server.conf
```

サーバー設定ファイルを環境に合わせて修正する

好みのエディタで開き、編集する

```
$ sudo vi /etc/openvpn/server.conf
```

以下に編集例(抜粋)を示す

```
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "route 10.0.0.0 255.255.0.0"
push "route 192.168.20.0 255.255.255.0"
client-config-dir ccd
route 192.168.20.0 255.255.255.0
client-to-client
keepalive 10 120
comp-lzo
persist-key
persist-tun
status openvpn-status.log
log-append  openvpn.log
verb 3
```

* `port`、`proto`、`tun`はそれぞれ接続ポート番号、接続方式(udp/tcp)、インターフェイス形式(tap/tun)を指定する
	* 本環境では、UDP1194ポートをVPN接続用として開放しているので、これを指定する
	* この設定は、後述のクライアント設定ファイルでも使用する
* `ca`、`cert`、`key`、`dh`は、各々のサーバー用ファイルの場所を、`/etc/openvpn/`ディレクトリを基準とした相対パスで入力する
* `server`には、サーバーサブネットアドレス範囲を指定する
* `push "route ..."`には、クライアントへ通知する、クライアントからアクセス可能なサブネットを指定する
	* 実際の動作としては、クライアントのルーティングテーブルに、指定したサブネットへアクセスする場合に、VPNサーバーへとルーティングするような設定が追加される
* `client-config-dir ccd`オプションは、クライアントサブネットへアクセスする必要がある場合など、クライアントに関する設定が必要な際に有効化する
	* 有効化した際には`/etc/openvpn/ccd/`ディレクトリを作成しないと、openvpnそのものが正常に起動しなくなるので注意する(次の手順で作成)
	* ディレクトリ名称`ccd`は任意のディレクトリを指定可能
	* 合わせて、`route ...`により、クライアントサブネットとして192.168.20.0/24サブネットを、サーバーのルーティングテーブルに追加している
* `client-to-client`オプションは、クライアント間の通信、及びクライアントサブネット間の通信を許可する場合に有効化する
* 設定項目については以下も参照
	* [OpenVPN How to](https://www.openvpn.jp/document/how-to/)

上記設定では、サーバー側からVPNクライアントに通知するルーティング設定として、以下の二つのサブネットにアクセスする際に、サーバーへとルーティングするような設定を加えている

* 192.168.20.0/24
* 10.0.0.0/16

前者のサブネットは、青のVPNクライアント兼ルーターの属するクライアントサブネットを示す

後者のサブネットは、AWSのVPC上のサブネット`10.0.0.0/24`(サーバーの立っているパブリックサブネット)と`10.0.1.0/24`(プライベートサブネット)を内包する

## クライアントサブネットへの接続設定
VPNサーバーに、アクセス可能なVPNクライアントサブネットへのルートを登録する

サーバー設定ファイル上で追加したVPNクライアントサブネット192.168.20.0/24への通信をclient2へルーティングするよう設定を加える

まず、`/etc/openvpn/ccd/`ディレクトリを作成する

```
$ sudo mkdir /etc/openvpn/ccd
```

ディレクトリは、サーバー設定ファイル上で行った指定に従う

次に、ccdディレクトリ中にclient2という名称のファイルを生成し、`iroute ...`にサブネットを指定する

```
$sudo vi /etc/openvpn/ccd/client2

iroute 192.168.20.0 255.255.255.0
```

この`iroute ...`の設定は、OpenVPNサーバーに対し、192.168.20.0/24サブネット(上記設定の場合)への通信を、client2にルートさせるためのものである

## サーバーをルーターとして使用可能にする
VPNサーバーにルーターとしての機能を持たせるためには、IPフォワーディングを許可する必要がある

以下のファイルを編集し、`net.ipv4.ip_forward`を1に変更する

```
$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward = 1
```

以上で、インスタンス内で実施する必要のある設定は終了である

次に、AWS上で、ルーティング設定の修正を行う

## AWS上の設定
AWS上のサブネットとデータの送受信を行うために、以下の設定を行う必要がある

1. サブネットのルートテーブルへ、VPNネットワークのアドレスを登録
2. VPNサーバーのインスタンスの送信元/送信先の変更チェックの無効化

### ルートテーブルの修正
VPNクライアントからサーバー側サブネットにアクセスするため、サーバー側サブネットのルートテーブルにVPNサブネット(10.8.0.0/24)を登録する

VPNクライアントからサーバー側サブネットに接続するための設定はサーバー側設定ファイル上の`push "route ..."`で実施済みであるが、サーバー側サブネットからVPNクライアントに接続するための情報は与えられていない

よって、AWSサブネットに紐づけられたルートテーブルの設定を修正する

* AWS>[VPC]>[ルートテーブル]>サブネットに関連付けているルートテーブルを選択
        * [ルート]タブ>[編集]>[別ルートの追加]
	* 送信先：10.8.0.0/24(VPNサブネットを指定)
	* ターゲット：OpenVPNインスタンス(のネットワークインターフェイス)を指定

サーバー側サブネットからVPNクライアントサブネットにアクセスする場合には、同様の手順(送信先のみ変更)でルートテーブルに追加する

今回は、サーバー側サブネット-クライアントサブネット間のアクセスはないものと想定し、設定しない

### 送信元/送信先の変更チェックの無効化
AWSのEC2インスタンスは通常、自身のIPアドレス以外を指定した通信を無視する設定となっている

この状態では、クライアントサブネット間の通信のように、自インスタンス以外に向かう通信をルーティングすることができない

よって、この設定を無効化し、通信を受け取ることができるようにする必要がある

以下の設定を行う

* AWS>[EC2]>VPNサーバーのインスタンスを選択
	* [アクション]>[ネットワーキング]>[送信元/送信先の変更チェック]
	* ダイアログにて、有効である場合には無効化を行う

![02.png](https://qiita-image-store.s3.amazonaws.com/0/126345/3fddb48f-73f1-a273-ffc9-9dc55c981f3f.png)

ここまでの設定が終了したならば、念のためインスタンスを再起動させておく

# 4.1. クライアントの準備をする：mac編
以下の環境を想定し、クライアント側の準備を行う
(先述の想定環境中の赤のVPNクライアントを想定する)

* OS:mac(macbook pro)
* VPNクライアント:TunnelBlick(GUI)

## クライアントへ必要なファイルを移動
SCPコマンド等を使用し、以下のファイルをクライアントへ移動する

* ca証明書：ca.crt
* クライアント用秘密鍵：client1.key等
* クライアント証明書：client1.crt等

SCPコマンドを使用して、ファイルをダウンロードする際の例を以下に示す

* 以下はssh接続用のキーが存在する場合の例
	* pemファイル名：xxx.pem
	* ダウンロード元アカウント名：ec2-user(AWSデフォルトの場合)
	* ダウンロード元グローバルIP：xxx.xxx.xxx.xxx(VPNサーバーグローバルIP)
	* ダウンロード元ファイル：/usr/local/EasyRSA/pki/private/ca.key等
	* ダウンロード先ディレクトリ：/etc/openvpn/
* 接続先のポート番号を指定する場合は、`scp -P xxxx -i ...`
* 接続先のパスは絶対パスで指定する

```
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/ca.crt /etc/openvpn
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/issued/client1.crt /etc/openvpn
$ sudo scp -i ~/.ssh/xxx.pem ec2-user@xxx.xxx.xxx.xxx:/usr/local/EasyRSA/pki/private/client1.key /etc/openvpn
```

取得したファイルは`/etc/openvpn/`に配置する

## クライアント設定ファイルの修正
必要があれば、クライアント設定ファイルのサンプルも、クライアントにコピーし使用する

あるいは、[OpenVPNのサイト](https://www.openvpn.jp/document/how-to/)より、コピーして使用することもできる

ファイル名は任意であるが、ここでは`client.conf`とする

以下にクライアント設定ファイルの設定例(抜粋)を示す

```
client
dev tun
proto udp
remote <サーバーグローバルIP> 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client1.crt
key client1.key
comp-lzo
verb 3
```

* `client`は、この設定ファイルを読んだ端末がクライアント側であることを示す
* `dev`、`proto`の指定はサーバー設定ファイル(前述)に従う
* `remote`には、<サーバーグローバルIP>とサーバーポート番号をスペース区切りで記述する
	* <サーバーグローバルIP>にはグローバルIPでなくとも、クライアントからサーバーにアクセスできるIPアドレス、あるいはホスト名を指定する
	* サーバーポート番号の指定はサーバー設定ファイルに従う
* `ca`、`cert`、`key`には、max x tunnelblick環境では、`/etc/openvpn/`ディレクトリを基準としたパスを指定する
	* 今回は`/etc/openvpn/`直下のためディレクトリ指定は行う必要はない

## Tunnelblickへの登録
作成した`client.conf`を、Tunnelblickで開く

* エラーが出た場合は修正する
	* どこが間違っているかは教えてくれる
	* tls設定がサンプル時点で有効になっている場合があるので確認が必要
* 特に、ca証明書のディレクトリ指定などに注意する

問題なく登録が終わったならば、"接続"できるはずである


# 4.2. クライアントの準備をする：ubuntu編
インストール手法などは異なるものの、linux系統でOpenVPNをクライアントとして導入する手順は同一である
(今回は、具体的にRaspberryPiにOpenVPNを導入する場合を想定する)

ここで、クライアントが所属するサブネットワークへのアクセスを許可する場合、VPNクライアント側で直接設定をする必要は基本的には存在しない

サブネットへのルーティング設定は、VPNサーバー側の設定のみで良い

ただし、ルーター上でサブネットの設定を行う必要はあるが、すでにルーターとして使用可能な状態であり、設定済みであるとして、今回は説明しない

ここでは以下の環境を想定し、クライアント側の準備を行う

* OS:ubuntu(macbook pro上でrefind使用)
* VPNクライアント:OpenVPN(CUI)

## OpenVPNのインストール
以下のコマンドでOpenVPNをインストールする

```
$ sudo apt-get install openvpn
```

## クライアントへ必要なファイルを移動
SCPコマンド等を使用し、以下のファイルをクライアントへ移動する

* ca証明書：ca.crt
* クライアント用秘密鍵：client1.key等
* クライアント証明書：client1.crt等

取得したファイルは`/etc/openvpn/`に配置する

## クライアント設定ファイルの修正
VPNクライアント設定ファイルのサンプルをコピーし、それをもとに実際の設定を行う

サンプルの置かれているディレクトリは以下の通りである

```
/usr/share/doc/openvpn-2.3.12/sample/sample-config-files/client.conf
```

サーバー設定ファイルが置かれていたディレクトリと同一である

このクライアント設定ファイルも`/etc/openvpn/`に配置する

```
$ sudo cp /usr/share/doc/openvpn-2.3.12/sample/sample-config-files/client.conf /etc/openvpn/client.conf
```

サーバー設定ファイルを環境に合わせて修正する

好みのエディタで開き、編集する

```
$ sudo vi /etc/openvpn/client.conf
```

以下に編集例(抜粋)を示す

```
client
dev tun
proto udp
remote <サーバーグローバルIP> 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca /etc/openvpn/ca.crt
cert /etc/openvpn/client1.crt
key /etc/openvpn/client1.key
comp-lzo
verb 3
```

* 一番上の`client`は、このファイルを読んだ端末がクライアント側であることを示す
* ***`ca`、`cert`、`key`には絶対パスを指定する***

なお、上記編集例はサーバー設定ファイルの内容に対応する

## VPNサーバーへの接続
VPNのクライアントとして起動させるには、起動の際にクライアント設定ファイルを指定する必要がある

以下のコマンドで、クライアントようコンフィグファイルを指定し、クライアントとしてVPNサーバーへ接続する

```
$ sudo /usr/sbin/openvpn /etc/openvpn/client.conf
...
... Initialization Sequence Completed
```

※コンソールを一つ占有することに注意する

# 5. 各サブネットに接続確認を行う
ここでは、以下のような環境で、クライアントからVPNサーバーを経由し、サーバー側サブネットへ`traceroute`を行う

ここまでの手順通りに行っていれば、問題なく接続できるはずである

`netstat`コマンドなどで、対応するルーティングが行われているか確認する

VPNクライアントから`ping`、あるいは以下のような`traceroute`コマンドが通れば、正常に設定できている

```
$ traceroute 10.0.1.228
traceroute to 10.0.1.228 (10.0.1.228), 64 hops max, 52 byte packets
1 10.8.0.1 (10.8.0.1) xxx ms xxx ms xxx ms
2 10.0.1.228 (10.0.1.228) xxx ms xxx ms xxx ms
$ traceroute 10.8.0.10
traceroute to 10.8.0.10 (10.8.0.10), 64 hops max, 52 byte packets
1 10.8.0.10 (10.8.0.10) xxx ms xxx ms xxx ms
$ traceroute 192.168.20.5
traceroute to 192.168.20.5 (192.168.20.5), 64 hops max, 52 byte packets
1 10.8.0.10 (10.8.0.10) xxx ms xxx ms xxx ms
2 192.168.20.5 (192.168.20.5) xxx ms xxx ms xxx ms
```

####***以上***####