【VPN Gateway】EC2にstrongSwanを立ててVPN接続する＋Firewall導入時の注意点

# はじめに
[Japan AWS Jr. Champions Advent Calendar 2024](https://qiita.com/advent-calendar/2024/japan-aws-jr-champions)  3日目の記事です。

AWSとオンプレミスを接続一つの選択肢に「Site-to-Site VPN」の採用があります。
私は、実際に接続してみないと設定の妥当性が分からないことに課題を感じていました。
特にオンプレミス担当事業者が別に存在する場合、コミニュケーションコストを最小限化するために細心の注意が必要です。
テスト用に自由に使用できるサーバが自社にあれば幸せなのですが、中々そううまくはいきません。
そこで今回は、仮想オンプレミス（VPC）を用意し、EC2にVPNサーバ（strongSwan）を立ててVPN接続をしてみました。
strongSwanはOSSなので無料で作成可能です。
さらにTipsとしてFirewallを導入時の注意点も最後に記します。

# 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/b17ea761-c5d3-49da-199f-b45cbfc9c38c.png)

# 構築手順
## AWS環境
VPCを「10.0.0.0/16」のIP範囲で作成します。
一般的な作業になるので手順は割愛します。
EC2は、プライベートサブネットに構築しています。
EC2には、sshできるようにセキュリティグループを設定します。

## オンプレミス環境１
VPCを「192.168.0.0/16」のIP範囲で作成します。
パブリックサブネットにVPNサーバとなるEC2、プライベートサブネットに接続テスト用のEC2を作成します。
スペックは無料枠の範囲で十分です。
VPNサーバとなるEC2には、sshできるようにセキュリティグループを設定します。
接続テスト用のEC2には、AWS環境のIP範囲「10.0.0.0/16」からpingできるようにセキュリティグループを設定します。

## VPNGateway
ここまでの手順で材料はそろったので仮想プライベートゲートウェイとカスタマーゲートウェイを作成します。
### 仮想プライベートゲートウェイ
デフォルト設定で作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/94d9751d-41d8-39c9-1a74-61b95fcd8ff5.png)
作成後、AWS環境のVPCにアタッチします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/2a1eab80-bfc6-eb91-76ff-a9155e2356a0.png)

### カスタマーゲートウェイ
IP アドレスに、作成したVPNデバイス用EC2のパブリックIPを指定して作成します。
その他はデフォルトです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/12cf043e-6b79-cda2-3a36-b2cc843933b9.png)

### Site-to-Site VPN 接続
最後に接続設定を作成します。
仮想プライベートゲートウェイ、カスタマーゲートウェイはそれぞれ上記手順で作成したリソースを選択します。
静的IPプレフィックスはGatewayを経由してアクセスするセグメント範囲（つまりオンプレミス環境のIP範囲）を指定します。本ユースケースでは「192.168.0.0/16」となります。ローカル IPv4 ネットワーク CIDRも同様です。
リモート IPv4 ネットワーク CIDRにはAWS環境のセグメント範囲「10.0.0.0/16」を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/7b850d7e-d290-23bb-e535-efa122377d62.png)

この時点では接続はDownの状態になっているかと思います。

:::note info
作成後のリソース画面からオンプレVPNデバイス用の接続設定ファイルをダウンロードできますが、実際の業務ではオンプレミス事業者に必要の有無を確認した上で共有した方が良い派です。
バージョンが完全一致していないとかえって混乱してしまうので、必要な情報だけヒアリングして共有がベターかなと思います。
:::

VPNサーバ用EC2のセキュリティグループに、仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレスからポート「500,4500」のインバウンドアクセス許可を追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ae2b26b6-87d3-4d1c-34fe-f30ff7dec70a.png)

:::note info
仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレスは、以下画面より確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/d991fcae-92c2-4bfa-ecac-e99be0ac6bf4.png)
:::

## オンプレミス環境2
いよいよVPNサーバをセットアップします。
今回はOSSの「strongSwan」を採用しました。![strongswan_square_large.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/0840894a-f518-6e32-3058-3198da6864b7.png)

まずはVPNサーバ用のEC2にsshしてstrongSwanをセットアップします。
```bash
sudo dnf update -y
sudo dnf groupinstall -y "Development Tools"
wget https://download.strongswan.org/strongswan-5.9.11.tar.gz
tar -xzf strongswan-5.9.11.tar.gz
cd strongswan-5.9.11
sudo dnf install -y autoconf automake libtool
autoreconf -i
./configure --prefix=/usr --sysconfdir=/etc --enable-eap-mschapv2 --enable-openssl --enable-nat-transport
make
sudo make install
```
/etc/systemd/system/strongswan.serviceを以下の内容に修正します。

```service
[Unit]
Description=strongSwan IPsec IKEv1/IKEv2 daemon using ipsec.conf
After=network.target

[Service]
ExecStart=/usr/sbin/ipsec start --nofork
ExecStop=/usr/sbin/ipsec stop
ExecReload=/usr/sbin/ipsec reload
Restart=on-failure
Type=simple

[Install]
WantedBy=multi-user.target
```
修正内容を反映します。

```bash
sudo systemctl daemon-reload
```

/etc/sysctl.confを以下の内容に修正します。

```conf
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.accept_source_route = 0
```
修正内容を反映します。

```bash
sudo sysctl -p
```
/etc/sysctl.confを以下の内容に修正します。

```conf
# basic configuration
config setup

include /etc/ipsec.d/*.conf
```
/etc/ipsec.d/aws.confを以下の内容で作成します。
それぞれがVPNGatewayとの接続情報を指しています。
leftsubnetはオンプレミス空間のIP範囲、rightsubnetはAWS空間のIP範囲を指定します。
また、leftidにはVPNサーバ用のEC2のPublicIP、rightidには仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレスを指定します。

```conf
conn Tunnel1
        authby=secret
        auto=start
        left=%defaultroute
        leftid=<VPNサーバ用のEC2のPublicIP>
        right=<仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレス➀>
        type=tunnel
        ikelifetime=8h
        keylife=1h
        ike=aes256-sha256-modp1024
        keyingtries=%forever
        keyexchange=ike
        leftsubnet=192.168.1.0/16
        rightsubnet=10.0.0.0/16
        dpddelay=10
        dpdtimeout=30
        dpdaction=restart
        auto=start


conn Tunnel2
        authby=secret
        auto=start
        left=%defaultroute
        leftid=<VPNサーバ用のEC2のPublicIP>
        right=<仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレス②>
        type=tunnel
        ikelifetime=8h
        keylife=1h
        ike=aes256-sha256-modp1024
        keyingtries=%forever
        keyexchange=ike
        leftsubnet=192.168.1.0/16
        rightsubnet=10.0.0.0/16
        dpddelay=10
        dpdtimeout=30
        dpdaction=restart
        auto=start
```

続けて、/etc/ipsec.d/aws.secretsを以下の内容で作成します。
```
VPNサーバ用のEC2のPublicIP 仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレス➀ : 仮想プライベートゲートウェイ側の接続➀用の事前共有キー"
VPNサーバ用のEC2のPublicIP 仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレス② : 仮想プライベートゲートウェイ側の接続②用の事前共有キー"

（例）
x.x.x.x y.y.y.y : PSK "sample.string"
x.x.x.x z.z.z.z : PSK "sample.string"
```
:::note info
事前共有キー配下の手順で確認できます。
１. 作成したSite-to-Site VPN 接続リソース画面より、「アクション＞VPNトンネルオプションを変更」を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/cf8c1ea3-9691-6a51-7d73-889625cc9c4a.png)
２. 仮想プライベートゲートウェイ側のVPNトンネル外部IPアドレス（VPNトンネル外部IPアドレス）を選択すると、事前共有キーが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/c69d9bd9-b53f-ecee-a37a-4a1397f67185.png)
:::

最後に設定を反映します。
```bash
sudo systemctl restart strongswan
sudo systemctl enable strongswan
sudo ipsec up Tunnel1
sudo ipsec up Tunnel2
```
以下コマンドの結果が画像の通り表示されれば接続完了です。

```bash
sudo ipsec status
Security Associations (2 up, 0 connecting):
     Tunnel2[2]: ESTABLISHED 1 second ago, 192.168.9.244[54.178.6.54]...57.181.119.38[57.181.119.38]
     Tunnel2{2}:  INSTALLED, TUNNEL, reqid 1, ESP in UDP SPIs: c4f9d261_i cbef13fc_o
     Tunnel2{2}:   192.168.0.0/16 === 10.0.0.0/16
     Tunnel1[1]: ESTABLISHED 1 second ago, 192.168.9.244[54.178.6.54]...54.92.9.29[54.92.9.29]
     Tunnel1{1}:  INSTALLED, TUNNEL, reqid 1, ESP in UDP SPIs: c886cc7a_i c1869be3_o
     Tunnel1{1}:   192.168.0.0/16 === 10.0.0.0/16
```

:::note info
トラブルシューティング
VPNサーバのログは以下のコマンドで確認できます。
```bash
sudo journalctl -u strongswan
```
:::

AWS側に戻ってSite-to-Site VPN 接続リソース画面より、接続が完了していることも確認しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/53c2dd16-cd04-a2ac-9735-35ccb5912797.png)

## ルートテーブルの整備
動作確認前に最終セットアップを行います。
### AWS環境-動作確認用EC2
ルートテーブルを作成し、ルート伝番タブより伝番を有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/929bceec-0621-11eb-5653-d143ba7bf943.png)
これにより、オンプレミス環境へのアクセス経路が仮想ネットワークゲートウェイより自動伝番されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/ff640a71-3e72-0f6a-9051-ad3ad8bd4432.png)

### オンプレミス環境-動作確認用EC2
ルートテーブルを作成し、送信先「10.0.0.0/16」に対するVPNサーバ用へのルートを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/6e2930d0-a12b-1865-1302-cf956050478e.png)

## 動作確認
AWS環境からオンプレミス環境へpingが通ることを確認します。
```bash
（例）
[ec2-user@ip-10-0-142-44 ~]$ ping 192.168.142.111
PING 192.168.142.111 (192.168.142.111) 56(84) bytes of data.
64 bytes from 192.168.142.111: icmp_seq=1 ttl=126 time=3.69 ms
64 bytes from 192.168.142.111: icmp_seq=2 ttl=126 time=3.54 ms
64 bytes from 192.168.142.111: icmp_seq=3 ttl=126 time=3.87 ms
```
Reachability AnalyzerからもGUI上で経路確認ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/703056f5-24b7-1272-a1fa-d70f41ecb488.png)

# Appendix
ユースケースによってはプライベート空間であっても、AWS環境とオンプレミス環境の境界にFirewallを設置し、悪意のあるアクセスからシステムを守る必要があるかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/992414/24a91933-82af-61ce-22a3-d25d30f647ce.png)


Firewallはステートフルなので、行きと帰りどちらもFirewallを通さないとパケットがロスされてしまいます。

AWS環境のEC2に紐づけるルートテーブルのルート「宛先：0.0.0.0/0、ネクストホップ：FirewallのIP」を作成することが一般的化と思います。
その際ルートの伝搬を有効化してしまうと、行きの通信はFirewallを経由せず、直接仮想プライベートゲートウェイに行ってしまいます。
一方、帰りはFirewallを経由するため、Firewall視点では整合性が無いとみなしパケットを捨ててしまうのです。
Firewallを採用する場合はこの点ご注意ください。

# 最後に
オンプレミス視点での作業を経験したことで、見えなかった部分の解像度が上がりすっきりしました。
NW周りは難しいですが、引き続き精進していきたいです。