OpenWrt の OpenVPN 基本ガイドの徹底解析

OpenWrt 公式の OpenVPN の基本ガイド(👉 OpenVPN Basic)について。(日本人にとっては英語情報であるということ以前に、たとえネイティブであってとしても)ただ単に設定方法のシェルコマンドが掲載してあるだけで、ほとんど説明がされていないので、今回はかなり細かく解析して検証してみたいと思う。

ソフトウェアのインストール


opkg update
opkg install openvpn-openssl openvpn-easy-rsa

これは一目瞭然だと思うが、openvpn-openssl と openvpn-easy-rsa パッケージをインストールしているだけ。LuCI からアプリをインストールするのと何ら違いはない。

openvpn-openssl が OpenVPN サーバー本体。openvpn-easy-rsa は証明書と鍵を生成するための認証局用のプログラム(easyrsa コマンドを使えるようにする)。この OpenWrt ルーターで証明書・鍵を直接発行せずに、他の PC 等で別途用意した証明書・鍵をルーターに持ってきて使用する場合には、openvpn-easy-rsa の方のインストールは必ずしも必要ではないのかもしれない。

証明書・鍵の発行

これだけが openvpn-easy-rsa に関する作業で、OpenVPN 本体とは直接関係がない、下準備に関する話ので、先に説明を済ませておくことにする。


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

各コマンドの意義は原文のコメントにある通り:

  1. Remove and re-initialize the PKI directory
  2. Generate DH parameters
  3. Create a new CA
  4. Generate a keypair and sign locally for a server
  5. Generate a keypair and sign locally for a client
  6. Generate TLS PSK

最後の openvpn コマンドで指定する tc.pem 用のパスは openvpn-easy-rsa の使用する easyrsa-pki 用のパスを指定している。もちろん、openvpn コマンド自体は、openvpn-openssl パッケージでインストールされるものだろうから、openvpn-easy-rsa のインストールだけでは最後の openvpn コマンドは実行できないだろう。

ともかく、以上の作業で、OpenVPN サーバーの運用に必要な証明書・鍵を用意できることになる。

定数

元のシェルコマンド例に戻って、定数を設定している部分を検証する。

※基本的にこれらはシェルコマンドの定数(ログアウトしたらリセットされる一時的なもの)を設定しているだけなので、OpenVPN サーバーに直接何かを働き掛けているものではない点に留意。


OVPN_DIR="/etc/openvpn"
OVPN_PKI="/etc/easy-rsa/pki"
OVPN_DEV="tun0"
OVPN_PORT="1194"
OVPN_PROTO="udp"
OVPN_POOL="192.168.8.0 255.255.255.0"
OVPN_DNS="${OVPN_POOL%.* *}.1"
OVPN_DOMAIN="$(uci get dhcp.@dnsmasq[0].domain)"
OVPN_DIR
後にサーバー用とクライアント用の OpenVPN で使うための .conf ファイルを出力するためのパスを指定しているだけで、特別な意味はない。そもそも .conf ファイルは手動でも記述できるものであり、この基本ガイドではそれをシェルスクリプトでテンプレート化して出力しているだけであり、openvpn コマンドとは無関係の作業である。無視しても何も困らない。
OVPN_PKI
証明書・鍵のパスであり、認証局側と OpenVPN サーバー側で共通して利用するものなので、デフォルトに従っておくのが無難だろう。
OVPN_DEV
ここでは tun(ルーターモード)を使っているが、tap(ブリッジモード)なら tap0 となる。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。
OVPN_PORT
ポート番号。1194 が標準。自分の場合、tun0 と tap0 のデュアルで運用しているので、tap0 の方には 1195 を指定している。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。
OVPN_PROTO
プロトコル種別。tcp か udp を選ぶことができる。当初は tcp を選び、後で mtu を適切に調整してから udp にした方が良い。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。
OVPN_POOL
この基本ガイドのシェルコマンド例では、tup(ルーターモード)で設定をしているので、(接続先 LAN とは別の)VPN 専用のネットワークアドレスを用意し、IP マスカレードで接続先 LAN にアクセスすることを前提としている。──のだが、基本ガイドではそれを何一つ述べていないので、こうしてこのシェルコマンド例の全体を解析してみて始めて明らかにできたことである。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。
OVPN_DNS
上の OVPN_POOL を使って、単に、XXX.XXX.XXX.1 を DHCP で通知される DNS サーバーアドレスとして生成しているだけ。しかし、XXX.XXX.XXX.1 が、DNS サーバーでない場合、正常に機能しない。自分の場合は、VPN 側の XXX.XXX.XXX.1 ではなく、接続先 LAN の YYY.YYY.YYY.1 を DNS サーバーとして指定すると意図通りに動作するようになった。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。
OVPN_DOMAIN
このコマンドでは、ルーターが所属している LAN のローカルドメイン名を uci コマンドを使って設定から読み取っているようである。DHCP で通知される接続先 LAN のローカルドメイン名(ドメイン名を省略してホスト名だけを指定した場合に補うために使われることになる)を設定するためだけに使われる。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。

. /lib/functions/network.sh
network_flush_cache
network_find_wan NET_IF
network_get_ipaddr OVPN_SERV "${NET_IF}"

OVPN_FQDN="$(uci -q get "$(uci -q show ddns \
| sed -n -e "/\.enabled='1'$/s//.lookup_host/p" \
| sed -n -e "1p")")"
if [ -n "${OVPN_FQDN}" ]
then
OVPN_SERV="${OVPN_FQDN}"
fi

これだけ大袈裟(?)なコマンドを連ねて、行っていることはただ一つ、定数 OVPN_SERV を決定しているだけ。WAN 側のグローバルアドレスを探っているだけで、当の OpenWrt サーバーをセットアップした人なら、こんなことをコマンドを通じてやらずとも、自分で設定した値なのだから把握しているはず。外部からアクセスする場合のアドレスであり、あくまでも OpenVPN クライアント側が設定値として使うべきグローバルアドレスなので、二重ルーター状態になっていたりすると、このコマンドでやろうとすると一重目のプライベートアドレスが使われたりして、むしろ不適切なアドレスになるかもしれない。これも .conf ファイルのテンプレート出力に使っているだけで、直接 OpenVPN サーバーに対して何か働きかけているわけではない。全く手動で代用可。

ファイアーウォールの設定


uci rename firewall.@zone[0]="lan"
uci rename firewall.@zone[1]="wan"
uci rename firewall.@forwarding[0]="lan_wan"
uci del_list firewall.lan.device="${OVPN_DEV}"
uci add_list firewall.lan.device="${OVPN_DEV}"
uci -q delete firewall.ovpn
uci set firewall.ovpn="rule"
uci set firewall.ovpn.name="Allow-OpenVPN"
uci set firewall.ovpn.src="wan"
uci set firewall.ovpn.dest_port="${OVPN_PORT}"
uci set firewall.ovpn.proto="${OVPN_PROTO}"
uci set firewall.ovpn.target="ACCEPT"
uci commit firewall
/etc/init.d/firewall restart

基本的には WAN 側からの VPN 用のポート番号に対する Traffic Rule を追加している。その過程で Zone 名を変えてしまったりしているので(汗)、何も考えないでコマンドをそのまま使ってしまうと、(当人にとっての)想定外の事態にならないとも限らない。

ここも結局は「WAN 側からの VPN 用のポート番号(+プロトコロル種別)に対する Traffic Rule を(該当の WAN 側インターフェイスに対して)追加」以上の意義は何もないので、直接的に何ら OpenVPN サーバーに働きかけているわけではなく、手動でファイアーウォール設定を LuCI で行った方が話が簡単に済む。

.conf ファイルの作成


OVPN_DH="$(cat ${OVPN_PKI}/dh.pem)"
OVPN_TC="$(sed -e "/^#/d;/^\w/N;s/\n//" ${OVPN_PKI}/tc.pem)"
OVPN_CA="$(openssl x509 -in ${OVPN_PKI}/ca.crt)"

/etc/easy-rsa/pki にある dh.pem, tc.pem, ca.crt(テキストファイル)の内容を一部加工して読み取っている。が、特にこの加工が必須というわけでもなさそうで、自分の場合はこのようなことをせずに直接これらのファイルの内容を使っていたりするが、特に問題なく OpenVPN が動いているので、加工はほとんど基本ガイドの作者の趣味の問題ではないかと思われるが……。


umask go=
ls ${OVPN_PKI}/issued | sed -e "s/\.\w*$//" | while read -r OVPN_ID
do
(...)
done

/etc/easy-rsa/pki/issued にあるファイル名を収集し(vpnserver.crt, vpnclient.crt の 2 つ)、拡張子を除いた vpnserver と vpnclient の 2 種類のそれぞれを OVPN_ID として、do 〜 done ブロックで繰り返し処理をしている。

do 〜 done ブロックの内容
do
OVPN_KEY="$(cat ${OVPN_PKI}/private/${OVPN_ID}.key)"
OVPN_CERT="$(openssl x509 -in ${OVPN_PKI}/issued/${OVPN_ID}.crt)"
OVPN_CERT_EXT="$(openssl x509 -in ${OVPN_PKI}/issued/${OVPN_ID}.crt -purpose)"
OVPN_CONF_SERVER="\
user nobody
group nogroup
dev ${OVPN_DEV}
port ${OVPN_PORT}
proto ${OVPN_PROTO}
server ${OVPN_POOL}
topology subnet
client-to-client
keepalive 10 60
persist-tun
persist-key
push \"dhcp-option DNS ${OVPN_DNS}\"
push \"dhcp-option DOMAIN ${OVPN_DOMAIN}\"
push \"redirect-gateway def1\"
push \"persist-tun\"
push \"persist-key\"
<dh>\n${OVPN_DH}\n</dh>"
OVPN_CONF_CLIENT="\
dev ${OVPN_DEV%%[0-9]*}
nobind
client
remote ${OVPN_SERV} ${OVPN_PORT} ${OVPN_PROTO}
auth-nocache
remote-cert-tls server"
OVPN_CONF_COMMON="\
<tls-crypt>\n${OVPN_TC}\n</tls-crypt>
<key>\n${OVPN_KEY}\n</key>
<cert>\n${OVPN_CERT}\n</cert>
<ca>\n${OVPN_CA}\n</ca>"
case ${OVPN_CERT_EXT} in
(*"SSL server : Yes"*) cat << EOF > ${OVPN_DIR}/${OVPN_ID}.conf ;;
${OVPN_CONF_SERVER}
${OVPN_CONF_COMMON}
EOF
(*"SSL client : Yes"*) cat << EOF > ${OVPN_DIR}/${OVPN_ID}.ovpn ;;
${OVPN_CONF_CLIENT}
${OVPN_CONF_COMMON}
EOF
esac
done

要するにここでは、サーバー用とクライアント用に分けて設定ファイルの記述内容を /etc/openvpn に vpnserver.conf と vpnclient.ovpn として出力している。このそれぞれの設定ファイルにしても、直接、手動でファイルを記述したり、LuCI の GUI で設定すれば同じことだし、このように一見複雑なシェルコマンドで自動化する必要性はほとんどないと思う。サーバー用・クライアント用で共通部分の <tls-crypt> で使用されている tc.pem、<key> で使用されている *.key、<cert> で使用されている *.crt、<ca> で使用されている ca.crt など、いずれもコマンドを使って取得していて、そのままの形ではないが、特にそのままの形で使った場合との違いはないように思われる。

/etc/config/openvpn を直接設定した方が話が早い

結局、上までの結果、vpnserver.conf と vpnclient.ovpn という 2 種の設定ファイルが作成されるだけである。ところが、この設定ファイルを作成したからといって、それだけでそれが OpenWrt の OpenVPN サーバーで使われるわけでもないようだ。実際に OpenWrt の OpenVPN サーバーが使うのは、/etc/config/openvpn である。vpnserver.conf を使うためには、/etc/config/openvpn の中で

config openvpn 'vpnserver'
    option enabled '1'
    option config /etc/openvpn/vpnserver.conf

というような風に記述する必要があるだろう。

一方、LuCI の拡張 GUI(luci-app-openvpn)で設定すると、/etc/config/openvpn の方を直接操作することができる。また、OpenVPN の他の文書 👉「Routing Example: OpenVPN」でも、直接 /etc/config/openvpn を操作していて、この基本ガイド(👉 OpenVPN Basic)のような複雑怪奇なシェルコマンドによる設定ファイルの作成は行っていない。

要するに、この基本ガイドは参照せず、「Routing Example: OpenVPN」を参考にした方がいいものと思われる。

──結局自分で「OpenWrt の OpenVPN(サーバー)についての包括的メモ」を作成した。

コメント

このブログの人気の投稿

清水俊史『上座部仏教における聖典論の研究』

シークエンスパパとも 本物の霊能力

シークエンスパパともの先見の明