自宅にVPNサーバーを構築して、クライアント端末から接続確認する方法

## はじめに
突然ですが、VPN接続のネットワーク検証案件を対応した際、あまりにも理解できずかなり苦労した経験を振り返り、記録としてブログに残します。

当時、VPNに関する知識がなさすぎたため、自宅でOpenVPNを導入して検証を繰り返しました。今回は、その備忘録として、VPNサーバー構築や証明書発行などについて振り返ります。

## 前提条件
・VirtualBox上で稼働しているAlmaLinux（CentOSの後継）を使用
・クライアント端末として、プライベートで使用しているWindows 11端末を使用
・VPNのミドルウェアとしてOpenVPNを採用

## 全体像および自宅検証環境
一般的なVPNは、出先からでもVPN専用回線を通して自宅のVPN対応ルーターを経由し、自宅の環境にアクセスして作業することを想定しています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/ea7e13ad-cdf3-bc9d-194e-d451550b3054.png)
引用画像：https://www.aspicjapan.org/asu/article/35028

:::note warn
ただし、**私の使用しているルーターはVPNに対応していないことが判明したため、今回はVPNサーバーを構築し、自宅ネットワークからVPNサーバーに接続する検証**を行います。
:::

今回もVirtualBox環境上で動いているAlmaLinuxを使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/0511088a-70b6-07ed-d20e-6f8641fb2ec2.png)
※検証で使用するのは実行中の「AlmaLinux」になります。

## 知識整理
OpenVPNは、インターネット上で安全な仮想プライベートネットワーク（VPN）を構築するためのオープンソースソフトウェアです。暗号化されたトンネルを通じてデータを送受信し、プライバシーやセキュリティを保護します。

企業や個人がリモートアクセスやセキュアな通信を実現するために広く利用されています。

:::note warn
必要な知識については、詳しいサイトが多数ありますので、公式サイトと併せてご紹介します。

**VPNについて**
参考サイト：https://www.ntt.com/business/services/network/internet-connect/ocn-business/bocn/knowledge/archive_103.html
参考サイト：https://jp.norton.com/blog/privacy/vpn

**OpenVPNについて**
参考サイト：https://surfshark.com/ja/blog/what-is-open-vpn
公式サイト：https://openvpn.net/community-downloads/

**クライアント証明書について**
参考サイト：https://www.nrapki.jp/client-certificate/telework/
参考サイト：https://jp.globalsign.com/managed-pki/about_clientcert.html
:::

## 構築の流れ
ここでは、OpenVPNのインストール、クライアント証明書の発行、端末へのセットアップ手順を説明します。

:::note info
**ステップ1：OpenVPNの導入及び設定**
**ステップ2：クライアント証明書の発行**
**ステップ3：Windows 11端末へOpenVPN導入及びセットアップ**
:::

では、AlmaLinuxにOpenVPNをインストールし、設定する手順から紹介していきます。

## ステップ1：OpenVPNの導入及び設定
まずは、EPELリポジトリを有効にし、openvpnとeasy-rsaをインストールします。
```
sudo yum install epel-release
sudo yum install openvpn easy-rsa
```

環境変数の設定で証明書の有効期間を変更する場合は、以下コマンドを実行する。
※ここでは、証明書の有効期間を7300日（20年）に設定しています。
```
export EASYRSA_CA_EXPIRE=7300
```

Easy-RSAのディレクトリに移動し、PKIを初期化します。
※3.0.8の箇所はご自身の環境のバージョンに合わせてください。
```
cd /usr/share/easy-rsa/3.0.8/
sudo ./easyrsa init-pki
```

CA（認証局）の証明書を作成します。秘密鍵のパスフレーズが求められるので入力します（2度求められる場合がありますが、最初のは無視します）。
```
sudo ./easyrsa build-ca
```

CA証明書の有効期間を必要に応じて変更してください。
※ここでは、CA証明書の有効期間を3650日（10年）に設定しています。
```
export EASYRSA_CERT_EXPIRE=3650
```

サーバー証明書を生成します（パスフレーズ不要のためnopassを指定）。
```
sudo ./easyrsa build-server-full vpn.example.com nopass
```

Diffie-Hellmanパラメーターを生成します。
```
sudo ./easyrsa gen-dh
```

TLS-Authの共通鍵を生成します。
```
sudo openvpn --genkey --secret /etc/openvpn/server/ta.key
```

OpenVPNサーバーの設定ファイルをコピーします。
```
sudo cp /usr/share/doc/openvpn/sample/sample-config-files/server.conf /etc/openvpn/server/
```

/etc/openvpn/server/server.confファイルを以下のように編集します（重要な設定のみ記載）。

:::note alert
※3.0.8の箇所はご自身の環境のバージョンに合わせてください。
:::

```/etc/openvpn/server/server.conf
port 7716
proto udp
dev tun
ca /usr/share/easy-rsa/3.0.8/pki/ca.crt
cert /usr/share/easy-rsa/3.0.8/pki/issued/vpn.example.com.crt
key /usr/share/easy-rsa/3.0.8/pki/private/vpn.example.com.key
dh /usr/share/easy-rsa/3.0.8/pki/dh.pem
server 172.16.1.0 255.255.255.0
push "route 192.168.1.0 255.255.255.0"
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 192.168.1.11"
push "dhcp-option DNS 8.8.8.8"
client-to-client
duplicate-cn
tls-auth ta.key 0
```

IPフォワードを有効にするため、設定ファイルを変更します。
```
sudo echo -e "\n# Controls IP packet forwarding\nnet.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sudo sysctl -p
```

ファイアウォールの設定として使用するポートの例外登録とIPマスカレードの設定を行います。
```
sudo firewall-cmd --permanent --add-port=7716/udp
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload
```

SELinuxのポリシー変更ユーティリティをインストールします。
```
sudo yum install policycoreutils-python-utils
```

OpenVPNのポートをSELinuxで許可します（ポート7716を使用）。
```
sudo semanage port -t openvpn_port_t -a -p udp 7716
```

OpenVPNサーバーサービスを有効化し、起動します。
```
sudo systemctl enable --now openvpn-server@server
```

OpenVPNのステータスを確認するには、以下のコマンドを使用します。
```
sudo systemctl status openvpn-server@server
```

```
**出力例：**
[root@HONDA-TEST server]# sudo systemctl status openvpn-server@server
● openvpn-server@server.service - OpenVPN service for server
     Loaded: loaded (/usr/lib/systemd/system/openvpn-server@.service; enabled; preset: disabled)
     Active: active (running) since Sun 2024-09-08 06:01:34 JST; 56min ago
```

このコマンドは、OpenVPNサーバーの現在のステータス（稼働中かどうか、エラーメッセージなど）を表示します。

## ステップ2：クライアント証明書の発行
以下、サイトを参考にして後続の作業は進めています。
https://weblog.hirohiro716.com/?p=2345

OpenVPNのクライアント証明書を手動で発行するのは手間がかかるため、自動でクライアント用の設定ファイル（.ovpnファイル）を生成するスクリプトを活用します。

:::note alert
**特にTLS-Authの設定で、サーバー側がkey-direction 0ならクライアント側はkey-direction 1にする必要がある点に注意が必要です。**
:::

以下は、クライアント証明書を発行し、.ovpnファイルを作成するためのスクリプトです。
※3.0.8の箇所はご自身の環境のバージョンに合わせてください。

```shell
#!/bin/bash
easyrsa_location=/usr/share/easy-rsa/3.0.8
sample_config_location=/usr/share/doc/openvpn/sample/sample-config-files
takey_location=/etc/openvpn/server/ta.key
server=vpn.example.com
port=7716

# ユーザーIDを入力
echo "作成するユーザーIDを入力してください。"
read -p "ユーザーID:" id
if [ -z "$id" ]; then
    exit 0
fi
fullname=$id.$server

# 確認メッセージ
echo "クライアント接続設定ファイル「$id.ovpn」を作成します。"
read -p "Enterキーで続行します。" result
if [ ! -z "$result" ]; then
    exit 0
fi

# 証明書の作成
default_location=$(pwd)
cd $easyrsa_location
./easyrsa build-client-full $fullname nopass
if [ ! $? -eq 0 ]; then
    exit 0;
fi
cd $default_location

# クライアント設定ファイルの生成
cp $sample_config_location/client.conf $id.ovpn
sed -i "s/my-server-1 1194/$server $port/g" $id.ovpn
sed -i "s/ca ca.crt/#ca ca.crt/g" $id.ovpn
sed -i "s/cert client.crt/#cert client.crt/g" $id.ovpn
sed -i "s/key client.key/#key client.key/g" $id.ovpn
sed -i "s/tls-auth ta.key 1/#tls-auth ta.key 1\nkey-direction 1/g" $id.ovpn

# 証明書情報の埋め込み
echo "" >>$id.ovpn
echo "<ca>" >>$id.ovpn
grep -A 100 'BEGIN CERTIFICATE' $easyrsa_location/pki/ca.crt >>$id.ovpn
echo "</ca>" >>$id.ovpn
echo "" >>$id.ovpn

echo "<key>" >>$id.ovpn
grep -A 100 'BEGIN PRIVATE KEY' $easyrsa_location/pki/private/$fullname.key >>$id.ovpn
echo "</key>" >>$id.ovpn
echo "" >>$id.ovpn

echo "<cert>" >>$id.ovpn
grep -A 100 'BEGIN CERTIFICATE' $easyrsa_location/pki/issued/$fullname.crt >>$id.ovpn
echo "</cert>" >>$id.ovpn
echo "" >>$id.ovpn

echo "<tls-auth>" >>$id.ovpn
cat $takey_location >>$id.ovpn
echo "</tls-auth>" >>$id.ovpn
echo "" >>$id.ovpn

echo "作成が完了しました。"
```

スクリプトを作成し、実行権限を付与します。
```
chmod +x build-client-ovpn.sh
```

スクリプトを実行して、対話形式でクライアント証明書と設定ファイルを生成します。
```
./build-client-ovpn.sh
```

このスクリプトにより、証明書と設定ファイルが自動で生成され、.ovpnファイルとしてまとめられます。

## ステップ3：Windows 11端末へOpenVPN導入及びセットアップ
次は、Windows 11 端末に OpenVPN クライアントを導入し、サーバーと接続する手順を説明します。

:::note warn
・**OpenVPN クライアントのインストール**
OpenVPNの公式サイト から、Windows 用のインストーラーをダウンロードします。
https://openvpn.net/community-downloads/
:::

ダウンロードしたファイルを実行して、インストールを開始します。インストール中、デフォルトの設定で進め、 TAP ドライバーも含めてインストールします。

・**VPNサーバーから .ovpn ファイルの取得**
サーバー側で作成したクライアント用の .ovpn ファイル（クライアント証明書や鍵が含まれている）を、Windows 11 端末へコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/88fe7ef4-ee6c-2e54-c379-d40e036d132f.png)


私の場合はWinSCPを使いファイル転送しましたが、各自の環境に合わせてセキュアな方法で転送してください（例：SCP、SFTP、USBメモリなど）。

・**OpenVPN クライアントの設定**
OpenVPN クライアントを起動し、右下のタスクバーにある OpenVPN アイコンを右クリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/b13a27a0-af1c-2b7b-1f8f-a1c9c54580e7.png)


:::note info
設定ファイルを追加するには、C:\Program Files\OpenVPN\config フォルダに .ovpn ファイルをコピーします。
:::

コピーした後、右下のタスクバーにある OpenVPN アイコンを右クリックし、Importからファイルのインポートに進み.ovpn ファイルを選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/519af59c-3a07-f529-4bc9-a3a31aad23a3.png)

OpenVPN クライアントに再度アクセスし、接続をクリックします。

:::note warn
**以下のような画面が表示され、右下のタスクバーにある OpenVPN アイコンが接続済になっていることを確認する。**
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/ea4b4e5d-290d-a438-8da7-a04e1e6b480d.png)

接続に成功すると、OpenVPN の右下のアイコンの色が変わり、クライアント端末は VPN サーバーに接続されます。

サーバーとの接続が完了したら、Windows のネットワーク設定から、IP アドレスや DNS サーバーが VPN 経由で設定されていることを確認できます。

## 検証及び検証結果
ここまでの作業を踏まえてOpenVPNサーバーおよびWindowsクライアント端末の接続確認を行っていきます。

### **OpenVPNサーバー側での稼働確認**
サーバーが正常に稼働しているか確認するためには、OpenVPNサービスのステータスをチェックします。以下のコマンドを実行してください。
```
sudo systemctl status openvpn-server@server
```

```
**出力例：**
active (running) と表示されていれば、正常に稼働しています。
※inactive や failed と表示されている場合は、設定やログを確認する必要があります。
```

次に、接続に関する情報やエラーを確認するために、OpenVPNのログファイルを確認します。
```
sudo journalctl -u openvpn-server@server
```
```
**出力例：**
Sep 08 06:01:34 HONDA-TEST openvpn[1063]: Initialization Sequence Completed: OpenVPNサーバーが正常に初期化されています。
Sep 08 06:05:48 HONDA-TEST openvpn[1063]: [honda.vpn.example.com] Peer Connection Initiated with [AF_INET]192.168.1.9:55489: クライアントとの接続が確立されたことを示します。
Sep 08 06:05:48 HONDA-TEST openvpn[1063]: MULTI: primary virtual IP for honda.vpn.example.com/192.168.1.9:55489: 172.16.1.6: 仮想IPがクライアントに割り当てられています。
```
このログから私のクライアント端末には、「**172.16.1.6**」の仮想IPが割り当てられていることが確認できました。

:::note info
補足として、/var/log/openvpn.log でもログを確認することもできます。
:::

最後に、OpenVPNサーバーに接続されているクライアントを確認するには、以下のコマンドを実行します。
```
sudo cat /etc/openvpn/server/openvpn-status.log
```

ここには、接続中のクライアントのIPアドレスや接続時間などの情報が表示されます。

### **Windowsクライアント側での稼働確認**
VPNに接続された状態で、IPアドレスやルート設定を確認するには、コマンドプロンプトで以下のコマンドを実行します。
```
ipconfig
```

**接続前**
OpenVPN接続前の状態は、「OpenVPN Data Channel Offload」の箇所に仮想IPアドレスが付与されていないことを確認しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/544f14da-5009-111b-5c01-cbc2c7d7b22c.png)

**接続後**

:::note warn
接続後は、事前に確認した、「**172.16.1.6**」の仮想IPがクライアント端末側にも割り当ていることが確認できました。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/de4305a4-a1f5-c583-7948-232c81ead4a5.png)




次に、VPN経由で送られるトラフィックのルートが設定されているか確認します。
```
route print
```

**接続前**
接続前は「192.168.x.x」のローカルネットワークであり、VPNのサブネットルートである、「172.16.x.x」にトラフィックは流れていないことが確認できました。
```
IPv4 ルート テーブル
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0      192.168.1.1      192.168.1.9     35
        127.0.0.0        255.0.0.0            リンク上         127.0.0.1    331
        127.0.0.1  255.255.255.255            リンク上         127.0.0.1    331
  127.255.255.255  255.255.255.255            リンク上         127.0.0.1    331
      192.168.1.0    255.255.255.0            リンク上       192.168.1.9    291
      192.168.1.9  255.255.255.255            リンク上       192.168.1.9    291
    192.168.1.255  255.255.255.255            リンク上       192.168.1.9    291
     192.168.99.0    255.255.255.0            リンク上      192.168.99.1    281
     192.168.99.1  255.255.255.255            リンク上      192.168.99.1    281
```

**接続後**

:::note warn
接続後は、VPNのサブネットルートである「172.16.x.x」が含まれる結果から、VPN経由のトラフィックと判断することができます。
:::

```
IPv4 ルート テーブル
アクティブ ルート:
ネットワーク宛先        ネットマスク          ゲートウェイ       インターフェイス  メトリック
          0.0.0.0          0.0.0.0      192.168.1.1      192.168.1.9     35
          0.0.0.0        128.0.0.0       172.16.1.9      172.16.1.10    281
        127.0.0.0        255.0.0.0            リンク上         127.0.0.1    331
        127.0.0.1  255.255.255.255            リンク上         127.0.0.1    331
  127.255.255.255  255.255.255.255            リンク上         127.0.0.1    331
        128.0.0.0        128.0.0.0       172.16.1.9      172.16.1.10    281
       172.16.1.0    255.255.255.0       172.16.1.9      172.16.1.10    225
       172.16.1.8  255.255.255.252            リンク上       172.16.1.10    281
      172.16.1.10  255.255.255.255            リンク上       172.16.1.10    281
      172.16.1.11  255.255.255.255            リンク上       172.16.1.10    281
      192.168.1.0    255.255.255.0            リンク上       192.168.1.9    291
```

ここまでで上手くいってない箇所がある場合は、OpenVPNログの確認やクライアント側およびサーバー側のファイアウォールが、必要なポートを許可していることを確認してみてください。

## まとめ
この記事では、自宅のAlmaLinux環境でOpenVPNサーバーを構築し、Windows 11端末からの接続検証を行った記録をまとめました。
ルーターがVPN対応していなかったことは、残念でしたが、家庭内ネットワークからVPNサーバーへ接続することはできたので、今回の構築は成功と言えそうです。

OpenVPNサーバーの動作確認もこれでばっちりですね！


## おまけ
実務的な観点から、クライアントに配布した証明書の更新期間を変更する場合には、証明書の再発行が必要になることを考慮し、今後はその手続きについても検討していきたいと思いました。

## 参考記事
https://weblog.hirohiro716.com/?p=2345

https://qiita.com/koko_kotsu/items/17c0313fc10df484ee86

https://www.yuuronacademy.com/article/2023/12/06/1190/

https://academy.gmocloud.com/wp/other/20180404/6320

http://poppypop.mydns.jp/TechnicalArticle/Linux/OpenVPN/