第27章 OpenVPNによるVPN構築
LPIC-2 212.5 相当
VPN(Virtual Private Network)は、インターネットのような公共のネットワークの上に、暗号化された仮想的な専用トンネルを作る技術です。本章ではLPIC-2の出題範囲に含まれるOpenVPNを中心に、サーバー構築からクライアント接続までを扱います。
VPNの利用形態とOpenVPNの位置づけ
| ソフトウェア | 特徴 |
|---|---|
| OpenVPN | SSL/TLSをベースにした、歴史が長く柔軟な設定が可能なVPN。証明書ベースの認証が標準 |
| WireGuard | 比較的新しく、シンプルな設定と高速な処理を特徴とするVPN。公開鍵ペアによる認証 |
| IPsec | IPプロトコル自体を拡張した規格。拠点間(サイト間)VPNで古くから使われ、多くのネットワーク機器が標準対応 |
VPNの利用形態は大きく2つに分けられます。「サイト間(site-to-site)VPN」は拠点のルーター同士を常時接続し、拠点全体のネットワークをまとめてつなぐ構成です。「リモートアクセスVPN」は個々の端末(在宅勤務者のPCなど)から必要なときだけ社内ネットワークへトンネルを張る構成で、OpenVPNは主にこちらの用途で使われますが、設定次第でサイト間VPNとしても利用できます。
WireGuardはOpenVPNと比べて設定項目が少なく、実装がカーネル内(Linuxカーネル5.6以降は標準搭載)に組み込まれていることもあって、スループットや接続確立の速さで有利とされます。一方でOpenVPNは、TCP/UDPどちらのトランスポートも選べる柔軟性や、証明書失効の仕組み・ユーザー認証との連携など、大規模な組織で運用する際の管理機能が豊富という強みがあります。LPIC-2の出題範囲としてはOpenVPNが中心ですが、「新しく軽量なWireGuard」「歴史が長く柔軟なOpenVPN」という位置づけの違いを押さえておくとよいでしょう。
OpenVPNのインストールとPKI(公開鍵基盤)の準備
OpenVPNはクライアント/サーバー型の証明書ベース認証を基本とします。サーバー・クライアントの双方が、共通のCA(認証局)によって署名された証明書を持つことで、互いを認証します。
$ sudo apt install openvpn easy-rsa # Debian/Ubuntu系(インストールログは省略)
$ sudo dnf install openvpn easy-rsa # RHEL/CentOS系(インストールログは省略)
$ openvpn --version
OpenVPN 2.6.11 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD]
library versions: OpenSSL 3.0.13 30 Jan 2024, LZO 2.10
この行のこの値に注目: openvpn --versionの[AEAD]は、後述のAES-256-GCMのような認証付き暗号(AEAD)モードに対応していることを示します。バージョンによってサポートする暗号アルゴリズムやオプションが異なるため、サーバー・クライアント双方で使えるバージョンかどうかをまず確認しておくと、後続のトラブルシューティングが楽になります。
証明書一式の発行にはeasy-rsaを使うのが一般的です。CA証明書、サーバー証明書・鍵、Diffie-Hellmanパラメータ、TLS認証鍵を順に作成します。
$ make-cadir ~/openvpn-ca # 正常時は無出力
$ cd ~/openvpn-ca
$ ./easyrsa init-pki
init-pki complete; you may now create a CA or requests.
Your newly created PKI dir is: /home/alice/openvpn-ca/pki
$ ./easyrsa build-ca # CA証明書を作成(パスフレーズを設定)
Enter New CA Key Passphrase:
Re-Enter New CA Key Passphrase:
Common Name (eg: your user, host, or server name) [Easy-RSA CA]: Example Corp VPN CA
CA creation complete and you may now import and sign cert requests.
Your new CA certificate file for publishing is at:
/home/alice/openvpn-ca/pki/ca.crt
$ ./easyrsa gen-req server nopass # サーバー用の証明書署名要求(CSR)を作成
Keypair and certificate request completed. Your files are:
req: /home/alice/openvpn-ca/pki/reqs/server.req
key: /home/alice/openvpn-ca/pki/private/server.key
$ ./easyrsa sign-req server server # CAでサーバー証明書に署名
Confirm request details: [Confirmed with 'yes']
Certificate created at: /home/alice/openvpn-ca/pki/issued/server.crt
$ ./easyrsa gen-dh # DHパラメータを生成(時間がかかる)
Generating DH parameters, 2048 bit long safe prime
...............+.........................+....(省略、数分かかることがある)
DH parameters appear to be ok.
$ openvpn --genkey secret ta.key # TLS認証用の共有鍵を生成(任意だが推奨)(正常時は無出力)
この行のこの値に注目: init-pki completeやCA creation completeのように、各ステップの完了メッセージには生成されたファイルの絶対パスが必ず添えられます。後続のsign-reqでserver.crtが意図した場所に作られているか、この出力で都度確認できます。gen-dhは他のステップより明確に時間がかかる処理で、進捗が.や+のドットで表示されるだけなので、しばらく応答がなくてもフリーズではありません。
クライアントごとにも同様の手順で個別の証明書を発行します(./easyrsa gen-req client1 nopass → ./easyrsa sign-req client client1)。クライアントごとに証明書を分けておくことで、退職者や紛失端末が出た場合に、そのクライアントの証明書だけを失効させる(easyrsa revokeしてgen-crlで失効リストを更新する)運用が可能になります。
サーバー側の設定(server.conf)
OpenVPNサーバー設定の例(/etc/openvpn/server/server.conf)
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0
server 10.8.0.0 255.255.255.0
push "route 192.168.1.0 255.255.255.0"
push "dhcp-option DNS 192.168.1.1"
keepalive 10 120
cipher AES-256-GCM
persist-key
persist-tun
user nobody
group nogroup
status openvpn-status.log
verb 3
$ sudo systemctl start openvpn-server@server # 正常時は無出力
$ sudo systemctl enable openvpn-server@server # 正常時は無出力
server 10.8.0.0 255.255.255.0は、接続してきたクライアントに10.8.0.0/24の範囲からIPアドレスを払い出すVPN内部ネットワークを定義します。push "route ..."行は、クライアント側のルーティングテーブルに、社内LANへ到達するための経路を自動的に追加させる指示です。これがないと、クライアントはVPNサーバーとは通信できても、その先の社内LANには到達できません。push "dhcp-option DNS ..."行は、クライアント側のDNSサーバーを社内のDNSサーバーに切り替えさせる指定で、社内限定のホスト名を名前解決させたい場合に使います。
user nobody・group nogroupは、起動直後の初期化(TUNデバイスの作成やポートのバインドなど、root権限が必要な処理)が終わった後、OpenVPNのプロセス権限を非特権ユーザーに落とす設定です。VPNサーバーはインターネットから直接到達可能な公開サービスであるため、万が一OpenVPN自体に未知の脆弱性があり乗っ取られたとしても、プロセスの権限があらかじめ制限されていれば、被害をそのユーザー権限の範囲に抑えられます。persist-key・persist-tunは、この権限降格の際に鍵ファイルやTUNデバイスを再オープンせずに済ませるための設定で、権限降格とセットで使うのが基本です。
tunデバイスとtapデバイスの違い
| デバイス種別 | 動作レイヤー | 特徴 |
|---|---|---|
| tun | OSI第3層(IP層) | IPパケットのみをルーティングする。ブロードキャストやARPは通らない。オーバーヘッドが小さく、一般的なリモートアクセスVPNではこちらが標準 |
| tap | OSI第2層(イーサネット層) | イーサネットフレーム単位で扱うため、ブリッジ接続が可能。ブロードキャストを含むLANをそのまま拡張したい場合(拠点間でDHCPやNetBIOSを通したいなど)に使う |
dev tunとdev tapの指定は、サーバー・クライアントの両方で一致させる必要があります。特別な理由がない限り、シンプルで安全なtunモードを選択するのが基本です。
クライアント側の設定(.ovpn)
クライアントに配布する設定ファイルは、慣習的に.ovpn拡張子でまとめられます。CA証明書・クライアント証明書・クライアント鍵・TLS認証鍵をインライン埋め込みにして、1ファイルで完結させることも一般的です。
クライアント設定の例(client1.ovpn)
client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
(CA証明書の内容)
</ca>
<cert>
(クライアント証明書の内容)
</cert>
<key>
(クライアント秘密鍵の内容)
</key>
<tls-auth>
(TLS認証鍵の内容)
</tls-auth>
key-direction 1
$ sudo openvpn --config client1.ovpn # 手動で接続
2026-08-21 10:05:12 TCP/UDP: Preserving recently used remote address: [AF_INET]203.0.113.10:1194
2026-08-21 10:05:12 UDP link local: (not bound)
2026-08-21 10:05:12 UDP link remote: [AF_INET]203.0.113.10:1194
2026-08-21 10:05:12 TLS: Initial packet from [AF_INET]203.0.113.10:1194, sid=...
2026-08-21 10:05:13 VERIFY OK: depth=1, CN=Example Corp VPN CA
2026-08-21 10:05:13 VERIFY OK: depth=0, CN=vpn.example.com
2026-08-21 10:05:14 Data Channel: using negotiated cipher 'AES-256-GCM'
2026-08-21 10:05:14 net_addr_v4_add: 10.8.0.6/24 dev tun0
2026-08-21 10:05:14 Initialization Sequence Completed
$ sudo systemctl start openvpn-client@client1 # 正常時は無出力
この行のこの値に注目: VERIFY OK: depth=0, CN=vpn.example.comは、接続先サーバーが提示した証明書のCommon Nameが検証に成功したことを示し、remote-cert-tls serverによる中間者攻撃対策が機能していることの確認になります。最終行のInitialization Sequence Completedが表示されれば、VPNトンネルの確立が完了しています。net_addr_v4_add: 10.8.0.6/24から、このクライアントに割り当てられたVPN内部IPアドレスが分かります。
remote-cert-tls serverは、接続先が確かにサーバー証明書(クライアント証明書ではない)を提示していることを検証する指定で、中間者攻撃(別のクライアント証明書を使ったなりすまし)を防ぐために重要です。
サイト間VPNとリモートアクセスVPNの構成の違い
ここまで見てきた設定は、個々の端末がVPNサーバーに接続する「リモートアクセスVPN」を前提としています。「サイト間(site-to-site)VPN」として拠点同士のLANをまるごとつなぐ場合は、両拠点のルーター(あるいはゲートウェイとなるLinuxサーバー)にそれぞれOpenVPNをインストールし、片方をサーバー、もう片方をクライアントとして常時接続させます。この場合、push "route ..."で相手拠点のLANセグメントの経路を伝え合い、さらに双方のゲートウェイでnet.ipv4.ip_forward = 1を有効にしてIPフォワーディングを許可することで、拠点Aの端末から拠点Bの端末へ、VPNトンネル経由で透過的に通信できるようになります。
| 構成 | 接続の性質 | 典型的な用途 |
|---|---|---|
| リモートアクセスVPN | 個々の端末が必要なときだけ接続 | 在宅勤務者が社内リソースにアクセスする |
| サイト間VPN | 拠点のゲートウェイ同士が常時接続 | 本社と支社のLANを常時つなぐ、拠点間でファイルサーバーを共有する |
1台のOpenVPNサーバーに複数拠点(あるいは複数クライアント)を接続し、それぞれの拠点固有のLANセグメントへ経路を振り分けたい場合は、client-config-dirディレクティブとirouteを組み合わせます。
server.confへの追記
client-config-dir /etc/openvpn/ccd
/etc/openvpn/ccd/branch-a(クライアント証明書のCommon Nameと同名のファイル)
iroute 192.168.10.0 255.255.255.0
client-config-dirで指定したディレクトリの中に、クライアント証明書のCommon Name(CN)と同名のファイルを置いておくと、そのクライアントが接続してきた際に個別の設定を適用できます。irouteは、サーバー側の内部ルーティングテーブルに「このクライアント(拠点)の先には、このネットワークがある」と教える指定で、拠点ごとに異なるLANセグメントをまとめて1台のOpenVPNサーバーで中継したい場合に使います。push "route ..."がクライアント側に経路を教えるのに対し、irouteはサーバー側が「どのクライアント宛にパケットを転送すればよいか」を把握するためのものである点が異なります。
フルトンネルとスプリットトンネル
クライアントの全トラフィックをVPN経由にするか、社内LAN宛の通信だけをVPN経由にするかも、設計上の重要な選択です。
| 方式 | 動作 | 特徴 |
|---|---|---|
| フルトンネル | クライアントの全通信をVPN経由にする(push "redirect-gateway def1") | インターネット全体の通信もVPNサーバー経由になり、社給端末の通信を一括監査・フィルタリングしたい場合に有効。その分VPNサーバーの負荷と回線帯域を消費する |
| スプリットトンネル | 社内LAN宛の経路だけをpushし、それ以外はクライアントのローカル回線を直接使う | VPNサーバーの負荷を抑えられるが、クライアント端末が同時に社外の通信経路も持つため、セキュリティポリシー上は管理が必要になる |
redirect-gateway def1をpushするとフルトンネル構成になります。何も指定しなければ、サーバーがpushしたルートの範囲だけがVPN経由になるスプリットトンネル構成が既定の動作です。
証明書失効リスト(CRL)の配布
クライアント証明書を失効させても、サーバー側がその失効情報を参照していなければ、失効したはずの証明書で接続できてしまいます。失効リスト(CRL: Certificate Revocation List)をサーバーに読み込ませることで、失効済み証明書での接続を拒否できます。
$ ./easyrsa revoke client1 # client1の証明書を失効させる
Please confirm you wish to revoke the certificate with the following subject:
subject=
commonName = client1
Type the word 'yes' to continue, or any other input to abort.
Continue with revocation: yes
Revoking Certificate 8600B3C1F4D9E2A0.
Certificate revoked.
$ ./easyrsa gen-crl # 失効リスト(crl.pem)を再生成
An updated CRL has been created.
CRL file: /home/alice/openvpn-ca/pki/crl.pem
$ sudo cp pki/crl.pem /etc/openvpn/server/ # 正常時は無出力
この行のこの値に注目: revoke実行時には対象のcommonName(この例ではclient1)が表示され、yesと入力して初めて失効が確定します。誤って別のクライアントを失効させないよう、この確認画面で対象を必ず確認してください。gen-crlの出力にあるCRL file:のパスから生成されたファイルをコピーし、サーバーのcrl-verifyで参照しているパスと一致していることを確認します。
server.conf に追記
crl-verify crl.pem
gen-crlで生成した失効リストは有効期限を持つため、定期的に再生成してサーバーに反映する運用が必要です。反映を忘れると、失効させたはずの証明書がいつまでも有効なままになってしまう点に注意してください。
接続状態の確認とトラブルシューティング
$ sudo cat /etc/openvpn/server/openvpn-status.log # 現在接続中のクライアント一覧を確認
OpenVPN CLIENT LIST
Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since
client1,203.0.113.20:52344,1048576,2097152,2026-08-21 09:50:12
ROUTING TABLE
Virtual Address,Common Name,Real Address,Last Ref
10.8.0.6,client1,203.0.113.20:52344,2026-08-21 10:05:14
$ sudo journalctl -u openvpn-server@server -f # サーバーログをリアルタイムで確認
Aug 21 10:05:13 vpn01 openvpn[3210]: 203.0.113.20:52344 VERIFY OK: depth=0, CN=client1
Aug 21 10:05:14 vpn01 openvpn[3210]: client1/203.0.113.20:52344 MULTI: Learn: 10.8.0.6 -> client1/203.0.113.20:52344
Aug 21 10:05:14 vpn01 openvpn[3210]: client1/203.0.113.20:52344 Initialization Sequence Completed
$ ip addr show tun0 # クライアント側でtunインターフェースが作成されているか確認
4: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 10.8.0.6/24 scope global tun0
valid_lft forever preferred_lft forever
この行のこの値に注目: openvpn-status.logのCLIENT LISTから現在接続中のクライアントとその接続元IP、ROUTING TABLEからVPN内部でどのIP(10.8.0.6)がどのクライアントに割り当てられているかが分かります。journalctlでInitialization Sequence Completedが該当クライアントのCNと共に出ていれば接続確立は正常です。クライアント側のip addr show tun0でUP,LOWER_UPとinet 10.8.0.6/24が表示されていれば、VPN内部アドレスが正しく割り当てられ、インターフェース自体は正常に稼働しています(それでも通信できない場合は、ルーティングやファイアウォールの設定を疑います)。
接続できない場合の典型的な原因としては、UDP 1194番ポート(変更していれば該当ポート)がファイアウォールで許可されていない、サーバー・クライアント間でdev tun/dev tapの指定が一致していない、証明書の有効期限切れやCAの不一致、サーバー側でnet.ipv4.ip_forward = 1が有効になっておらずパケットが転送されない、といったものが挙げられます。