ウサギでもできるStrongswan on EC2(CentOS7)でリモートVPNサーバを構築

# 序章
今担当しているお客様は、FWの内側に仮想Linuxサーバがあり、Strongswanを用いてリモートワークを実施していました。
社内のサーバ群をクラウドに移行する際、そのFWとAWSのsite-to-site VPNを接続したところ、StrongswanのIPsec通信を奪ってしまうという事件が発生したのです。
そりゃそうですよね。一つのIPで同じポートの通信を複数の機器が受けられるわけがありません。
そのため、site-to-site VPN接続のタイミングでリモートVPNサーバをクラウド化してしまおうという案が出てきたため、急遽構築することになって検証したのがきっかけで作成した記事です。

※実はFWでもリモートVPN設定は可能ですが、この機器ではNW担当の実績がないこと。更にまもなくFWを置き換える予定で置き換え後はFWでリモートVPNになるため、今回の設定は暫定であること。それもあってリモートPCの設定変更が容易であることを求められたため、接続先IPの変更のみで置き換えられるStrongswanの案になったという背景があります。

なお、初めはAmazonLinux2で構築しはじめたのですが、カーネルが異なっているからか、xl2tpdが起動しませんでした。おそらくソースコードからmakeすればイケると思うのですが、面倒なのでCentOS7での構築としました。

環境が異なりますが、こちらをはじめ、様々な記事を参考にさせていただきました。
[strongSwan + xl2tpd でVPN(L2TP/IPsec)を構築する](https://qiita.com/namoshika/items/30c348b56474d422ef64 "strongSwan + xl2tpd でVPN(L2TP/IPsec)を構築する")

と、いうわけで、まずはじめに~~服を脱ぎます~~　ｹﾞﾌﾝｹﾞﾌﾝ…　VPC、サブネットを用意します。
# NW構築
今回は以下のようにNWを構成しました。
VPNサーバは公開サーバになるため、ディフォルトゲートウェイをインタネットGWに向けた、パブリックサブネットとしています。
## NW構成詳細
| 用途 | CIDR |
|:-:|:-:|
|VPC|172.16.0.0/16|
|EC2配置サブネット|172.16.1.0/24|
|リモートPC割当IP|10.0.11.0/24|

## NW構築
サブネットを用意したら、インターネットGWのアタッチ、ディフォルトルートの設定を実施しておきます。
# VPNサーバの構築
次に、用意したサブネットにEC2を構築します。
## VPNサーバOSについて
今回はCentOS7を利用することにしました。
「CentOS 7 x86_64 - LATEST」でコミュニティAMIを検索し、赤丸の日付が新しいものを選択すると良いでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/d74e0851-526d-27f4-dda3-12fa4c59663e.png)
## セキュリティグループ
以下のようにセキュリティグループを設定しておきます。
[インバウンド]
|ポート|プロトコル|ソース|
|:-:|:-:|:-:|
|500|UDP|0.0.0.0/0|
|4500|UDP|0.0.0.0/0|
|22|TCP|自宅のIP|

## ElasticIPのアタッチ
ElasticIPを割り当てし、VPNサーバにElasticIPをアタッチします。
## ソース/宛先チェックを変更する
VPNサーバは接続元PCからのIPをNATする動作となるため、EC2の設定を変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/fc86ce28-8ab7-3ba4-5f1d-f8ad38af7202.png)
インスタンスを選択し、[アクション]-[ネットワーキング]-[ソース/宛先チェックを変更]を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/4714b6c7-5fb1-a8aa-f0d4-a677d4631bdd.png)
「停止」にチェックを入れて「保存」を押します。
ElasticIPを割り当てし、VPNサーバにElasticIPをアタッチします。
## サーバへのログイン
CentOSなので、OSのログインユーザー名は「centos」となり、あとはパスフレーズなしのキーファイルでログイン可能となります。
## セキュリティ設定を解除
ログイン後はまず、SELINUXを無効化します。
先人のお知恵を拝借しましょう。
[CentOS7 SELinuxの無効化手順](https://qiita.com/mattsun/items/470581ff34f87c7eb21d "CentOS7 SELinuxの無効化手順")
## 必要コンポーネントのインストール
以下のコマンドを投入していきます。
```bash
sudo -s
yum install -y xl2tpd strongswan
```
これで必要なパッケージがインストールできます。
## Strongswanの設定
パッケージの一つ目、Strongswanを設定していきます。
### ipsec.confの設定
```bash:/etc/strongswan/ipsec.conf
config setup
conn %default
    auto=add
conn L2TP
    type=transport
    leftauth=psk
    rightauth=ps
```
最低限の設定です。
### ipsec.secretsの設定
```bash:/etc/strongswan/ipsec.secrets
: PSK "secretkey"
```
事前共有キーとなる、任意の文字列を記載します。
このキーは全員が同じ文字列を用います。
### Strongswanの再起動と自動起動設定
```bash
systemctl restart strongswan
systemctl status strongswan
systemctl enable strongswan
systemctl is-enabled strongswan
```
サービスが起動したかと、自動起動に設定できたかも確認しておきましょう。
## xl2tpdの設定
次にxl2tpdを設定していきます。
### ipsec.confの設定
```bash:/etc/xl2tpd/xl2tpd.conf
[lns default]
ip range = 10.0.11.100-10.0.11.200
local ip = 10.0.11.99
require chap = yes
refuse pap = yes
require authentication = yes
name = xl2tpd
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
```
IPレンジは接続してくるリモートPCに払出すIPアドレス帯を指定します。
ローカルIPはそのリモートPCから見たVPNルーターのローカルアドレスを指定します。
### options.xl2tpdの設定
```/etc/ppp/options.xl2tpd
ipcp-accept-local
ipcp-accept-remote
ms-dns  8.8.8.8
ms-dns  1.1.1.1
noccp
auth
idle 1800
mtu 1500
mru 1500
nodefaultroute
proxyarp
connect-delay 5000
require-pap
refuse-chap
refuse-mschap
refuse-mschap-v2
logfile /var/log/xl2tpd.log
```
リモートPCにDHCPで払出すオプションとして、DNSのアドレスやログファイル名などを指定します。
### chap-secretsの設定
```/etc/ppp/chap-secrets
# Secrets for authentication using CHAP
# client        server  secret                  IP addresses
usagisan xl2tpd Passw0rd 10.0.11.102
```
最後にユーザー認証情報を記載していきます。
### xl2tpdの再起動と自動起動設定
```bash
systemctl restart xl2tpd
systemctl status xl2tpd
systemctl enable xl2tpd
systemctl is-enabled xl2tpd
```
こちらもサービスの起動と、自動起動設定を確認しておきましょう。
## パケットの転送を有効化する
OSの設定でIPフォワードをONにします。
```bash
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
```
これでサーバの構築は完了です。
# ルーティング設定を追加する
## リモートPC向けのルートを追加する
今回リモートPCに割り当てたCIDR「10.0.11.0/24」を、「VPNサーバのeni」に向けます。
|送信先|ターゲット|
|:-:|:-:|
|eni-0000000000|10.0.11.0/24|

ここまで設定すれば、AWS側の設定は完了です。

# リモートPCの設定(Windows10)
ここからはリモートで接続するPCPC側の設定をしていきます。
## NATトラバーサル機能を有効にする
WindowsPCの場合、今回のようなNATトラバーサル機能を用いたVPNルーターにT2TPで接続する場合、機能がディフォルトOFFになっているため、ネゴシエーションの時点でエラーとなり接続することができません。
そのため、まずはそれを解決していきます。
### レジストリの追記
Powershellを管理者モードで開き、以下コマンドを打ちます。
```powershell
New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\PolicyAgent -Name AssumeUDPEncapsulationContextOnSendRule -PropertyType DWord -Value 2
```
### PCの再起動
レジストリを変更出来たら適用するためにPCの再起動が必要です。
## VPN接続の作成
ここからVPN接続を作成します。
### VPN接続を作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/bf3a1465-c1be-215f-aa20-cffcb1671ba9.png)
[設定]-[ネットワークとインターネット]-[VPN]から「VPN接続を追加する」を押します
|項目|値|
|:-:|:-:|
|接続名|任意の文字列|
|サーバー名またはアドレス|VPNサーバのEIP|
|VPNの種類|事前共有キーを使ったL2TP/IPsec|
|事前共有キー|ipsec.secretsに書いた文字列|
|サインイン情報の種類|ユーザー名とパスワード|
|ユーザー名|ユーザー名|
|パスワード|パスワード|

ユーザー名とパスワードは「chap-secrets」に記載したものを利用します。

### 接続の詳細を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/0efb95e3-c358-eaef-b6ff-44b9f7b1f553.png)
[設定]-[ネットワークとインターネット]から「ネットワークと共有センター」を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/1344f66d-0a8f-8f82-a44b-30533e5fecc3.png)
「アダプターの設定の変更」を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/f3529f8a-dc1d-5c01-ef44-d42378bb2b17.png)
作成したVPN接続を選択し、「この接続の設定を変更する」を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/d879f969-f189-1358-126f-1c1d94ce9813.png)
「セキュリティ」タブを開き、「認証」の「次のプロトコルを許可する」を選択し、以下の二つにチェックを入れてOKを押す。
* チャレンジハンドシェイク認証プロトコル(CHAP)
* Microsoft CHAP Version2(MS-CHAP v2)
# 接続テスト
これですべての設定が完了したので、タスクトレイのネットワークアイコンから接続することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316614/d850fff3-06ec-f322-e854-e1881e7a0c8b.png)
接続後、同じVPC内にある別のサーバなどにRDP接続なりを試してみてください。