CentOS8代替OSでのOpenVPNサーバのインストール方法

2022年6月17日

前提

書くこと/書かないこと

OpenVPNをインストールし、クライアントとなるWindowsPCからOpenVPNサーバとその先のネットワーク上にあるサーバに接続する方法を書きます。
過去にCentOS6, 7やAmazon Linux2での構築は何度か経験がありましたが、今回は初めてCentOS8代替OSでの構築だったため、iptablesとnftablesの違いで結構苦戦しました。

環境

AlmaLinux release 8.6 (Sky Tiger) ※ CentOS8代替OS

実現イメージ

今回構築する環境は下記のような構成になります。
プライベートネットワーク内にあるサーバに外部からアクセスするため、OpenVPNを構成します。

各種のクラウドサービスで初めてサーバを構築する場合、環境によっては外部からアクセスする方法が問題となることがあります。

例えばAWSの場合だと、Direct ConnectやSite-to-Site VPNを使って他のネットワークからアクセスする方法や、Amazon WorkspacesなどのDaaSサービスを使い内側からアクセスする方法もありますが、個人利用や小規模なユースケースでは費用対効果に合わないことも考えられます。

今回の構成を採用すると、低スペックなサーバ1台でVPNを張ることが可能になります。

VPN接続するとき以外はサーバを落とせば、限界までコストを削減することができ、コンソール等からインスタンスを起動しないかぎりVPN Gatewayが見えない状態はセキュアであると考えることもできます。

OpenVPN Server構築手順

認証局の構築

epel-releaseのインストール

今回利用するOpenVPNとeasy-rsaはdnfの標準リポジトリではなくepelで提供されていますので、まずはリポジトリを追加します。
手動追加してもいいのですが、epelの定義情報がdnfで提供されているため、dnfからインストールします。

# dnf install epel-release
(省略)
インストール済み:
  epel-release-8-10.el8.noarch

完了しました!

dnfで参照するリポジトリ情報が定義される/etc/yum.repos.d/の下を確認すると、epelの定義ファイルが作成されていることが確認できます。

# ls -al /etc/yum.repos.d/
合計 64
drwxr-xr-x.  2 root root 4096  6月 12 18:49 .
drwxr-xr-x. 85 root root 8192  6月 12 18:49 ..
-rw-r--r--   1 root root 1177 12月  6  2020 epel-modular.repo
-rw-r--r--   1 root root 1259 12月  6  2020 epel-playground.repo
-rw-r--r--   1 root root 1276 12月  6  2020 epel-testing-modular.repo
-rw-r--r--   1 root root 1213 12月  6  2020 epel-testing.repo
-rw-r--r--   1 root root 1114 12月  6  2020 epel.repo

easy-rsaのインストール

OpenVPNで公開鍵認証を行うため、先に認証局を構築しておきます。
認証局の構築にはeasy-rsaを使います。

# dnf install easy-rsa
(省略)
インストール済み:
  easy-rsa-3.0.8-1.el8.noarch

完了しました!

インストール後は最初に認証局の初期化を行います。

# cd /usr/share/easy-rsa/3/
# ./easyrsa init-pki

init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /usr/share/easy-rsa/3/pki

認証局の構築

ここから認証局の構築を行っていきます。

# ./easyrsa build-ca
Using SSL: openssl OpenSSL 1.1.1k  FIPS 25 Mar 2021

Enter New CA Key Passphrase: ★任意のパスフレーズ
Re-Enter New CA Key Passphrase: ★任意のパスフレーズ
Generating RSA private key, 2048 bit long modulus (2 primes)
.......................+++++
..............+++++
e is 65537 (0x010001)
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Common Name (eg: your user, host, or server name) [Easy-RSA CA]: ★任意の認証局名

CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/usr/share/easy-rsa/3/pki/ca.crt

ダイアログが完了するとpki/ca.crtとpki/private/ca.keyが作成されます。がca.crtが認証局の公開鍵(証明書)、ca.keyが認証局の秘密鍵となります。

DHパラメータの生成

次にDHパラメータを生成します。この処理は少し時間がかかります。

# ./easyrsa gen-dh
Using SSL: openssl OpenSSL 1.1.1k  FIPS 25 Mar 2021
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
(省略)
DH parameters of size 2048 created at /usr/share/easy-rsa/3/pki/dh.pem

サーバ証明書の生成

easy-rsaでサーバの秘密鍵と公開鍵の鍵ペアを生成し、公開鍵に対して認証局の秘密鍵で署名します。

# ./easyrsa build-server-full [任意のサーバ識別子(ファイル名)] nopass
Using SSL: openssl OpenSSL 1.1.1k  FIPS 25 Mar 2021
Generating a RSA private key
..............................................................................................................................................................................................................................................................+++++
...........+++++
writing new private key to '/usr/share/easy-rsa/3/pki/easy-rsa-4793.vt9tRN/tmp.cufOly'
-----
Using configuration from /usr/share/easy-rsa/3/pki/easy-rsa-4793.vt9tRN/tmp.fztXiI
Enter pass phrase for /usr/share/easy-rsa/3/pki/private/ca.key: ★認証局構築時に設定したパスフレーズ
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'[任意のサーバ識別子(ファイル名)]'
Certificate is to be certified until Sep 14 10:31:33 2024 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

クライアント証明書の生成

サーバ証明書と同様にクライアントの秘密鍵と公開鍵を生成し、公開鍵に対して認証局の秘密鍵で署名します。

# ./easyrsa build-client-full [任意のクライアント識別子(ファイル名)] nopass
Using SSL: openssl OpenSSL 1.1.1k  FIPS 25 Mar 2021
Generating a RSA private key
..............+++++
.................................................+++++
writing new private key to '/usr/share/easy-rsa/3/pki/easy-rsa-5051.4VRurW/tmp.HKQSSH'
-----
Using configuration from /usr/share/easy-rsa/3/pki/easy-rsa-5051.4VRurW/tmp.TJpuHA
Enter pass phrase for /usr/share/easy-rsa/3/pki/private/ca.key: ★認証局構築時に設定したパスフレーズ
Check that the request matches the signature
Signature ok
The Subject's Distinguished Name is as follows
commonName            :ASN.1 12:'[任意のクライアント識別子(ファイル名)]'
Certificate is to be certified until Sep 14 10:34:16 2024 GMT (825 days)

Write out database with 1 new entries
Data Base Updated

ここまでで、認証局を構築し、OpenVPNのサーバとクライアントそれぞれの鍵ペアを作成しました。
今回認証局をOpenVPNサーバと同じサーバに自前で建てましたがこれは必須ではありません。今回の方法が最もお手軽だと思いますが、状況によっては社内や外部の認証局を利用することが求められるケースもあるかも知れません。

OpenVPNサーバの構築

OpenVPNサーバのインストール

パッケージを利用できるか確認します。

# dnf list openvpn
Extra Packages for Enterprise Linux 8 - x86_64   15 MB/s |  11 MB     00:00
Extra Packages for Enterprise Linux Modular 8 - 1.1 MB/s | 1.0 MB     00:00
メタデータの期限切れの最終確認: 0:00:01 時間前の 2022年06月12日 18時52分43秒 に 実施しました。
利用可能なパッケージ
openvpn.x86_64                         2.4.12-1.el8                         epel

epelからOpenVPNの2.4.12を利用できることが確認できましたので、これをインストールします。

 # dnf install -y openvpn
(省略)
インストール済み:
  openvpn-2.4.12-1.el8.x86_64          pkcs11-helper-1.22-7.el8.x86_64

完了しました!

TLS証明書の作成

設定の前にまずはTLS証明書を作成します。

# cd /etc/openvpn/server/
# openvpn --genkey --secret ta.key
# ls ./
ta.key

configファイルの準備

server用のconfigファイルのサンプルが別の場所にあるので設定用ディレクトリにコピーします。

# cp /usr/share/doc/openvpn/sample/sample-config-files/server.conf /etc/openvpn/server/server.conf

configファイルに設定する証明書や秘密鍵などを設定用ディレクトリにまとめます。
dh.pemだけファイル名を変えているので注意してください(confファイルの設定値と合わせればいいので変更は必須ではありません)。

# ln -s /usr/share/easy-rsa/3/pki/issued/server.crt /etc/openvpn/server/
# ln -s /usr/share/easy-rsa/3/pki/private/server.key /etc/openvpn/server/
# ln -s /usr/share/easy-rsa/3/pki/ca.crt /etc/openvpn/server/
# ln -s /usr/share/easy-rsa/3/pki/dh.pem /etc/openvpn/server/dh2048.pem

server.confの編集

先ほどコピーしたserver.confを編集します。
今回は最低限の箇所だけ設定していきます。

(省略)
# Push routes to the client to allow it
# to reach other private subnets behind
# the server.  Remember that these
# private subnets will also need
# to know to route the OpenVPN client
# address pool (10.8.0.0/255.255.255.0)
# back to the OpenVPN server.
push "route 10.0.0.0 255.0.0.0" ★プライベートネットワークのIPレンジを指定

(省略)
# It's a good idea to reduce the OpenVPN
# daemon's privileges after initialization.
#
# You can uncomment this out on
# non-Windows systems.
user nobody ★コメントアウトを解除
group nobody ★コメントアウトを解除

(省略)
# By default, log messages will go to the syslog (or
# on Windows, if running as a service, they will go to
# the "\Program Files\OpenVPN\log" directory).
# Use log or log-append to override this default.
# "log" will truncate the log file on OpenVPN startup,
# while "log-append" will append to it.  Use one
# or the other (but not both).
log-append /var/log/openvpn.log ★ログファイル出力先を指定

OpenVPNサーバの起動

サービスの自動起動をONにてサービスを起動します。
ここでのポイントはUnit名の”@”の後ろを"server.service"にすることです。

# systemctl enable openvpn-server@server.service
# systemctl start openvpn-server@server.service
# systemctl status openvpn-server@server.service
●openvpn-server@server.service - OpenVPN service for server
   Loaded: loaded (/usr/lib/systemd/system/openvpn-server@.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2022-06-14 23:13:26 JST; 1min 18s ago
     Docs: man:openvpn(8)
           https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
           https://community.openvpn.net/openvpn/wiki/HOWTO
 Main PID: 1539 (openvpn)
   Status: "Initialization Sequence Completed"
    Tasks: 1 (limit: 5936)
   Memory: 2.4M
   CGroup: /system.slice/system-openvpn\x2dserver.slice/openvpn-server@server.service
           └ 1539 /usr/sbin/openvpn --status /run/openvpn-server/status-server.log --status-version 2 --suppress-timestamps --cipher AES-256-GCM --ncp-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC:AES-128-CBC:BF-CBC --config server.conf
(省略)

これでOpenVPNサーバが起動しました。

IPフォワーディングの設定

OpenVPNサーバを立てただけでは、パケットを他のサーバに転送することができません。
それを可能にするためにIPフォワーディングを有効化します。

まずはsysctl.confに下記内容を記載して保存します。

net.ipv4.ip_forward=1

設定を反映します。

# sysctl -p
net.ipv4.ip_forward = 1

これでOpenVPNサーバがパケットを中継し、目的のサーバと通信することができるようになりました。

nftablesの設定

次にパケットフィルタリング機能であるnftablesの設定です。

結構ハマったポイントですが、結論だけ記載します。
パケットを中継させるための設定を設定ファイルに記載します。
まずは新しい設定ファイルを作成し、下記の内容を記載して保存します。
※ 下記の記述ではすべての通信をOpenVPNクライアントからのすべての通信をプライベートネットワークに転送します。用途がはっきりしている場合はポートを限定するなどのセキュリティ対策を講じるべきです。

table ip nat {
  chain POSTROUTING {
    type nat hook postrouting priority 100; policy accept;
    oifname "{プライベートネットワーク用のネットワークインターフェース名}" ip saddr {OpenVPNクライアントに割り振るCIDR} masquerade
  }
}

なお、{プライベートネットワーク用のネットワークインターフェース名}はサーバ上でifconfigを実行して確認することができます。

下記の例だと"ens160″です。

次にこの設定ファイルを読み込むようにするため、confファイルの末尾に下の内容を追記します。

(省略)
include "/etc/nftables/openvpn.nft"

nftablesを起動します。自動起動の設定も合わせて行います。

# systemctl enable nftables.service
# systemctl start nftables.service
# systemctl status nftables.service
●nftables.service - Netfilter Tables
   Loaded: loaded (/usr/lib/systemd/system/nftables.service; disabled; vendor preset: disabled)
   Active: active (exited) since Thu 2022-06-16 20:56:43 JST; 2min 48s ago
     Docs: man:nft(8)
  Process: 2225 ExecStop=/sbin/nft flush ruleset (code=exited, status=0/SUCCESS)
  Process: 2233 ExecStart=/sbin/nft -f /etc/sysconfig/nftables.conf (code=exited, status=0/SUCCESS)
  Main PID: 2233 (code=exited, status=0/SUCCESS)

 6月 16 20:56:43 hostname systemd[1]: Starting Netfilter Tables...
 6月 16 20:56:43 hostname systemd[1]: Started Netfilter Tables.

OpenVPNクライアントの設定

クライアント側に必要となるファイル

サーバ側で用意した各種ファイルのうち、下記についてはクライアントの設定で使うので、SFTPやWinSCPなどを使い対象マシンに持ってきます。

サーバ証明書:ca.crt
TLS鍵:ta.key
クライアントの鍵ペア: client.crt, client.key
※ “client"はクライアント証明書の生成時に指定した任意のクライアント識別子

OpenVPNクライアントのインストール

OpenVPNのクライアントとなる端末はケースによって異なると思いますが、今回はWindows PCをクライアントとするケースで説明します。

OpenVPNクライアントには公式のOpenVPN GUI for Windowsもありますが、使い勝手の良さからvpnux Clientを使います。下記の公式サイトからダウンロードし、インストールしてください(手順は簡単なので省略します)。

vpnux Client

vpnux Clientの設定

vpnuxを起動し、下部の[プロファイル]から追加を押すと下記の画面が表示されます。

設定する内容はOpenVPNサーバの設定次第の箇所も多々あるので、ここでは最低限必要な証明書や鍵関係の対応だけ記載します。

[プロファイル名]: 任意のプロファイル名(MyVPN)
[VPNサーバー]: OpenVPNサーバのグローバルIP
[拡張設定]->[TLS-Auth HMAC署名を使用]: チェックする
[拡張設定]->[TLS-Auth HMAC署名を使用]->[共通鍵]: ta.key
[認証]->[CA証明書]: ca.crt
[認証]->[証明書認証(PKI)を使用]: チェックする
[認証]->[証明書(PKI)を使用]->[証明書認証(PKI)]->[証明書]: client.crt
[認証]->[証明書(PKI)を使用]->[証明書認証(PKI)]->[秘密鍵]: client.key

上記を入力またはファイルから読み込みして保存します。

vpnuxの起動確認

先ほど作成したプロファイルを選んで[接続]を押します。

接続が始まります。

接続に成功すると下記のような通知メッセージが表示されます。

これでVPNサーバへの接続は完了です。
繋がらない場合は設定ファイルに誤りがないか、クライアントからOpenVPNサーバまでの経路上のF/Wでポート(1194/UDP)が閉じてないかなどを確認してください。

動作確認

vpnuxが起動しているOpenVPNクライアントから、OpenVPNサーバとその先にあるサーバに対してpingを試行し、応答が確認出来たらOKです。

ちなみに、プライベートネットワーク内にあるサーバとの通信はVPNでトンネリングするため、ポートは1194/UDPだけ空いていれば大丈夫です。