OpenWrt の OpenVPN(サーバー)についての包括的メモ

一応、公式に基本ガイド(👉 OpenVPN Basic)があるが、「OpenWrt の OpenVPN 基本ガイドの徹底解析」で先日述べたように何かと不満があるので、自分用を兼ねて包括的なメモを記しておくことにする。

情報の鮮度的には、古い方のルーター(OpenWrt v18.06.2)が OpenVPN v2.4.5、新しい方のルーター(OpenWrt v19.07-SNAPSHOT)が OpenVPN v2.4.7 である。

また、クライアント側は、macOS の Tunnelblick を使用している。


ソフトウェア

openvpn-openssl と openvpn-easy-rsa(と luci-app-openvpn)を LuCI からインストールした。


PKI

PKI については基本ガイドや巷の情報でほとんど十分だと思うので基本的に割愛する(👉 Easy-RSA 3 QuickstartEasy-RSA 3)。PKI 用の easyrsa コマンドは /etc/easy-rsa で実行し、PKI のパスが /etc/easy-rsa/pki となりその以下に各ファイルが配置されている。ただし、openvpn コマンドで作成する tlscrypt.key のみ、/etc/openvpn に置いている。


cd /etc/easy-rsa
easyrsa init-pki
easyrsa build-ca nopass
easyrsa build-server-full openvpn-server nopass
easyrsa build-client-full openvpn-client nopass
easyrsa gen-dh
openvpn --genkey --secret /etc/openvpn/tlscrypt.key

TAP サーバーとして動かす場合

TAP サーバーとして設定するのは特に難しくなく、既存の情報で何か不足するようなこともない。

tap0 の準備

OpenVPN 自体の設定に一所懸命になっていると、この作業を思わず忘れてしまっていて動かずに原因がわからずに悩む場合があるが、仮想のものではあるとは言っても、まずは tap0 というインターフェースを用意してアサインしないことには、外部から何ら情報を受け取ることができない。

LuCI の Network > Interfaces から論理インターフェース「LAN」の編集画面に入り、Physical Settings タブで所属する物理インターフェースを設定できるので、(仮想ではあるが)物理インターフェースとして「tap0」を LAN にアサインする(存在しなければ Custom Interface で「tap0」とすればよい)。これによって、論理インターフェース「LAN」の 192.168.1.0/24 のネットワークに tap0 を参加させることができる。

Firewall の設定は不要である。LAN の参加する Firewall ゾーン「LAN」の設定の管理下に入るだけである。

※インターフェースに変更を行うと LuCI の Save & Apply では不十分で、インターフェース自体を再起動する必要がある。

サーバー側の設定(/etc/config/openvpn)
config openvpn 'bridge_server'
 option enabled '1'
 option dev 'tap0'
 option port '51194'
 option proto 'tcp-server'
 option compress 'lz4-v2'
 option dh        '/etc/easy-rsa/pki/dh.pem'
 option ca        '/etc/easy-rsa/pki/ca.crt'
 option cert      '/etc/easy-rsa/pki/issued/openvpn-server.crt'
 option key       '/etc/easy-rsa/pki/private/openvpn-server.key'
 option tls_crypt '/etc/openvpn/tlscrypt.key'
 option remote_cert_tls 'client'
 option key_direction '0'
 option keepalive '10 120'
 option persist_tun '1'
 option persist_key '1'
 option user 'nobody'
 option group 'nogroup'
 option verb '5'
 option server_bridge '192.168.1.1 255.255.255.0 192.168.1.250 192.168.1.254'
 list push 'redirect-gateway def1'
 list push 'dhcp-option DOMAIN myhomelan'
 list push 'dhcp-option DNS 192.168.1.1'
 list push 'persist-tun'
 list push 'persist-key'
  • OpenVPN サーバーのルーターが実際に所属するネットワーク(ここでは 192.168.1.0/24)に直接接続されることになるので、server-bridge(server_bridge)としてはルーター自体のアドレスとネットマスク、後ろの 2 つは同じネットワークの中からクライアントに割り当てる IP アドレスプールを範囲で示す。
  • dhcp-option DOMAIN は、ドメインを省略してホスト名だけで示した場合に自動補完されるべきローカルのドメイン名で、オマケ的もの。
  • dhcp-option DNS は必須。ここではゲートウェイを DNS サーバーとして示している。
  • tls-key を使っていないので、key-direction が本当に必要で実際に機能しているのかは不明。
  • proto 'tcp-server' なので mssfix を設定せずとも一応どうにかなると思う。理想的には、proto 'udp' にして且つ、適切な mssfix を ping の試行を繰り返して決定しておくことである。
  • compress 'lz4-v2' は、Mac 側の Tunnelblick との間で両方が対応していたので、これを選んで使っている。これにしなければというようなものでもない。
クライアント側の設定(.ovpn ファイル)
client
dev tap0
proto tcp-client
remote XXX.XXX.XXX.XXX 51194
resolv-retry infinite
nobind
remote-cert-tls server
key-direction 1
compress lz4-v2
verb 5
<ca>
-----BEGIN CERTIFICATE-----
(略)
-----END CERTIFICATE-----
</ca>
<cert>
Certificate:
    Data:
(略)
    Signature Algorithm: sha256WithRSAEncryption
(略)
    Signature Algorithm: sha256WithRSAEncryption
(略)
-----BEGIN CERTIFICATE-----
(略)
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
(略)
-----END PRIVATE KEY-----
</key>
<tls-crypt>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
(略)
-----END OpenVPN Static key V1-----
</tls-crypt>
  • remote にはサーバーの WAN 側のグローバルアドレスを使う。
  • サーバー側の proto 'tcp-server' と、クライアント側の proto tcp-client が対になっている点に留意。udp の場合はサーバー・クライアントとも 'udp' になる。
  • ca と tls-crypt はサーバー・クライアント共通だが、cert + key のセットはサーバーとクライアントのそれぞれのために作成して存在するものなので、一つの同じセットを使い回さないように注意すること。PKI の作業において、openvpn-server と、openvpn-client の 2 通りを作成するのは、そのためである。ここでは /etc/easy-rsa/pki/issued/openvpn-client.crt と /etc/easy-rsa/pki/private/openvpn-client.key の内容を .ovpn にコピー&ペーストして使っている。また、ca と tls-crypt も、サーバーと同じものの内容を .ovpn にコピー&ペーストして使っている。
  • proto の場合同様に、サーバー側の remote_cert_tls 'client' と、クライアント側の remote-cert-tls server が対になっている。これはセキュリティ的なオマケの設定なので(接続失敗の原因ともなるので)、最初は使わない方が良い。

TUN サーバーとして動かす場合

難しかったり、とりあえず動かすことに成功しても、細かいオプションなどで疑問が湧き、それに関する情報が見付けにくいのが TUP サーバーとして設定する場合で、自分用にもこのメモを記しておきたいと思った主な動機にもなっている。

tun0 の準備

結果論からすると、OpenVPN 自体の設定は実は TAP と比べて大きな違いはない。重要なのは、この tun0 の準備の方である。こちらをちゃんとやらないと、いくら OpenVPN の設定を色々といじくり回して試行錯誤しても、意味がないのである。

TUN の場合、VPN は専用のネットワーク空間(ここでは 192.168.255.0/24)を持つことになるので、TAP と違って論理インターフェース「LAN」(ここでは 192.168.1.0/24)に直接参加させることはできず、まずは論理インターフェース「VPN」を別途用意して、そこに tun0 をアサインする形になる。この辺りが、OpenVPN 自体というよりは、OpenWrt すなわち Linux のネットワークインターフェースとかファイアーウォールの設定の話なので、そこが良くわかっていないと、ネットで OpenVPN として検索して得られる情報だけでは「???」なので、大変だった。

Protocol は「Unmanaged」で設定すること。OpenVPN の server オプションで 192.168.255.0/24 を割り当てるからといって、論理インターフェース「VPN」側でも同じアドレスを律儀に設定してしまうと、競合が起こるのか、却って正常に動かなくなってしまう。次に TAP の場合と同じように Physical Interface として「tun0」をアサインする(存在しなければ Custom Interface で「tun0」とすればよい)。さらに、この新生した論理インターフェース「VPN」を、LAN と同じ Firewall ゾーン「LAN」に所属させる。これで準備 ok なはずである。

※インターフェースに変更を行うと LuCI の Save & Apply では不十分で、インターフェース自体を再起動する必要がある。

サーバー側の設定(/etc/config/openvpn)
config openvpn 'router_server'
 option enabled '1'
 option dev 'tun0'
 option port '1194'
 option proto 'tcp-server'
 option compress 'lz4-v2'
 option dh        '/etc/easy-rsa/pki/dh.pem'
 option ca        '/etc/easy-rsa/pki/ca.crt'
 option cert      '/etc/easy-rsa/pki/issued/openvpn-server.crt'
 option key       '/etc/easy-rsa/pki/private/openvpn-server.key'
 option tls_crypt '/etc/openvpn/tslcrypt.key'
 option remote_cert_tls 'client'
 option key_direction '0'
 option keepalive '10 120'
 option persist_tun '1'
 option persist_key '1'
 option user 'nobody'
 option group 'nogroup'
 option verb '5'
 option server '192.168.255.0 255.255.255.0'
 option topology 'subnet'
 list push 'redirect-gateway def1'
 list push 'dhcp-option DOMAIN myhomelan'
 list push 'dhcp-option DNS 192.168.1.1'
 list push 'persist-tun'
 list push 'persist-key'

TAP の場合の server-bridge に対して、TUN では server と topology のセットになっている部分が異なっており、あとは(当然ながら)dev と port のみ異なっている。

  • server-bridge の場合と違い、ルーター自身のネットワークアドレスを指定したりはしない。server オプションでは単に、VPN クライアント用に割り当てる専用のネットワーク空間(ここでは 192.168.255.0/24)を示すのみである。ゲートウェイを示す必要がないのは、自動的にそのネットワークの .1 が OpenVPN サーバーに割り当てられ、それをゲートウェイとしてクライアントが使用するように OpenVPN サーバーが処理するので、設定段階で設定する余地を与えるまでもないというだけの理由である。
  • topology を 'subnet' にセットし忘れると、互換性維持のための古いデフォルト設定(非推奨)の 'net30' が使われてしまい、意味不明の挙動に悩まされることになる。server 設定に必須のオプション。
クライアント側の設定(.ovpn ファイル)
client
dev tun0
proto tcp-client
remote XXX.XXX.XXX.XXX 1194
resolv-retry infinite
nobind
remote-cert-tls server
key-direction 1
compress lz4-v2
verb 5
<ca>
-----BEGIN CERTIFICATE-----
(略)
-----END CERTIFICATE-----
</ca>
<cert>
Certificate:
    Data:
(略)
    Signature Algorithm: sha256WithRSAEncryption
(略)
    Signature Algorithm: sha256WithRSAEncryption
(略)
-----BEGIN CERTIFICATE-----
(略)
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
(略)
-----END PRIVATE KEY-----
</key>
<tls-crypt>
#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
(略)
-----END OpenVPN Static key V1-----
</tls-crypt>

.ovpn については remote の port と dev の部分のみで、他は TAP の場合と違いはない。


作業上の注意点

  • ifconfig(macOS)で自分の VPN で割り当てられたアドレスをチェックする。
  • VPN の詳細ログを見て(Tunnelblick)、エラーの原因を細かく特定できるようにする。
  • nc -v [IP] [port] でネットワークインターフェースやファイアーウォールが適切に設定されてちゃんと接続自体ができているかチェックする。
  • scp [local path] root@[address]:/etc/config/openvpn (先に SSH を設定しておく) で設定ファイルをプッシュ更新する。LuCI(luci-app-openvpn)では対応していないオプションもあったりするので、scp で設定ファイルをプッシュして、ssh でログインし、service openvpn reload する(そして ssh を exit する)、それからクライアント側で OpenVPN を接続し直す、という繰り返しで設定を試した。
  • たとえ新しく仮想の論理インターフェースを追加するような場合であっても、インターフェース設定を変更する場合は要注意(カーネルモジュールドライバーが絡んでいるせいか?)。最悪、ネットが不通になる。設定をいじる直前にバックアップは必須。LuCI の Save & Apply では正常に変更が反映されず、Reboot が必要となることが多い。また、LuCI からソフトウェア的に Reboot しても Wi-Fi(のチップ)がちゃんと再起動されず、ハードウェアボタンを使って一旦完全に電源を切ってから、改めて起動し直す必要が生じたことすらあった。なのでインターフェース絡みの設定を変更する場合には、VPN を通じて遠隔から作業しようとしない方が良い(いつでもハードウェア的な電源のオフ・オンが可能なようにしておくのが安全)。
  • OpenVPN 2.4 の公式マニュアル

補遺:プロトコルの UDP 化

通常は UDP で運用されると考えられているのが OpenVPN だが、DIY で OpenWrt を扱うような一般人の場合は、まずは TCP をデフォルトに考えて OpenVPN の安定環境を確立することに専念した方がいい。そう考えて、上までの内容はすべて proto 'tcp-server'(クライアント側は proto tcp-client) での設定となっている。

しかしパフォーマンス面の有利性からすると、一旦 TCP での安定運用が確実となったならば、是非 UDP に切り替えておきたいところである。他に何の障害要因もなくなった状態で、初めて UDP 周りのオプションだけいじるのであれば、問題の特定は容易であり、精神生理的にリスクを最小限に抑えられる。

UDP の場合、MTU、MSS、フラグメントサイズのオプションを適切に設定しないと、デフォルトや自動算出に任せると問題が発生することがある。とはいえ、逆に言えば、MTU、MSS、フラグメントサイズの意味することをわかっていた上で、適切に設定していれば、問題なく UDP を使うことができるということになるわけである。

実は、今回、ちゃんと MTU サイズを ping コマンドで計測して最適な値を決めようとして、判明した事実なのだが、自分のサーバーのネットワーク環境(au ひかりのホームゲートウェイ)が WAN 側からの ping に応答しない(icmp を DROP する)仕様のため、OpenVPN の自動算出機能も機能しない、最悪のシチュエーションにバッチリ当て嵌っていたのである。それで昔から「TCP でないと安定運用できないじゃないか!」ということが、自分的には常識化していたわけである。──そんなこと(au ひかりの仕様と、OpenVPN が ping で自動算出しようとすること)を元から知っていたのなら、もちろん大した問題ではないわけだが、そんな知識だってやっていくうちにおいおい付くものだし、最初から何もかも情報が揃うわけでもない。なので、UDP よりもまずは TCP だろうというわけだ。

また、さらに状況が悪いことに、今、クライアント側の環境としては、楽天 UN-LIMIT を利用している。これが、調べてみると、MTU: 1340(MSS: 1300)という、かなり小さめの値で、OpenVPN のデフォルト値 MTU: 1500(MSS のデフォルト値は 1450)を下回っており、確実に断片化してしまう。つまりこのままでは、たとえ UDP にせず TCP のままでも効率的には悪い状況になっていたことになる。

ともかく、以上まで、色々と調べてやっと全貌が把握できたので、これで UDP 化を安全にできる状況になり、以下のような設定に至った(TCP と違っている部分についてのみ示す。他は同じ。TAP 用か TUP 用かということについてはサーバー・クライアントいずれの側でも設定に違いはない):

サーバー側の設定(/etc/config/openvpn)
 option proto 'udp'
 option fragment '1460'
 option mssfix '1460'
クライアント側の設定(.ovpn ファイル)
proto udp
fragment 1300
mssfix 1300

※ mssfix を指定する場合、tun-mtu は指定せず(デフォルトの 1500 のまま)放置する(mssfix と fragment で直接制御するので、tun-mtu に頼らないのが正しい使い方ということのようだ)。

特に問題なく快調に VPN 通信できているように思う。例えば、TCP の時は FTP で NAS からファイルをダウンロードしながら、待つ間にヤフーニュースを見たりしていると、レスポンス・読み込み共にかなり重かった。UDP に変えると、そこがある程度改善したように思われる。

※ 楽天 UN-LIMIT のテザリング以外に、例えば公衆 Wi-Fi の tullys_Wi-Fi を使う場合は、MTU: 1118(MSS: 1078)なので fragment と mssfix を 1078 にして使えばよい……はずなのだが、公衆 Wi-Fi のせいか UDP ではどうも不安定で fragment/mssfix の設定では理論通り行かない(TLS ハンドシェイクの段階で失敗している)。こういう場合は無理に UDP にせず、元の TCP 設定で繋ぐことにした方が良い。

※ この後に続けて WireGuard もセットアップしたが、OpenVPN よりもずっと良い(決して OpenVPN が悪いというわけではないが、WireGuard が良すぎる)。どちらか迷っているのであれば、まずは WireGuard を先に試すことを推奨。

(以上)

コメント

このブログの人気の投稿

EP-805A 廃インク吸収パッド交換

WZR-HP-AG300H with OpenWrt

この Apple ID は、App Store で使用されたことがありません