Ubuntu Weekly Recipe

第329回VPSとLXCとOpenVPNで仮想プライベートネットワークを構築する

今回はUbuntuの具体的な利用方法として、VPS、LXC、OpenVPNなどを利用して、Android端末からローカルにあるUbuntuマシンのコンテンツをインターネット越しに閲覧する方法を紹介します。

まずは目的を決める

写真や動画のマルチメディアコンテンツの保存について頭を悩ませている人は多いでしょう。デジタルカメラやスマートフォンといった簡単に撮影できる機材が手元にあるおかげで、写真や動画が際限なく増えていくために整理が大変ですし、単体のファイルサイズも大きいのでクラウドストレージはすぐにあふれてしまいます。自分で撮影したデータは世界に1つだけなので、おいそれとは捨てられません。

家にあるマシンに保存する場合、今度は閲覧が問題になってきます。できれば家の外からでも閲覧したい、そうすると家のマシンを常時起動しながらサーバーをインターネットに公開する必要がある、など考えるべきことが増えてきます。

そこで今回はご家庭にあるメディアライブラリーを、インターネット越しにAndroidを使って閲覧することを目的に、その手段を考えてみようと思います[1]⁠。

VPSを経由する

ご家庭のメディアライブラリーをインターネットに公開する一番シンプルな方法は、ライブラリーをサーバーとして直接インターネットに繋ぐ方法です。

この場合、次のような点を考慮する必要があります。

  • 外部からアクセスできるようにルーターを適切に設定する
  • サーバーのIPアドレスを、クライアントが知る術を用意する
  • DLNAなどルーター越しには使えないサービスについては代替手段を用意する

この中で一番やっかいなのはルーターの設定でしょう。ルーターごとに手順が異なるうえに、プロバイダーの契約内容によっては、できることが限定されるケースもよくあります。

VPSを1台用意し、インターネット側のアクセスを待ち受けるのは、すべてVPS上に存在するサービスになるよう心がければ、ルーターの設定はほぼ変更しなくてすみます。

2番目のIPアドレスについては、VPSなら最初からグローバルな固定IPアドレスが割り振られているので、対応不要になるでしょう。ちなみに3番目については、VPSにするだけでは状況は変わりません。

VPSを使う場合、毎月1,000円前後の料金がかかってしまうというデメリットは存在します。しかしご家庭で常時PCを起動する時の電気代に比べるとそこまで高い料金ではありません[2]⁠。さらに最近のVPSなら100Gバイト程度のディスク領域もついてくるので、自前でサービスをインターネットに公開することを考えているのであれば、クラウドサービスの有料枠を購入するよりはお手軽でしょう[3]⁠。

よって今回はクライアントがインターネットから接続するサーバーは一度VPSを経由するような設定を紹介します。ご家庭内のサービスを外向けに公開する場合は、適宜VPSの部分をご家庭のサーバーに読み替えて設定してください。

接続方式としてOpenVPNを使う

家庭内LANで公開されているホームサーバーをVPS経由でインターネットからアクセスする方法はいくつか存在します。たとえばVPSにプロキシーを設定する方法や、VPNやSSHを使ってトンネルを作成する方法、などです。

今回はその中でもOpenVPNを利用します。OpenVPNはさまざまなプラットフォームに対応した、仮想プライベートネットワークを構築するソフトウェアです。特定のプロトコルのみをトンネルしたいとは限らないこと、Android側の設定ができるだけ簡単にしたいこと、パッケージマネージャーからすぐにインストールできることなどがOpenVPNを使用するおもな理由です。

VPS上にOpenVPNサーバーを構築し、ホームサーバーやAndroid端末をOpenVPNクライアントとして設定します。OpenVPNサーバーはアクセスしてきたOpenVPNクライアント同士をLAN内で接続したように見せ、さらにOpenVPNを経由するパケットはOpenSSLを使って暗号化されます。

今回はルーティングモード(tunデバイスを使う接続方法)のみを紹介します。メディアサーバーとしてDLNAを使う場合は、ブリッジモード(tapデバイスを使う接続方法)の方が望ましいのですが、Android側でブリッジモードを使うためにはrootを取る必要があるため除外しました。DLNAを使いたいのであれば、L2TP/IPsecを使えるソフトウェア、たとえばSoftEther VPNを使うと良いでしょう。

メディアリポジトリとしてWebDAVを使う

メディアサーバーといえば前節でも述べたとおりまずDLNAが思いつきます。DLNAは配信機器を検知するためにSSDPのブロードキャストパケットを送信するのですが、ルーティングモードのOpenVPNはこのパケットを配送してくれませんので、そのままではDLNAサーバーを利用できません。

今回はよりお手軽に、WebDAVでメディアリポジトリを公開して、AndroidのESファイルエクスプローラーでアクセスする形を採用します。WebDAVはWebサーバー上でリソースの移動やコピー、属性管理を行えるようにした、HTTPの拡張です。オンラインストレージサーバーとして人気のownCloudも、WebDAVのインターフェースとして実装されています。よって今回の手順はそのままownCloudにも適用できるはずです。ちなみにWebDAVサーバーとして、今回はnginxを使用します。

ちなみに、WebDAVはLAN内部でファイルを公開するサーバーとして使っているだけなので、SambaやNFS、SFTPなどに置き換えることも可能です。Android側のアプリに合わせて、使いやすいものを使うと良いでしょう。

LXCを使う

ここまでで紹介したソフトウェアはすべてLXCのコンテナ上にインストールすることにします。VPS上に直接OpenVPNをインストールしてももちろん良いのですが、コンテナ上にインストールしておけば、OpenVPNを使わなくなったら、コンテナごと止めてしまえるというメリットがあります[4]⁠。

合わせてUbuntuでLXCを使う場合における、ネットワークやデバイス、AppArmorの設定についてもこのRecipeで確認していきましょう。

サーバー側の設定

最初にVPS側の設定を行います。VPSにはあらかじめUbuntu 14.04 LTSがインストールされているものとします。12.04でもそれほど変わるわけではありませんが、LXCの機能を考えるとカーネルはHWEカーネルにしてより新しくしておいたほうが良いでしょう。もし12.04でLXCをインストールしたい場合はリポジトリのそれではなく、1.0.xが提供されているUbuntu LXCチームのPPAを使用してください。

(Ubuntu 12.04 LTSのみ実行する)
$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:ubuntu-lxc/stable
$ sudo apt-get install linux-hwe-generic

LXCのインストール

VPS上に用意したUbuntuにLXCをインストールします。

$ sudo apt update
$ sudo apt install lxc

UbuntuのLXCのコンテナ(ゲスト)はdnsmasqによって、10.0.3.2から10.0.3.254までのIPv4アドレスが動的に割り当てられます。そのままですと、NATの設定を行うときに不便なので、アドレスレンジのうち前半をDHCPで使用しつつ、後半は固定IPアドレスとして利用できるようにしておきます。

$ sudo sed -i 's/LXC_DHCP_RANGE="10.0.3.2,10.0.3.254"/LXC_DHCP_RANGE="10.0.3.2,10.0.3.127"/' /etc/default/lxc-net
$ sudo sed -i 's/LXC_DHCP_MAX="253"/LXC_DHCP_MAX="126"/' /etc/default/lxc-net
$ sudo stop lxc
$ sudo start lxc

OpenVPN用のコンテナの作成

OpenVPN用のコンテナ(コンテナ名:openvpn)を作成します。

$ sudo lxc-create -t ubuntu-cloud -n openvpn -- --auth-key=/home/`id -un`/.ssh/authorized_keys

ubuntu-cloudは、クラウドインスタンス上で動作することを想定したイメージファイルです。ubuntuテンプレートがdebootstrapコマンドを使って一からルートファイルシステムを作るのに対して、ubuntu-cloudテンプレートはサーバーから既存のイメージをダウンロードして設定します。今回はubuntu-cloudテンプレートを使っていますが、ubuntuテンプレートでも後述のネットワーク設定方法以外は同じです。

「--auth-keys」オプションは、コンテナの初期ユーザー(ユーザー名:ubuntu、パスワード:ubuntu)の.ssh/authorized_keysに書き込む公開鍵情報を指定します。公開鍵や既存のauthorized_keysを指定可能です。ubuntu-cloudテンプレートのクラウドイメージはAmazon EC2の鍵情報やcloud-initで指定された鍵が最初から登録されることを前提に、sshd_configのPasswordAuthenticationをnoに設定しているためです。起動後にsshログインできるように、使用する鍵情報をこのオプションで渡しています[5]⁠。ubuntuテンプレートを使う場合は必要ありません。

各テンプレート固有のオプションはテンプレートに「--help」オプションを渡すことで確認できます。さらにubuntu-cloudテンプレートについては、hook側にもいくつかオプションがあるので、後者のコマンドのように参照すると良いでしょう。Amazon EC2のように、Cloud Init用のデータを渡すことも可能です。

$ lxc-create -t ubuntu-cloud --help
$ /usr/share/lxc/hooks/ubuntu-cloud-prep --help

次に、openvpnコンテナのIPv4アドレスを固定化します。ここでは、⁠10.0.3.200」を割り当てることにします。ファイルパス名に出てくる「openvpn」はコンテナ名です。名前の異なるコンテナを作成した場合は、適宜読み替えてください。

$ sudo editor /var/lib/lxc/openvpn/config
「# Network configuration」付近に次の2つのオプションを追加します。
    lxc.network.ipv4 = 10.0.3.200/24
    lxc.network.ipv4.gateway = 10.0.3.1

ubuntuテンプレートの場合
$ sudo editor /var/lib/lxc/openvpn/rootfs/etc/network/interfaces

ubuntu-cloudテンプレートの場合
$ sudo editor /var/lib/lxc/openvpn/rootfs/etc/network/interfaces.d/eth0.cfg

eth0をdhcpからmanualに変更し、DNS設定を追加します
    auto eth0
    iface eth0 inet manual
        dns-nameservers 10.0.3.1

interfacesファイルは、ubuntuテンプレートとubuntu-cloudテンプレートでファイル名が異なるので注意してください。内容はどちらも同じです。

openvpnコンテナを起動し、sshでアクセスします。ユーザー名、パスワード共に「ubuntu」です。

$ sudo lxc-start -n openvpn -d
$ sudo lxc-info -n openvpn
Name:           openvpn
State:          RUNNING
PID:            1817
IP:             10.0.3.200
CPU use:        74.27 seconds
BlkIO use:      329.48 MiB
Memory use:     206.20 MiB
KMem use:       0 bytes
Link:           vethLUALUW
 TX bytes:      61.14 MiB
 RX bytes:      61.61 MiB
 Total bytes:   122.75 MiB

$ ssh [email protected]

うまく動作するようなら、openvpnコンテナが自動起動するように設定しておきましょう。

$ sudo editor /var/lib/lxc/openvpn/config

以下の行を追加する
    # enable autostart
    lxc.start.auto = 1

$ sudo lxc-ls --fancy openvpn
NAME     STATE    IPV4        IPV6  AUTOSTART
---------------------------------------------
openvpn  RUNNING  10.0.3.200  -     YES

以降、openvpnコンテナ上での作業は、プロンプトを「openvpn$」と表示します。

OpenVPNとeasy-rsaのインストールとサーバー証明書の作成

次にOpenVPNと、PKI(公開鍵基盤)の構築に便利なeasy-rsaパッケージをopenvpnコンテナにインストールします。

openvpn$ sudo apt update
openvpn$ sudo apt install openvpn easy-rsa

ルーティングモードのOpenVPNはtunデバイスを使用しますので。デバイスノードを作成しておきます。

openvpn$ sudo mkdir /dev/net
openvpn$ sudo mknod /dev/net/tun c 10 200
openvpn$ sudo chmod 0666 /dev/net/tun

コンテナ上でデバイスノードを作成した場合、そのデバイスを使用できるようにcgroupに設定する必要があります。tunデバイス(メジャー番号10、マイナー番号200)であれば、以下のような設定を記述します。

lxc.cgroup.devices.allow = c 10:200 rwm

ただしubuntu、ubuntu-cloudテンプレートの場合、最初から/usr/share/lxc/config/ubuntu.common.confに上記が記述されているため、改めて設定する必要はありません。

次にUbuntuのドキュメントを参考に認証局の鍵と証明書を作成します。

openvpn$ sudo mkdir /etc/openvpn/easy-rsa/
openvpn$ sudo cp -r /usr/share/easy-rsa/* /etc/openvpn/easy-rsa/
openvpn$ sudo editor /etc/openvpn/easy-rsa/vars
以下の値を適宜書き換え、KEY_ALTNAMESを追加する
    export KEY_COUNTRY="US"
    export KEY_PROVINCE="CA"
    export KEY_CITY="SanFrancisco"
    export KEY_ORG="Fort-Funston"
    export KEY_EMAIL="[email protected]"
    export KEY_OU="MyOrganizationalUnit"

    export KEY_NAME="EasyRSA"
    export KEY_CN="CommonName"
    export KEY_ALTNAMES="EasyRSA"
openvpn$ sudo -s
openvpn# cd /etc/openvpn/easy-rsa/
openvpn# source vars
openvpn# ./clean-all
openvpn# ./build-ca
    すべてEnterを入力
openvpn# exit

varsの設定は、OpenVPN用のプライベートな認証局であることを念頭に置いて設定してください。これにより/etc/openvpn/easy-rsa/keys以下に、認証局の鍵(ca.key)や証明書(ca.crt)などが作成されます。

さらにOpenVPNサーバー用の証明書を作成し、先ほどの認証局の鍵で署名を行います。

openvpn$ sudo -s
openvpn# cd /etc/openvpn/easy-rsa/
openvpn# source vars
openvpn# ./build-key-server サーバー名
    基本的にEnterを押し、最後2つの署名を行うかどうかの確認でyと入力
openvpn# ./build-dh
openvpn# cp keys/サーバー名.crt keys/サーバー名.key keys/ca.crt keys/dh2048.pem /etc/openvpn/
openvpn# exit

最後から2つ目のコマンドで、作成した認証局の証明書とサーバー用の証明書と公開鍵をOpenVPNから見える場所にコピーしています。/etc/openvpn/easy-rsa/ディレクトリは、今後クライアント証明書の作成でも使用するのでそのままにしておいてください。

OpenVPNサーバーの設定

パッケージにある設定ファイルの雛形を元に、OpenVPNサーバーの設定を行います。

openvpn$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz /etc/openvpn/
openvpn$ sudo gzip -d /etc/openvpn/server.conf.gz
openvpn$ sudo editor /etc/openvpn/server.conf
    ca ca.crt
    cert サーバー名.crt
    key サーバー名.key
    dh dh2048.pem
    client-to-client

client-to-clientオプションは、OpenVPNクライアント間通信を行うためのオプションです。先頭にある「;」を削除して、このオプションを有効にしてください。

最後に、OpenVPNサーバーを起動します。

openvpn$ sudo service openvpn start
openvpn$ ip addr show dev tun0
2: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none
    inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0
       valid_lft forever preferred_lft forever

OpenVPNでは、10.8.0.0/24をアドレスVPN内のアドレスとして割り当てます。

正しく起動できているかどうかは、/var/log/syslogのログで確認できます。

Jun 13 23:30:25 openvpn ovpn-server[615]: Initialization Sequence Completed

ホスト側の設定

OpenVPNサーバーは、初期設定ではUDPの1194ポートで、クライアントからの接続を待ち受けます。よってLXCのコンテナでOpenVPNサーバーを動かすためには、ホストの1194/UDPに届いたパケットをopenvpnコンテナに転送する必要があります。

具体的にはホスト上で以下のルールをiptablesに追加します。

$ sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
$ sudo iptables -t nat -A PREROUTING -p udp --dport 1194 -j DNAT --to 10.0.3.200:1194

このルールをホスト起動時に適用するようにしても良いのですが、せっかくなのでopenvpnコンテナが起動したときのみ、このルールを適用するように設定してみましょう。LXCの設定ファイルには、コンテナのネットワークの作成と設定が済んだ後にホスト側で実行するスクリプトを指定できます。

まず次のような設定ファイルを、⁠/var/lib/lxc/openvpn/iptables.sh」という名前でホスト上に作成しましょう。openvpnはコンテナ名で、10.0.3.200はopenvpnコンテナのIPv4アドレスです[6]⁠。

#!/bin/sh

# for OpenVPN
case $3 in
up)
    iptables -w -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
    iptables -w -t nat -A PREROUTING -p udp --dport 1194 -j DNAT --to 10.0.3.200:1194
    ;;
down)
    iptables -w -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
    iptables -w -t nat -D PREROUTING -p udp --dport 1194 -j DNAT --to 10.0.3.200:1194
    ;;
esac

POSTROUTING向けのルールは、コンテナ内部のOpenVPNサーバーが外部のOpenVPNクライアントと通信するために必要です。PREROUTINGは、OpenVPNサーバーがクライアントからのアクセスを待ち受けるために必要です。

さらにスクリプトに実行権限を追加し、コンテナ起動時に呼び出せるように設定を追加します。

$ sudo chmod a+x /var/lib/lxc/openvpn/iptables.sh
$ sudo editor /var/lib/lxc/openvpn/config
以下の2行を追加する
    lxc.network.script.up = /var/lib/lxc/openvpn/iptables.sh
    lxc.network.script.down = /var/lib/lxc/openvpn/iptables.sh

これでopenvpnコンテナ起動時にルールが追加され、コンテナ終了時にルールが削除されます。試しに再起動してみましょう。

$ sudo lxc-stop -n openvpn
$ sudo lxc-start -n openvpn
$ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       udp  --  anywhere             anywhere             udp dpt:openvpn to:10.0.3.200:1194

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  10.0.3.0/24         !10.0.3.0/24
MASQUERADE  all  --  10.8.0.0/24          anywhere

ここまででOpenVPNサーバーの設定は完了です。

クライアント側の設定

次にOpenVPNクライアントの設定を行います。クライアントごとに以下の3つの作業が必要です。

  • そのクライアント用のクライアント証明書と鍵を作成する(OpenVPNサーバー上で行う)
  • クライアント証明書と鍵、認証局の証明書を安全な方法でクライアントに移動する
  • クライアントの設定を行う

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

今回の設定では、OpenVPNサーバーに接続するために、認証局の証明書で署名されたクライアント証明書が必要になります。そこで最初に、OpenVPNサーバー(つまりはVPS上のopenvpnコンテナの中)でクライアント証明書を作成しましょう。

openvpn$ sudo -s
openvpn# cd /etc/openvpn/easy-rsa/
openvpn# source vars
openvpn# ./build-key クライアント名
    基本的にEnterを押し、最後二つの署名を行うかどうかの確認でyと入力
openvpn# tar zcvf クライアント名.tgz keys/クライアント名.crt keys/クライアント名.key keys/ca.crt
openvpn# rm keys/クライアント名.*
openvpn# exit

必要なのは証明書(クライアント名.crt⁠⁠、(クライアント名.key⁠⁠、認証局の証明書(ca.crt)です。これを固めて安全な方法でクライアントへと移動します。移動したら作成したクライアント証明書と鍵は削除しておきましょう。

この作業はクライアントの数だけ行います。以降はすべてclient.tgzという名前で作ったことを前提に説明しますが、ファイルの内容はクライアントごとに異なっていることに注意してください。

LXCクライアントの設定

まずはLXCでクライアントコンテナを作成し、そこから接続する方法を試してみましょう。⁠LXCのインストール」「OpenVPN用のコンテナの作成」と同様にLXCをインストールして、コンテナを作成してください。ここでは作成したコンテナ名を「openvpn-client」とします。IPアドレスの固定化は実施してもしなくてもかまいません。

次にこのコンテナ上にOpenVPNをインストールし、tunデバイスを作成します。

openvpn-client$ sudo apt update
openvpn-client$ sudo apt install openvpn
openvpn-client$ sudo mkdir /dev/net
openvpn-client$ sudo mknod /dev/net/tun c 10 200
openvpn-client$ sudo chmod 0666 /dev/net/tun

事前に作成しておいたクライアント用の証明書類(client.tgz)をコンテナ内部に移動し、設定ディレクトリに展開します。

openvpn-client$ sudo tar xvf client.tgz -C /etc/openvpn/

クライアント用の設定ファイルを雛形から作成します。

openvpn-client$ sudo cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf /etc/openvpn/
openvpn-client$ sudo editor /etc/openvpn/client.conf
    remote myvps.example.com 1194
    ca keys/ca.crt
    cert keys/client.crt
    key keys/client.key

remoteには、OpenVPNサーバーのアドレスを指定してください。ca、cert、keyには先ほど展開した証明書類のファイルパスを指定します。

さらにiptablesの設定もしておきましょう。クライアント側は待ち受けはないので、サーバーとクライアントの間のパケットを疎通する設定だけで問題ありません。OpenVPNサーバーの時と同様に次のような設定ファイルを、⁠/var/lib/lxc/openvpn-client/iptables.sh」という名前でホスト上に作成します。

#!/bin/sh

# for OpenVPN
case $3 in
up)
    iptables -w -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
    ;;
down)
    iptables -w -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
    ;;
esac

さらにスクリプトに実行権限を追加し、コンテナ起動時に呼び出せるように設定を追加します。

$ sudo chmod a+x /var/lib/lxc/openvpn-client/iptables.sh
$ sudo editor /var/lib/lxc/openvpn-client/config
以下の2行を追加する
    lxc.network.script.up = /var/lib/lxc/openvpn-client/iptables.sh
    lxc.network.script.down = /var/lib/lxc/openvpn-client/iptables.sh

最後にOpenVPNサービスを立ち上げましょう。

openvpn-client$ sudo service openvpn start

次のようにtun0に10.8.0.0/24のどれかのアドレスが割り当てられていれば接続完了です。OpenVPNサーバーにpingを飛ばして疎通するかどうかも確認しておきましょう。

openvpn-client$ ip addr show tun0
2: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none
    inet 10.8.0.10 peer 10.8.0.9/32 scope global tun0
       valid_lft forever preferred_lft forever
openvpn-client$ ping 10.8.0.1
PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data.
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=107 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=100 ms
...

もしアドレスが割り当てられない、pingが通らない、といったトラブルに遭遇した場合、まずはOpenVPNサーバー上の/var/log/syslogを確認してください。サーバー側にクライアントから接続されていれば、次のようなログが残っているはずです。

ovpn-server[615]: AA.BB.CC.DD:23730 TLS: Initial packet from [AF_INET]AA.BB.CC.DD:23730, sid=1d1cec97 357177c6
(中略)
ovpn-server[615]: client5/AA.BB.CC.DD:23730 SENT CONTROL [client5]: 'PUSH_REPLY,route 10.8.0.0 255.255.255.0,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.18 10.8.0.17' (status=1)

もしこの間にエラーが表示されているようなら、そのエラーに対応しましょう。そもそも「Initial packet」が表示されない場合はOpenVPNサーバーが起動していないか、途中でパケットがフィルタリングされている、クライアントが全然異なるサーバーにアクセスしようとしているなどが考えられます。iptablesやサーバー、クライアントの設定ファイルを再度確認してください。

とくに今回はホストからLXCゲストへとNATでつなげているので、設定を間違うと途中でパケットがドロップされます。ホスト上で「sudo tcpdump -i eth0 port 1194」などとして、少なくともホスト上まではパケットが届いているかどうかや、ゲスト上にはどうかということも確認すると良いでしょう。

デスクトップクライアントの設定

デスクトップ版のUbuntuに最初からインストールされているネットワーク管理ツールであるNetworkManagerには、OpenVPNに接続するためのプラグインが存在します。これを使うと、デスクトップから直接OpenVPNサーバーへ接続できます。

最初にデスクトップ上にOpenVPNプラグインをインストールし、NetworkManagerを再起動してください。

$ sudo apt install network-manager-openvpn
$ sudo restart network-manager

画面右上のネットワークインジケーターの「VPN接続>VPNを設定>追加ボタン>OpenVPN>作成ボタン」と順番に選択すると、次のようなダイアログが表示されるはずです。

図1 ⁠接続名」はインジケーターで表示する名前として使われる
図1 「接続名」はインジケーターで表示する名前として使われる

GatewayにはOpenVPNサーバーのアドレス、User Certificateにはクライアント証明書類から展開したクライアント証明書(crtファイル⁠⁠、CA Certificateには同じく展開した認証局の証明書(caファイル⁠⁠、Private Keyにはクライアントの鍵(keyファイル)をそれぞれ指定します。

さらに「Advanced」ボタンで表示される画面から「Use LZO data compression」にチェックを入れておいてください。

次にIPv4設定タブに移動し、⁠方式」「自動(VPN)アドレス専用」に変更します。そして「ルート」ボタンから「そのネットワーク上のリソースのためにのみこの接続を使用」にチェックを入れます。これによりVPNのサブネットへの接続のみVPNが使われるようになります。クライアントから外向けのアクセスをすべてVPN経由にしたい場合はここの設定は変更しないでください。

最後に「保存」ボタンを押してダイアログを閉じたら設定完了です。

OpenVPNサーバーへの接続は、ネットワークインジケーターのVPN接続から、VPN接続名を選択します。接続に成功すれば、ネットワークインジケーターのアイコンの右下に鍵マークが追加されます。

図2 接続に成功すれば、通知が表示され、アイコンに鍵マークが追加される
図2 接続に成功すれば、通知が表示され、アイコンに鍵マークが追加される

Androidクライアントの設定

Androidには公式のOpenVPNクライアントであるOpenVPN Connectと非公式のクライアントであるOpenVPN for Androidの2種類のクライアントが存在します。基本はLXCの時と同様に、client.confと証明書類を用意してAndroidに安全な方法で移動するだけです。client.confはclient.ovpnという名前に変更しておきましょう。

client.ovpnは、UbuntuのOpenVPNパッケージにある雛形を元に設定すれば問題ありません。設定ファイルと証明書類をopenvpnというフォルダーを作成してそこにすべて保存し、クライアントから設定ファイルを読み込めば、接続できるはずです。

図3 非公式ながらOpenVPN for Androidの方が自由度は高
図3 非公式ながらOpenVPN for Androidの方が自由度は高

接続に成功したらIPv4アドレスが割り当てられています。前述のデスクトップやLXCクライアントも動作済みであれば、OpenVPNサーバーとクライアント間だけでなく、クライアント同士でもpingが疎通することを確認しておきましょう。

WebDAVサービスの設定

ここまでで、Android上のOpenVPNクライアントとホームサーバー上のOpenVPNクライアントが、VPS上のOpenVPNサーバーを経由してインターネット越しに接続できるようになりました。これでようやく下準備が完了です。

次にホームサーバー上のopenvpn-clientコンテナに、WebDAVをインストールして、Android端末からOpenVPN経由でファイルの閲覧やアップロードをできるようにしてみましょう。

nginxとWevDAVの導入

WebDAVはHTTPサーバーに拡張を追加することで導入できます。今回はnginxを使ってみます。nginxは本体にもWebDAV機能は実装されているのですが、いくつかのメソッドが未実装です。そこで他のメソッドにも対応したnginx-dav-ext-moduleを追加します。幸い、Ubuntuのリポジトリにはこのモジュールや他の拡張モジュールが追加された、nginx-extrasパッケージが存在しますので、それをインストールしましょう[7]⁠。

openvpn-client$ sudo apt install nginx-extras

次に必要なディレクトリを作成し、設定ファイルを編集しましょう。

openvpn-client$ sudo mkdir -p /var/www/webdav
openvpn-client$ sudo chown -R www-data: /var/www
openvpn-client$ sudo editor /etc/nginx/sites-available/webdav
server {
    listen      80 default_server;
    listen      [::]:80 default_server ipv6only=on;

    root /var/www/webdav;
    charset utf-8;
    autoindex on;

    allow 10.8.0.0/24;
    deny all;

    location / {
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_ext_methods PROPFIND OPTIONS;
        dav_access user:rw group:rw all:r;
        client_body_temp_path  /tmp/webdav;
        create_full_put_path on;
    }
}
openvpn-client$ sudo rm /etc/nginx/sites-enabled/default
openvpn-client$ cd /etc/nginx/sites-enabled/
openvpn-client$ sudo ln -s ../sites-available/webdav

client_body_temp_pathは、大きなファイルをPUTするときに発生する巨大なbodyデータを一時的に保存する場所を指定しています。さらにVPNの内側(10.8.0.0/24)からのアクセスのみを許可し、それ以外を拒否しています。

今回はドキュメントルートをWebDAVのルートに設定するために、default設定を削除しています。特定のディレクトリ以下のみを公開したい場合は、defaultを消す必要はありません。

最後にconfigtestで設定ファイルをテストし、問題なければ再読み込みします。

openvpn-client$ sudo service nginx configtest
openvpn-client$ sudo service nginx reload

WebDAVのテスト

WebDAVが動作しているかcURLでテストしてみましょう。

openvpn-client$ curl -T - http://10.8.0.2/matsuya.json <<END
{ hash: '#matsuyanow' }
END
openvpn-client$ curl http://10.8.0.2/matsuya.json
{ hash: '#matsuyanow' }
openvpn-client$ curl -X MKCOL http://10.8.0.2/matsuya/
openvpn-client$ curl -X MOVE --header 'Destination: http://10.8.0.2/matsuya/matsuya.json' \
    http://10.8.0.2/matsuya.json
openvpn-client$ curl -X PROPFIND http://10.8.0.2/matsuya/ -H "Depth: 1" \
    -d '<?xml version="1.0"?><a:propfind xmlns:a="DAV:"><a:prop><a:resourcetype/></a:prop></a:propfind>'
AndroidからならESファイルエクスプローラーを使えば簡単にWebDAVをマウントできます。スライドメニューのネットワークから「FTP」を選択し、新規ボタンで「webdav」を選びます。あとはサーバー名を指定するだけでWebDAVを使ってファイルの閲覧やアップロードができます。
図4 ESファイルエクスプローラーでファイルリストの表示
図4 ESファイルエクスプローラーでファイルリストの表示

もちろん表示されているファイルはIntentで他のアプリから閲覧できます。写真フォルダーをフォトビューワーでギャラリー表示して編集したり、動画ファイルを好みのプレイヤーで再生するといった、普通のフォルダーと同じ操作が可能です。

ホストディスクのマウント

ホストのディスクをコンテナにbind mountしておけば、WebDAVのデータをホストと共有できます。たとえばホスト上に/var/www/webdavが存在し、それをopenvpn-clientコンテナのWebDAVサーバーで公開したいとしましょう。/var/lib/lxc/openvpn-client/fstabに以下の行を追加します。

/var/www/webdav var/www/webdav none bind,create=dir

これにより、WebDAVにアップロードされたファイルはホストの/var/www/webdavに保存されますし、ホスト上からファイルを直接編集できるようになります。

もしコンテナ内部で、NFSやext4でマウントしたい場合は、AppArmorの設定を変える必要があります。LXC用のAppArmorのプロファイルは、特定のファイルシステムしかマウントを許可していないからです。そこでホスト上で、新しいプロファイルを作成し、そこでNFSやext系のマウントを許可してみましょう。

$ sudo cp /etc/apparmor.d/lxc/lxc-{default,openvpn-container}
$ sudo editor /etc/apparmor.d/lxc/lxc-openvpn-container
    profile lxc-openvpn-container flags=(attach_disconnected,mediate_deleted) {
        (上のプロファイル名を変更)
        (中略)
        (以下の4行を追加)
        mount fstype=nfs,
        mount fstype=nfs4,
        mount fstype=rpc_pipefs,
        mount fstype=ext*,
$ sudo apparmor_parser --replace /etc/apparmor.d/lxc-containers
$ sudo editor /var/lib/lxc/openvpn-client/config
    (以下の2行を追加)
    # Allow mount nfs and ext file systems
    lxc.aa_profile = lxc-openvpn-container

これでopenvpn-clientコンテナを再起動すれば、NFSやext4のマウントができるようになっているはずです。

DockerやJujuでも

LXCを使うといろんなものをコンテナの中に閉じ込めることができます。いざとなれば/var/lib/lxcの下を消しさえすれば、それまでにトライアンドエラーを繰り返すことでしっちゃかめっちゃかになったファイルシステムをばっさり削除できるのです。このメリット自体はKVMや他の仮想環境でも同じではあるもの、LXCの場合は仮想環境の作成や起動、終了がとても高速なので、いろんなソフトウェアをテストするには持って来いの環境になります。

今はやりのDockerはLXCと同じように、Linuxカーネルのcgroupやnamespaceを利用して隔離空間を構築することができます。単にOpenVPNをデプロイするだけであれば、おそらくDockerを使うほうが簡単でしょう。OpenVPN用イメージを作るためのDockerfileも存在します。

デプロイと言えば、UbuntuのJujuも今回行ったような設定作業を簡素化するツールの1つです。JujuにもOpenVPN用のCharmが存在しますので、シンプルな設定であればコマンド一発でデプロイ可能です。ちなみにOpenVPNのCharmは、ユーザーを追加するとクライアント証明書アーカイブが自動生成される仕組みになっていますので、証明書の作成が面倒だったらCharmを使うのも良いでしょう。

おすすめ記事

記事・ニュース一覧