OpenVPN で L2VPN を作成する

自宅内のVPN Gatewayに、OpenVPNとブリッジインターフェイスを使って、レイヤー2 VPNを作成する。自宅外からVPN接続したとき、bonjourやWindowsネットワーク検索のように、同一ネットワークセグメントで利用可能なサービスが自宅内と同様に使えるのがメリット。

サーバ: Raspberry Pi 2 (Raspbian 9.0 stretch)
環境は以下の図の通り。

この文書の手順以外に別途、外部接続ルータでサービスポートを外部公開しておく必要がある。

Apple iOS系デバイスはtapに対応していないため、Layer2 VPNを利用できないので注意。Android系もroot権限なしの標準APIとそれを利用する標準クライアントではtapに対応していない(有料ソフトウェアのOpenVPN Clientはtapに対応している)。

1. OpenVPN パッケージインストール

$ sudo apt install openvpn bridge-utils

2. ネットワークインターフェイスをbridge設定にする

eth0デバイスを直接使わず、ブリッジデバイスbr0を使ってeth0をそのメンバーとするように設定する。IPアドレスはDHCP範囲外の固定IPアドレスを利用することとする。

interfacesファイルを編集する。

$ sudo vi /etc/network/interfaces

auto br0
iface br0 inet static
        address 192.168.100.20
        netmask 255.255.255.0
        network 192.168.100.0
        broadcast 192.168.100.255
        gateway 192.168.100.1
        dns-nameservers 192.168.100.1
        dns-search home.private
        bridge_ports eth0

もともとeth0だったところをbr0にして、bridge_ports eth0 を追加するように編集する。

ここで一度再起動して、br0にIPアドレスが付くことを確認する。
$ sudo shutdown -r now

3. サーバ設定

OpenVPN サーバの設定ファイルを作成する。

$ sudo mkdir -p /var/log/openvpn /etc/openvpn/ccd
$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/server/
$ sudo gzip -d /etc/openvpn/server/server.conf.gz
$ sudo mv /etc/openvpn/server/{server,udp7231}.conf
$ sudo vi /etc/openvpn/server/udp7231.conf

port 7231
proto udp
dev tap
ca ca.crt
cert server.crt
key server.key
dh dh2048.pem
client-config-dir ccd
server-bridge 192.168.100.0 255.255.255.0 192.168.100.192 192.168.100.223
push "dhcp-option DNS 192.168.100.1"
push "dhcp-option DOMAIN-SEARCH home.private"
client-to-client
keepalive 10 120
tls-auth ta.key 0
cipher AES-256-CBC
comp-lzo
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append  /var/log/openvpn/openvpn.log
verb 3

トランスポート層プロトコルは UDP を利用する(TCP over TCP問題を避けるため)。利用するポートは念のため標準の 1194 から変え、適当にランダムで決める。今回は 7231 とした。

4. サーバ証明書の準備

easy-rsaパッケージを利用してCAを作成し、サーバ証明書・鍵を作成する。CA 作業用ディレクトリとして、/etc/openvpn/easy-rsa を作成する。

$ sudo make-cadir /etc/openvpn/easy-rsa
$ sudo -i
# cd /etc/openvpn/easy-rsa
# ln -s openssl-1.0.0.cnf openssl.cnf
# vi vars

以下のような設定をvarファイルに記述する。

export KEY_SIZE=2048
export KEY_COUNTRY="JP"
export KEY_PROVINCE="Aichi"
export KEY_CITY="Nagoya"
export KEY_ORG="Home"
export KEY_EMAIL="root@raspberrypi.home.private"
export KEY_OU="Server"

CAを作成し、サーバ鍵・証明書を作成する。

# . vars
# ./clean-all
# ./build-ca
# ./build-key-server server

TLS Static Key と DH Keyを作成する。DH Keyの生成にはかなりの時間がかかる(Raspberry Piでは10分以上)

# openvpn --genkey --secret /etc/openvpn/server/ta.key
# ./build-dh
# cp -p keys/ca.crt keys/server.crt keys/server.key keys/dh2048.pem /etc/openvpn/server/
# exit

5. OpenVPNサービスの有効化と起動

サーバ側 OpenVPN サービス用の systemd ユニットファイルは /lib/systemd/system/openvpn-server@.service である。@の後にサーバ側 conf ファイルのドットより前の名前(今回の場合は “udp7231″)を指定して有効化・起動する。

サービスを有効化し、起動する。
$ sudo systemctl enable openvpn-server@udp7231
Created symlink /etc/systemd/system/multi-user.target.wants/openvpn-server@udp7231.service → /lib/systemd/system/openvpn-server@.service.
$ sudo systemctl start openvpn-server@udp7231

これだけだと、ブリッジデバイスに tap0 デバイスが組み込まれず、通信できない。/etc/systemd 以下に追加の systemd ユニットファイルを作成し、tap0 デバイスをブリッジに追加する。

$ sudo vi /etc/systemd/system/openvpn-bridge.service

[Unit]
Description=OpenVPN bridge service
Requires=openvpn-server@udp7231.service
After=openvpn-server@udp7231.service

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c '/sbin/brctl addbr br0 || /bin/true'
ExecStartPost=/bin/sh -c '/sbin/brctl addif br0 tap0 || /bin/true'
ExecStartPost=/sbin/ip link set tap0 up
ExecReload=/bin/true
ExecStop=/sbin/brctl delif br0 tap0

[Install]
WantedBy=multi-user.target

brctl addbr br0 の行は本来必要ないはず(br0 の立ち上げは /etc/network/interfaces に書いてあれば自動的に実行される)なのだが、タイミングの問題で openvpn サービス起動時にまだ br0 が無い場合があり、次の行の brctl addif br0 tap0 に失敗することがあるため、念のため加えている。

ブリッジ用自作サービスを有効化し、起動する。
$ sudo systemctl enable openvpn-bridge
Created symlink /etc/systemd/system/multi-user.target.wants/openvpn-bridge.service → /etc/systemd/system/openvpn-bridge.service.
$ sudo systemctl start openvpn-bridge

6. iptables設定

OpenVPNサービスポートの、ローカルファイアウォール設定を開けておく。

iptablesの設定にiptables-persistentパッケージを利用している場合は、/etc/iptables/rules.v4を編集して以下の行を加える。

-A INPUT -p udp -m udp --dport 7231 -j ACCEPT

設定を再読み込みする。
$ sudo netfilter-persistent reload

もしiptables直接ではなくufwを使っているのであれば、以下のコマンドでサービスポートを開ける。
$ sudo ufw allow 7231/udp
$ sudo ufw reload

ブリッジ設定の場合、パケットフォワーディングの設定(sysctl -w net.ipv4.ip_forward=1)は不要。

7. クライアント側設定テンプレートの作成

設定を複数のクライアントに配布するため、元となるテンプレートファイルを作成する。

$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/client/client.template
$ sudo vi /etc/openvpn/client/client.template

client
dev tap
proto udp
remote 203.0.113.10 7231
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
key-direction 1
cipher AES-256-CBC
comp-lzo
<tls-auth>
ここに/etc/openvpn/server/ta.keyファイルの内容をペーストする。
</tls-auth>
<ca>
ここに/etc/openvpn/server/ca.crtファイルの内容をペーストする。
</ca>
<key>
</key>
<cert>
</cert>

証明書・鍵ファイル等を別ファイルの形で配布しても良いが、設定ファイルの中に取り込む形にしてみる。設定を単一ファイルで配布できれば取り回しが良いため。

8. クライアント証明書の発行と設定ファイルの作成

まず1番目のクライアント用の鍵・証明書を発行する。

$ sudo -i
# cd /etc/openvpn/easy-rsa
# . vars
# ./build-key client1
# exit

クライアント1のためのファイル (client1.ovpn) をテンプレートから作成する。

$ sudo cp /etc/openvpn/client/{client.template,client1.ovpn}
$ sudo vi /etc/openvpn/client/client1.ovpn

client1.ovpnファイルの中の<cert>, <key>各セクションの中に、作成した証明書(client1.crt)と鍵(client1.key)ファイルの内容をコピー&ペーストする。

作成した/etc/openvpn/client/client1.ovpnファイルを配布して、クライアントデバイスにインストールして接続確認する。

接続成功するようなら、client2、client3、…と設定ファイルを同様に作成していく。