SoftEtherでAWS-自宅間のVPN接続を爆速構築

AWSと自宅をVPNで接続したら便利そうなので、VPNを理解していないけど調べながらやってみました。  

:::note warn
注意
**この記事の記載はセキュリティのベストプラクティスについては考慮していません**。セキュリティリスクが存在している可能性があるのでご注意ください。  
:::

## やりたいこと

やりたいことのイメージはこれです。  
![やりたいこと](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/fc5107a0-0ce1-4e3a-6a2a-0bf902140938.png)

ネットワーク要件を書き出してみると以下のような感じでしょうか…
- 自宅のWindows PCから友人宅のWindows PCにリモートデスクトップ接続できる。逆も可
- 自宅のWindows PCから友人宅のUbuntuサーバにSSHやNFSで接続できる
- 自宅および友人宅からAWSのパブリックサブネットのEC2インスタンスにSSHで接続できる
- 自宅および友人宅からAWSのプライベートサブネットのEC2インスタンスにSSHで接続できる
- 自宅および友人宅からインターネットを経由しないでAWSの各サービスにアクセスできる
- EC2インスタンスからはもちろんAWSの各サービスにアクセスできる
- インターネットには(どこからでもいいんだけど)AWSのインターネットゲートウェイから抜ける
- 速度はあんまり気にしない

ググってみるとAWS Client VPNで簡単にできるよ！ということのようなのですが、
> AWS Client VPN エンドポイントアソシエーション　0.15USD/時間
> AWS Client VPN 接続　0.05USD/時間

利用料がまあまあかかる。趣味で使うには痛い…  
セルフマネージドなら、(安全性や運用性は不安ですが)お財布には優しいのでは？というのが今回の趣旨です。  

## 構築の流れ

構築の流れは以下の通りです。  

1. EC2インスタンスを準備して
1. VPNサーバをインストール・設定して
1. VPNクライアントを接続する

## VPNサーバにするインスタンスの準備

最初の工程はEC2インスタンスの準備です。  
Elastic IPアドレスを紐付けたインスタンスを用意します。  
利用料を極限までケチるために、今回は```t3a.nano```を選んでみました。  

また、VPNサーバとVPNクライアントの間の通信にはL2TP/IPsecというプロトコルを用いるので、これに対応したポートのインバウンドトラフィックをセキュリティグループで許可します。  
ポートは以下の2つです。  

- UDP/500
- UDP/4500

SSHで接続して初期設定をしていきます。  

```sh
# メモリ不足でyum updateできないことがあるのでスワップ領域を増やす
sudo dd if=/dev/zero of=/swap.extended bs=1M count=512
sudo mkswap /swap.extended
sudo chmod 0600 /swap.extended
sudo swapon /swap.extended
sudo sh -c "echo '/swap.extended swap swap defaults 0 0' >> /etc/fstab"

# yum update
sudo yum update
# 次の工程で使うパッケージの導入
sudo yum install gcc
```

## SoftEther VPN Serverのインストール

次の工程はVPNサーバのインストールです。  
今回は導入が簡単そうなSoftEtherでチャレンジします！  

とりあえず[バージョン更新履歴](https://ja.softether.org/5-download/history)で最新バージョンを確認したら、ソースコードをダウンロードします。  
ソースコードは https://ja.softether.org/5-download/src から指示通りにリンクを辿ればダウンロードできます。  
今回は```softether-vpnserver-v4.42-9798-rtm-2023.06.30-linux-x64-64bit.tar.gz```がよさそうなので、そのダウンロードリンクを取得します。  

![ダウンロードページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/ae7640ea-3550-5349-e3c4-95b89e88a6f5.png)

ソースコードからインストールしていきます。  

```sh
# ダウンロード
wget https://www.softether-download.com/files/softether/v4.42-9798-rtm-2023.06.30-tree/Linux/SoftEther_VPN_Server/64bit_-_Intel_x64_or_AMD64/softether-vpnserver-v4.42-9798-rtm-2023.06.30-linux-x64-64bit.tar.gz
# 解凍
tar zxvf softether-vpnserver-v4.42-9798-rtm-2023.06.30-linux-x64-64bit.tar.gz
# make
cd vpnserver
make
cd ..
# インストール先のディレクトリに配置してファイル権限を付与
sudo mv vpnserver /opt/vpnserver
sudo chown root:root -R /opt/vpnserver
```

## コマンドラインでVPNサーバの設定

次の工程はVPNサーバの設定です。  
まず、この後の設定で使うEC2インスタンスのNICの名前と、VPCのネットワークアドレスをそれぞれ確認しておきます。  

```raw
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
2: ens5: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
    inet 10.0.4.95/20 metric 512 brd 10.0.15.255 scope global dynamic ens5
```

自分が選んだOSと設定だと```ens5```という名前みたいです。  
また、自分が設定したVPCのネットワークアドレスは```10.0.0.0/20```みたいです。  

SoftEther VPN Serverはまず起動してから設定するそうです。  
サービスとして登録して起動させます。  
サービスの登録は[こちら](https://qiita.com/clerk67/items/d31504e786fe4f8f9acf)を参考にしました。  

https://qiita.com/clerk67/items/d31504e786fe4f8f9acf

```sh
sudo vi /etc/systemd/system/vpnserver.service
sudo systemctl daemon-reload
sudo systemctl start vpnserver
sudo systemctl status vpnserver
sudo systemctl enable vpnserver
```

```/etc/systemd/system/vpnserver.service```の内容は以下のようになっています。  

```ini:vpnserver.service
[Unit]
Description=SoftEther VPN Server Service
After=network.target

[Service]
Type=forking
User=root
ExecStartPre=/sbin/ip link set dev (ここはNICの名前にする) promisc on
ExecStart=/opt/vpnserver/vpnserver start
ExecStop=/opt/vpnserver/vpnserver stop
Restart=on-abort
WorkingDirectory=/opt/vpnserver/

[Install]
WantedBy=multi-user.target
```

SoftEther VPN Serverが起動すると```vpncmd```というツールで設定できるようになります。  
上記の手順そのままだとパスが通っていないので、フルパス```/opt/vpnserver/vpncmd```でツールを実行しましょう。  
```vpncmd```のヘルプは```?```で見れます。  

この設定も[こちら](https://qiita.com/clerk67/items/d31504e786fe4f8f9acf)を参考にしました。  
設定値として以下のことをあらかじめ決めておくとサクサク進められそうです。  
- 仮想Hub名
- 仮想Hubパスワード。仮想Hubの設定を確認・変更するときに必要になる
- ユーザ名。同じユーザ名で複数端末から同時にVPNに接続することはできないので端末ごとに決める
- ユーザパスワード
- IPsecの暗号化で使う事前共有鍵。できるだけ長いものを用意する
- VPNで接続したときに割り振られるIPアドレス。AWSや自宅と被らないようにする

```raw
1
[空のままでEnter]
HubCreate my-hub
Hub my-hub
UserCreate my-pc /GROUP:none /REALNAME:none /NOTE:none
UserCreate tomo-pc /GROUP:none /REALNAME:none /NOTE:none
UserCreate tomo-server /GROUP:none /REALNAME:none /NOTE:none
UserPasswordSet my-pc /PASSWORD:hogehoge
UserPasswordSet tomo-pc /PASSWORD:hogehoge
UserPasswordSet tomo-server /PASSWORD:hogehoge
IPsecEnable /L2TP:yes /L2TPRAW:no /ETHERIP:no /DEFAULTHUB:my-hub
[事前共有鍵を入力]
SecureNatEnable
DhcpSet /START:192.168.30.10 /END:192.168.30.200 /MASK:255.255.255.0 /EXPIRE:7200 /GW:192.168.30.1 /DNS:192.168.30.1 /DNS2:none /DOMAIN:none /LOG:yes /PUSHROUTE:"10.0.0.0/255.255.0.0/192.168.30.1"
exit
```

DHCP機能を設定するときの```/PUSHROUTE:"10.0.0.0/255.255.240.0/192.168.30.1"```については以下のように決めました。  
- 1つ目のIPアドレス：VPCのネットワークアドレス
- 2つ目のIPアドレス：VPCのネットワークアドレスのサブネットマスク
- 3つ目のIPアドレス：```/GW```に指定したIPアドレス

VPNで接続すると仮想NAT(SecureNat)の内側に収容されるので、EC2インスタンスなど(10.0.x.x)にアクセスしたいときは、```/GW```から外側に出る…ということかな？  

## VPNクライアントの接続：Windows

最後の工程はVPNクライアントの接続です。まずはWindowsの設定から。  
今回用いているL2TP/IPsecというプロトコルは標準的なものなので、Windowsの標準機能でVPNに接続できます。  
設定＞ネットワークとインターネット＞VPN＞VPNを追加、で出てくるダイアログに設定値を入力すれば完了です。  

![Windowsの設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/37996c54-b442-3e7b-be5b-76763a40ae2c.png)

## VPNクライアントの接続：Ubuntu

Ubuntuの設定もやってみます。  
UbuntuのバージョンはUbuntu Desktop 22.04.3 LTSです。  

コマンドラインで設定する自信がないので、GUI(NetworkManager)から設定していきます。  
残念ながらデフォルトのNetworkManagerにはL2TP/IPsecの設定画面が用意されていません。そこで、L2TP/IPsecの設定画面を追加するパッケージを導入します。  

```sh
sudo apt install network-manager-l2tp-gnome
```

![Ubuntuの設定(1)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/5061ff45-a5e6-f697-2ccf-9f2aa5b5914d.png)

ここで出てくるダイアログに設定値を入力すれば完了です。  
```NTドメイン```は入力不要です。また、```IPsec Settings...```から事前共有鍵を入力する必要があります。

![Ubuntuの設定(2)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/476e9067-d255-5fbd-268c-72f6126e4cf8.png)

![Ubuntuの設定(3)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2917580/164866ce-10a2-d163-c88a-b47e805022e6.png)

NetworkManagerから一回設定してしまえば、後は```nmcli```コマンドで接続・切断できます。  

```sh
# ユーザパスワードと事前共有鍵をファイルに書いておく必要がある。ファイル名はなんでもいい
vi ~/.secrets/vpn
# VPN接続のプロファイル名を調べる
sudo nmcli connection show
# VPN接続のプロファイル名が仮に"AWS"だとすれば
sudo nmcli connection up id AWS passwd-file ~/.secrets/vpn
```

上記の```~/.secrets/vpn```には以下のようなフォーマットでユーザパスワードと事前共有鍵を書いておきます。  

```raw:~/.secrets/vpn
vpn.secrets.password:(ユーザパスワード)
vpn.secrets.ipsec-psk:(事前共有鍵)
```

```nmcli```を実行するコマンドをスクリプト化すれば、サーバを再起動したときに自動的にVPNに接続できそうですね。  
```rc.local```に書いてもいいのかな？  

```sh
# スクリプトを作成
vi /home/user1/connect-vpn.sh
chmod 755 /home/user1/connect-vpn.sh
# スクリプトをサービスとして登録して起動させる
sudo vi /etc/systemd/system/connect-vpn.service
sudo systemctl daemon-reload
sudo systemctl start connect-vpn.service
sudo systemctl enable connect-vpn.service
```

```sh:connect-vpn.sh
#!/bin/sh
sudo nmcli connection up id AWS passwd-file /home/user1/.secrets/vpn
```

```ini:/etc/systemd/system/connect-vpn.service
[Unit]
Description=Connect VPN

[Service]
ExecStart=/home/user1/connect-vpn.sh
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target
```

試しにサーバを再起動してみて、自動的にVPNに接続されればOK！