Ubuntu20.04とRaspberry PiでIPsecゲートウェイの簡易VPN構築 - 1.StrongSwan導入

# 前提と準備

## Linuxサーバー構築の記事
- [Sambaでファイルサーバー構築(CentOS 8.1・openSUSE 15.1・Ubuntu 20.04)](https://qiita.com/kazumi75kitty/items/4298795029ba9f67b9d2)
- [LinuxでApache2.4＋PHP7.4をソースコンパイル - 1.Apache導入](https://qiita.com/kazumi75kitty/items/daaf8ff8feb50ad04f41) /[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/67686eccaaec73251458)
- [LinuxでApache2.4＋PHP7.4をソースコンパイル - 2.PHP導入](https://qiita.com/kazumi75kitty/items/fca116b1b387fe56c93c) /[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/50f1a447f6ebc2ee2b61)
- [LinuxでApache2.4＋PHP7.4をソースコンパイル - 3.MySQL導入](https://qiita.com/kazumi75kitty/items/76807c637c096618a0e1)/[【Raspberry Pi】](https://qiita.com/kazumi75kitty/items/4212dacc45944f27ca94)
- [LinuxでApache2.4＋PHP7.4 - 4.セキュリティ(chownとfirewalld)](https://qiita.com/kazumi75kitty/items/8ad51806ac50bd04b644)
- [LinuxでIPsecゲートウェイをVPN構築 - 1.StrongSwan導入](https://qiita.com/kazumi75kitty/items/60941f00ae91c4d9f555) /【Ubuntu 20.04＋Raspberry Pi：この記事】
- [LinuxでIPsecゲートウェイをVPN構築 - 2.VPNへの接続確認](https://qiita.com/kazumi75kitty/items/f5a33abda9f8edd5c47f) /[【Ubuntu 20.04＋Raspberry Pi】](https://qiita.com/kazumi75kitty/items/c83f920f052d83d62457)

[前回は、CentOS 8 ＋ ラズパイopenSUSEで、IPsecゲートウェイをStrongSwanのソースコンパイルで構築しました](https://qiita.com/kazumi75kitty/items/60941f00ae91c4d9f555)。これをUbuntu 20.04とRaspberry Pi OSでできないかもやってみたよ(˶ ･ᴗ･ )੭⚐⚑

## 環境
- IPsecプログラム：StrongSwan 5.9.0(ソースコンパイル)
- IPsec交渉受信側：Raspberry Pi 3B+ / Raspberry Pi OS(2020/08版)(armhf(v7)、32bit)
- IPsec交渉発信側：Hyper-V(第2世代)のx64仮想機 / Ubuntu 20.04(x86_64)

## 前提
- OSは最小限のインストール。また、最新の状態でOSをアップデートしていること
- ユーザーはrootでインストール(私の検証ではadminという管理者アカウントにて、そこからsudoで処理しています)
- どのディストリビューションでも、ファイアウォールはufwを使う(ラズパイのRaspberry Pi OSでは、firewalldについて、IPv6周りの連動が糞だったので、Debian系標準のufwを使用することにしました)

## サーバー条件
### IPアドレスとネットワーク構築図
- IPsec交渉受信側ゲートウェイ(下の図の左、Raspberry Pi)：
 - インターネット側(eth0)：192.168.1.22
 - VPN領域側(eth1)：192.168.2.1

- IPsec交渉発信側ゲートウェイ(下の図の右、Ubuntu 20.04)：
 - インターネット側(eth0)：192.168.1.18
 - VPN領域側(eth1)：192.168.5.1

- ネットワークセグメント：
 - インターネット接続可能：192.168.1.0/24
 - Raspberry Pi(図の左の交渉受信側)セキュアセグメント：192.168.2.0/24
 - Ubuntu 20.04(図の右の交渉発信側)セキュアセグメント：192.168.5.0/24

- IPsec領域関連：
 - トンネリング区間：192.168.1.22 ～ 192.168.1.18間
 - VPN連携：192.168.2.0/24 ～ 192.168.5.0/24をVPN接続
![sswan00_IPsecGW図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/c71f67d0-b486-00c0-4653-cd952ad3a3a9.png)

※ 図の左の「openSUSE」は**Raspberry Pi OS**に、右の「Linuxルーター CentOS 8」は**Ubuntu 20.04**に読み替えてください。

### パッケージを個別ダウンロードしてインストールする機能とバージョン(2020年9月時点)
- zlib-1.2.11.tar.gz
- strongswan-5.9.0.tar.gz

それ以外の必要なパッケージは、ディストリビューションの標準パッケージコマンド(dnfやaptなど)でインストールし、個別ダウンロードは不要です。

ダウンロードについては、公式サイトにアクセスして、そこからダウンロードしてFTPで転送するか、ダウンロードファイルのURLさえわかれば、wgetで入手することもできますが、入手方法は省略しています。

# 作業手順
## 準備
### makeやcmake、パッケージ解凍機能のインストール
Hyper-V仮想マシンとラズパイでディストリビューションごとのインストールをします(ソースコンパイルは両方とも同じ)

```Ubuntu20.04(Hyper-V/x64)
# apt-get -y install make cmake tar bzip2
```

```RaspberryPiOS(2020.08)
# apt-get -y install make cmake tar bzip2
```

### GCCとC++コンパイラのインストール

```Ubuntu20.04(Hyper-V/x64)
# apt-get -y install gcc build-essential
```

```RaspberryPiOS(2020.08)
# apt-get -y install gcc build-essential
```

### zlibのソースインストール
zlibの配置場所はデフォルトのまま変えないでインストールしました。ソースコンパイルはHyper-V仮想マシンとラズパイで同じです

```
# cd [zlibの書庫ファイルが置いてあるディレクトリ]
# tar zxvf zlib-1.2.11.tar.gz
# cd zlib-1.2.11/
# ./configure
# make
# make install
```

### IP転送の有効化
IPsecゲートウェイとして動作するためには、IP転送を有効化しなければならないので、有効化します。コマンドだとやはりsysctl.dの設定ファイルに追加して読み込ませる必要があるそうですね(openSUSEだと親切にSSHからでもYaSTで設定しやすい)

```
# cat /proc/sys/net/ipv4/ip_forward
0
# vi /etc/sysctl.d/01-ipv4fwd.conf
```

```conf:/etc/sysctl.d/01-ipv4fwd.conf
# Controls IP packet forwarding
net.ipv4.ip_forward = 1
```


### VPN領域のネットワークアダプタの増設
Hyper-V仮想マシンとRaspberry Piともに、IP転送の有効化＋ネットワークアダプタを増設するため、**いったん電源を切ります**。私の場合は、ラズパイはUSBでVPN向けの有線LANアダプタを増設し、Hyper-Vは設定で、ネットワークアダプタを増設しました。

増設後は「eth1」が追加されているか確認しますが、もちろんIPアドレスはまだ割り振られていません。

```
# ip addr

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether [最初からあるネットワークアダプタのMACアドレス] brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.18/24 brd 192.168.1.255 scope global noprefixroute eth0
       valid_lft forever preferred_lft forever
    inet6 [最初からあるネットワークアダプタのIPv6アドレス] scope global temporary dynamic
       valid_lft 14043sec preferred_lft 12243sec
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether [増設ネットワークアダプタのMACアドレス] brd ff:ff:ff:ff:ff:ff
    inet6 [増設ネットワークアダプタのIPv6アドレス] scope link noprefixroute
       valid_lft forever preferred_lft forever

# ip route

default via 192.168.1.1 dev eth0 proto static metric 100
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.18 metric 100
```

再起動後は、コマンドでIP転送の有効化を操作しているので、有効(ip_forwardが1)になっているか確認をします。

```
# cat /proc/sys/net/ipv4/ip_forward
1

# sysctl --system
…(中略)
* Applying /etc/sysctl.d/01-ipv4fwd.conf ...
net.ipv4.ip_forward = 1
…(中略)
```

### StrongSwanをコンパイルするために必要なパッケージをディストリビューション標準パッケージコマンドでインストール
注意：面倒くさくても実行しないと、パッケージがない、とエラーが出てコンパイルが中止されるんです(´•ω•̥`)

```Ubuntu20.04(Hyper-V/x64)
# apt-get -y install libgmp-dev libssl-dev
```

```RaspberryPiOS(2020.08)
# apt-get -y install libgmp-dev libssl-dev
```

## StrongSwan 5.9.0のソースコンパイルのインストール
Hyper-V仮想機とラズパイ共通です。この作業は結構時間がかかりました(特にRaspberry Piだと、20～30分かかりました)

### configureとmake

```
# cd [strongswan-5.9.0.tar.gzが置いてあるディレクトリ]
# tar xvzf strongswan-5.9.0.tar.gz
# cd strongswan-5.9.0/
# ./configure --prefix=/usr --sysconfdir=/etc --enable-openssl
# make
# make install
```

エラーなくコンパイルできれば、インストールは完了です♪(\*˘︶˘\*).｡.:\*♡

### StrongSwanの環境設定
ソースコンパイルでStrongSwanをインストールすると、設定ファイルは/etc/ipsec.confに格納されるので、その中にIPsecの接続設定を行います。

```
[Apacheの基本設定]
# vi /etc/ipsec.conf
```

IPsecを確立する側(確立を発信する方)、そう、まずはHyper-VのUbuntu 20.04の設定を行います。「サーバー条件」の項にある通り、Hyper-Vマシン側からは、自分のIPアドレスが192.168.1.18、確立相手のRaspberry Piが192.168.1.22になるので、その対を設定ファイルに書き込みます。

「left」は確立を交渉発信する自分を、「right」は確立相手の情報を書き込むとのことです([StrongSwan公式のマニュアルから](https://wiki.strongswan.org/projects/strongswan/wiki/ConnSection))

```conf:Ubuntu20.04(Hyper-V/x64)
…
# 以下を追記する
conn [識別名 例：linux-2-linux]
      authby=secret
      auto=start				# IPsec交渉を発信する
      closeaction=restart
      dpdaction=restart
      left=192.168.1.18			# leftは自分側のIPsecゲートウェイ
      leftid=192.168.1.18		# IPsec交渉するのに自分を識別するID
      leftsubnet=192.168.5.0/24
      right=192.168.1.22		# rightは相手側のIPsecゲートウェイ
      rightid=192.168.1.22		# IPsec交渉相手を識別するID
      rightsubnet=192.168.2.0/24
…
```
ちなみにIDは、私の場合は簡単のためIPアドレスを使用しましたが、文字列でもOKです。そんでleftsubnetとrightsubnetというのは、自分側と相手側の受け持つVPN領域なので、leftsubnetは自分の持っているVPN領域なので192.168.5.0/24を、rightsubnetは相手のラズパイのVPN領域なので192.168.2.0/24を入れました。

続いて、ラズパイの設定も行います。「サーバー条件」の項にある通り、確立を受ける自分のIPアドレスが192.168.1.22、確立してくる相手側のHyper-V Ubuntu20.04が192.168.1.18になるので、Hyper-V(Ubuntu20.04)とは逆の内容を設定ファイルに書き込みます。

```conf:RaspberryPiOS(2020.08)
# 以下を追記する
conn [識別名 例：linux-2-linux]
      authby=secret
      auto=add				# IPsec交渉を受信する
      closeaction=clear
      dpdaction=clear
      left=192.168.1.22
      leftid=192.168.1.22
      leftsubnet=192.168.2.0/24
      right=192.168.1.18
      rightid=192.168.1.18
      rightsubnet=192.168.5.0/24
```

要は、auto=addとすることで、IPsec確立交渉を受信する側に設定し、leftの内容とrightの内容を真逆にするというところだね。そうすれば、識別名(connの後ろの文字列)が一致すれば、IPsecの接続を確立できる、ということ

StrongSwanのデフォルトでは、PSK方式で、標準でAES/SHA暗号方式を採用しています。それ以外の暗号方式を設定する必要がある場合はまた別途設定するが、ここでは省略。

余談ですが、この時点でのStrongSwanはNAT-Tに対応していて、IPsecをNATに適用させることもできる(かつてはIPsecをNATに適用することができなかった)ので、インターネットでNATが使われている空間でさえも、StrongSwanで構築されたVPNをインターネット越しに通信できるそうです(実験したことはありませんが…)

```
[StrongSwanの鍵の設定]
# vi /etc/ipsec.secrets
```

```conf:/etc/ipsec.secrets
…
: PSK "[適当な文字列：例…kazumi75kitty]"
…
```
デフォルトのPSK方式を使っているので、Hyper-V仮想マシンとラズパイともに同じにします。


## StrongSwanの起動
### サービスの起動スクリプト作成＆有効化
StrongSwanに必要な環境設定がそろったので、起動できるようにしたいと思います。起動スクリプトはSystemdなので、/etc/systemd/systemに作成します

```
# cd /etc/systemd/system
# vi strongswan.service
```

```ini:strongswan.service
[Unit]
Description=strongSwan

[Service]
Type=forking
ExecStart=/usr/sbin/ipsec start
ExecStop=/usr/sbin/ipsec stop

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

ここではSystemdのスクリプトは詳しく説明しませんが、起動と停止は親プロセスからはバックグランドで実行されるため、strongSwanでは[Service]のTypeはforkingとなっています。

### ufwの設定
ufwもfirewalldのときと同様に、IPsecを受け付けます。ipsecが使うポートは500・4500のTCP/UDPポート番号と、AH・ESPプロトコルを使用しているそうです。ただしここでの設定だと、トンネリングをかます192.168.1.0/24内部限定を明記していなく、ほかのネットワークセグメントからもIPsecのパケットが通行可能であれば受信できてしまいます。しかしここまで対策すると煩雑になるので今回は簡単のためIPsecの許可のみとします。

```
# ufw allow 500
# ufw allow 4500
# ufw allow to any proto ah
# ufw allow to any proto esp
# ufw status numbered

Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 30303/tcp                  ALLOW IN    192.168.1.0/24
[ 2] 5900:5999/tcp              ALLOW IN    192.168.1.0/24
[ 3] 500                        ALLOW IN    Anywhere
[ 4] 4500                       ALLOW IN    Anywhere
[ 5] Anywhere/esp               ALLOW IN    Anywhere/esp
[ 6] Anywhere/ah                ALLOW IN    Anywhere/ah
[ 7] 500 (v6)                   ALLOW IN    Anywhere (v6)
[ 8] 4500 (v6)                  ALLOW IN    Anywhere (v6)
[ 9] Anywhere/esp (v6)          ALLOW IN    Anywhere/esp (v6)
[10] Anywhere/ah (v6)           ALLOW IN    Anywhere/ah (v6)

```

Hyper-V仮想マシンとラズパイの両方で共通のコマンドです。

### 起動と動作確認
それでは、起動します。enableでの常時起動有効化＆statusで「Active」「Running」になっていることを確認。

**まずはIPsec確立交渉を受信する側からStrongSwanを起動したのちに、IPsec確立を発信する側の順に起動させています**。ここではラズパイのStrongSwanを起動後に、Hyper-V仮想マシンのStrongSwanを起動させています。

```
# systemctl start strongswan
# systemctl enable strongswan
# systemctl status strongswan
```

Hyper-V仮想マシンとラズパイで「Active」「Running」になっていることを確認し、いよいよIPsecトンネリングが確立されているかをチェック。

```Ubuntu20.04(Hyper-V/x64)
# /usr/sbin/ipsec status
Security Associations (1 up, 0 connecting):
linux-2-linux[1]: ESTABLISHED 2 minutes ago, 192.168.1.18[192.168.1.18]...192.168.1.22[192.168.1.22]
linux-2-linux{1}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: ********_i ********_o
linux-2-linux{1}:   192.168.5.0/24 === 192.168.2.0/24

# ip xfrm policy
src 192.168.5.0/24 dst 192.168.2.0/24
        dir out priority 375423 ptype main
        tmpl src 192.168.1.18 dst 192.168.1.22
                proto esp spi 0x******** reqid 1 mode tunnel
src 192.168.2.0/24 dst 192.168.5.0/24
        dir fwd priority 375423 ptype main
        tmpl src 192.168.1.22 dst 192.168.1.18
                proto esp reqid 1 mode tunnel
src 192.168.2.0/24 dst 192.168.5.0/24
        dir in priority 375423 ptype main
        tmpl src 192.168.1.22 dst 192.168.1.18
                proto esp reqid 1 mode tunnel
```

```RaspberryPiOS(2020.08)
# /usr/sbin/ipsec status
Security Associations (1 up, 0 connecting):
linux-2-linux[1]: ESTABLISHED 2 minutes ago, 192.168.1.22[192.168.1.22]...192.168.1.18[192.168.1.18]
linux-2-linux{1}:  INSTALLED, TUNNEL, reqid 1, ESP SPIs: ********_i ********_o
linux-2-linux{1}:   192.168.2.0/24 === 192.168.5.0/24

# ip xfrm policy
src 192.168.2.0/24 dst 192.168.5.0/24
        dir out priority 375423 ptype main
        tmpl src 192.168.1.22 dst 192.168.1.18
                proto esp spi 0x******** reqid 1 mode tunnel
src 192.168.5.0/24 dst 192.168.2.0/24
        dir fwd priority 375423 ptype main
        tmpl src 192.168.1.18 dst 192.168.1.22
                proto esp reqid 1 mode tunnel
src 192.168.5.0/24 dst 192.168.2.0/24
        dir in priority 375423 ptype main
        tmpl src 192.168.1.18 dst 192.168.1.22
                proto esp reqid 1 mode tunnel
```

IPsecトンネリングが、Hyper-V仮想マシンとラズパイでちゃんと確立されてました(˶ ･ᴗ･ )੭⚐⚑

## おまけ(ipsec.confのIDにドメインなど文字列を指定)
StrongSwanの設定ファイル/etc/ipsec.confで、leftidやrightidで、IPアドレスではなく文字列を使った場合もキャプチャ画面を載せておきます(\*´꒳`\*)

たぶんこっちのほうがわかりやすいかもしれません。画面はCentOS＋openSUSEですが、動作的にはUbuntuなどでも同じだと思います。
例では「raspberrypi」と「testonly@kazumi-jam」を識別させています
![ipsec.confのIDに文字列やドメインを指定する](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617906/1c2c198b-cde6-3e2a-7c8a-051e036f5d48.png)

## 補足(IPsecトンネリングがNATを経由する場合)

わかりやすい画像はまだないけど、ipsec.confでのleftとrightのIPアドレスは、それぞれ自分自身のIPアドレスと、自分から見たNAT変換後の相手のIPアドレスを入れます。その際、leftidとrightidは、必ず自分と相手で対に一致していることが必要になります。

例：
192.168.1.0/24 → 192.168.120.0/28 がNATで仕切られている場合

IPsecGW-1(192.168.1.0/24内)
　left=192.168.1.22
　leftid="gw1"
　right=192.168.1.18
　rightid="gw2"
IPsecGW-2(192.168.120.0/28内)
　left=**192.168.120.1**
　leftid="gw2"
　right=**192.168.120.3**
　rightid="gw1"

という風に、IPアドレスはNATを考慮したものを入れることでIPsecが確立できるけど、詳しくはまた後で載せてみます。

# 次回
一通りのStrongSwan導入で、Raspberry PiとHyper-V上のUbuntu 20.04でも、CentOSの時と同様に、IPsecゲートウェイの構築が完了しました。次はUbuntu下でもそのIPsecゲートウェイにつながったVPNにクライアントとサーバーを接続して、互いに接続できるかを試します( ˶˙ᵕ˙˶ )