新型コロナウイルスによる感染症の広がりにより、リモートワークの必要性が急遽クローズアップされています。
とはいえ、業務に必要なシステムをクラウド上に構築し、場所を選ばずに業務を継続できる環境をすでに整備できているという企業は、まだまだ少ないのが現実ではないでしょうか。社内ネットワーク上に業務システムが構築されている場合、とにもかくにも社内ネットワークにリモートから接続できないことには始まりません。そんな理由で、急遽リモートワーク用のVPN環境を整備することになった、社内情シス担当の方も多いのではないのでしょうか(ご苦労さまです…) 。
今回は、最近Linuxカーネルにマージされたことでも話題となっているWireGuardを用いて、VPNサーバーを構築する方法を紹介します。
WireGuardとは
WireGuard とは、Linuxのカーネルモジュールとして動作するオープンソースのVPN実装です。
OpenVPNやIPSecなど、従来のVPNサーバーのセットアップは、多くの場合煩雑になりがちでした。対してWireGuardは、SSHサーバーと同程度に、簡単にセットアップが可能なことを目指して開発されています。実際、サーバーとクライアント間で公開鍵を交換し、IPアドレスを割り当てるだけで、VPN通信が可能になります(設定については後述) 。
またWireGuardは非常にシンプルながらも高速であり、OpenVPNやIPsecよりも高いパフォーマンスを謳っています。
WireGuardのインストール
今回は例として筆者の自宅内にWireGuardサーバーを用意し、インターネットから家庭内へのVPN接続できる環境を構築します[1] 。
今回は、サーバーにはRaspberry Pi 4 4GBモデルと、Raspberry Pi向けのUbuntu Server 19.10.1(64bit版) を使用しました。OSをインストール したら、NICに固定のIPアドレスを割り振って おくと便利です。クライアントには、ノートPCにインストールしたUbuntu 19.10を使用しました。ノートPCをスマートフォンのテザリングを利用してインターネットに接続し、そこから自宅内にVPNを張ることで検証を行いました。
Ubuntu 19.10の場合、wireguardパッケージをインストールするだけで、WireGuardを使いはじめることができます。ただし、現時点でwireguardパッケージはUniverseリポジトリにあるという点には注意してください。
wireguardパッケージのインストール
$ sudo apt install wireguard
サーバー用鍵ペアの作成
WireGuardがインストールできたら、サーバー用の鍵ペアを作成します。WireGuardに関する様々な操作はwgコマンドで行います。まずは秘密鍵を作成するためgenkeyサブコマンドを実行してください。genkeyサブコマンドは作成した秘密鍵を標準出力に出力しますので、teeコマンドでファイルに保存しておくことにしましょう[2] 。また保存した鍵ファイルは、念の為パーミッションを落としておきましょう。
[2] 鍵は後述する設定ファイルに直接記述するため、別途ファイルに保存しておく必要はありません。しかし後で再利用することと、紛失すると鍵を再生成してクライアントに配り直さないといけないため、ここでは別ファイルに保存しています。
秘密鍵の作成と保存
$ wg genkey | sudo tee /etc/wireguard/server.key
$ sudo chmod 600 /etc/wireguard/server.key
続いて、秘密鍵から公開鍵を作成します。これにはpubkeyサブコマンドを実行します。pubkeyサブコマンドは、標準入力から秘密鍵を読み込み、公開鍵を標準出力に出力するサブコマンドです。鍵は後ほどクライアントに配る必要があるため、ここでも別ファイルに保存しておくことにします。
秘密鍵から公開鍵を作成する
$ sudo cat /etc/wireguard/server.key | wg pubkey | sudo tee /etc/wireguard/server.pub
$ sudo chmod 600 /etc/wireguard/server.pub
クライアント用鍵ペアの作成
クライアント側でも、同様にWireGuardのインストールと鍵の作成を行います。クライアントのUbuntu上でも、先ほどサーバーで実行したのと同様のコマンドを実行してください。ここでも秘密鍵と公開鍵はファイルに保存しておくとよいでしょう。
クライアントPC上でも、WireGuardのインストールと鍵ペアの作成を行う
$ sudo apt install wireguard
$ wg genkey | sudo tee /etc/wireguard/client.key
$ sudo cat /etc/wireguard/client.key | wg pubkey | sudo tee /etc/wireguard/client.pub
$ sudo chmod 600 /etc/wireguard/client.key /etc/wireguard/client.pub
サーバーの設定
ここは再びサーバー上での作業となります。WireGuardの設定ファイルは、「 /etc/wireguard/作成するインターフェイス名.conf」という名前で作成する必要があります。今回はインターフェイス名を「wg0」とするので、「 /etc/wireguard/wg0.conf」というファイルを作成し、以下の内容を記述してください。なお今回の例ではWireGuardのネットワークに「10.0.0.0/24」のサブネットを割り当て、サーバーのインターフェイスに「10.0.0.1/32」 、クライアントのインターフェイスに「10.0.0.2/32」のIPアドレスを割り当てることにします。
サーバーの/etc/wireguard/wg0.confの内容
[Interface]
PrivateKey = 作成したサーバーの秘密鍵
Address = 10.0.0.1 (サーバーのインターフェイスに割り当てるIPアドレス)
ListenPort = 51820
[Peer]
PublicKey = 作成したクライアントの公開鍵
AllowedIPs = 10.0.0.2/32 (クライアントに割り当てたIPアドレス)
クライアントの設定
クライアント側にも設定ファイルが必要です。以下の内容で「/etc/wireguard/wg0.conf」を作成してください。インターネットから家庭内へ接続する場合、ここで設定するサーバーのIPアドレスは、ルーターに割り当てられているグローバルIPアドレスになるでしょう。なおプロバイダから固定のIPアドレスを割り当てられていない場合は、ダイナミックDNSサービスを利用するなどの工夫が必要になるかもしれません。
クライアントの/etc/wireguard/wg0.confの内容
[Interface]
PrivateKey = クライアントの秘密鍵
Address = 10.0.0.2 (クライアントのIPアドレス)
[Peer]
PublicKey = サーバーの公開鍵
EndPoint = サーバーのIPアドレス:51820
AllowedIPs = 10.0.0.0/24 (WireGuardを経由して通信する先のIPアドレス)
見ての通りWireGuardの設定は、Interfaceに自分自身の設定を記述し、Peerに接続先の情報を登録するだけという、非常にシンプルなものになっています。
WireGuardの起動
サーバー上で、WireGuardのサービスを起動しましょう。WireGuardの起動や終了には、wg-quickというラッパースクリプトを使用します。systemdのユニット名は、「 wg-quick@使用するインターフェイス名」となっています。sytemctlコマンドで、サービスを有効化してください。
wg-quick@wg0を有効化し、起動する
$ sudo systemctl enable wg-quick@wg0
$ sudo systemctl start wg-quick@wg0
ipコマンドでインターフェイスが生成されていることを確認しましょう。wg0.confで設定したIPアドレス(10.0.0.1)が割り振られていれば成功です。
ipコマンドでインターフェイスの状態を確認する
$ ip a
(...略...)
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.0.0.1/32 scope global wg0
valid_lft forever preferred_lft forever
クライアントからVPNへの接続
クライアントからVPNへ接続するには、「 wg-quick up インターフェイス名」コマンドを実行します。
VPNの接続を確立する
$ sudo wg-quick up wg0
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.2 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.0.0.0/24 dev wg0
サーバーと同様に、ipコマンドでクライアントのインターフェイスの状態を確認してみましょう。クライアント上でもwg0が作成され、10.0.0.2のIPアドレスが割り当てられていれば成功です。
クライアントのインターフェイスの状態を確認する
$ ip a
(...略...)
4: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.0.0.2/32 scope global wg0
valid_lft forever preferred_lft forever
接続が確立できたら、クライアントからサーバーに対してPingを打って、応答が返ってくることを確認してみましょう。またその状態で、サーバー上でwgコマンドを実行してみてください。現在のコネクションの状態が表示されます。ハンドシェイクが成功し、データの送受信が発生していれば成功です。
wgコマンドでVPN接続の状態を確認する
$ sudo wg
interface: wg0
public key: (サーバーの公開鍵)
private key: (hidden)
listening port: 51820
peer: (クライアントの公開鍵)
endpoint: クライアントのグローバルIPアドレス:クライアントのポート
allowed ips: 10.0.0.2/32
latest handshake: 41 seconds ago
transfer: 564 B received, 476 B sent
LAN内へのルーティングを行うには
インターネットから家庭内へのVPNの確立には成功しました。しかしこの設定だけですと、WireGuardのネットワーク内でしか通信ができません。通常、VPNはインターネットからローカルネットワークに接続する目的で構築します。つまり、WireGuardのネットワークから家庭内LAN(192.168.1.0/24)へのルーティングができないと、その意味がありません。というわけで、ルーティングを設定します。
Ubuntuはデフォルトで、パケットのフォワードが禁止されています。サーバーの「/etc/sysctl.conf」をエディタで開き、コメントアウトされている「net.ipv4.ip_forward=1」のコメントアウトを解除してください。
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1 ← 行頭の # を消してコメントを解除する
sysctlコマンドを実行し、変更を反映します。
$ sudo sysctl -p
WireGuardのサーバー設定には「PostUp」「 PostDown」という項目があり、インターフェイスの起動/終了時に任意のコマンドを実行させることができます。ここにiptablesコマンドを指定して、インターフェイスが起動している間だけ、iptablesにIPマスカレードの設定を追加するようにします。「 /etc/wireguard/wg0.conf」の[Interface]セクションに以下の内容を追記してください。その後、WireGuardを再起動してください。
サーバーのwg0.confにIPマスカレードを行うiptablesのルールを追加する。なおwlan0は実際に使っているNICの名前に置き換えること
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE
WireGuardを再起動すると、iptablesのPOSTROUTINGチェインにルールが追加される
$ sudo systemctl restart wg-quick@wg0
$ sudo iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
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 -- 0.0.0.0/0 0.0.0.0/0
これでサーバー側の設定は完了です。
最後に、クライアントのAllowedIPsにLAN内のIPレンジを追加します。クライアントのPeer設定にあるAllowedIPsは、そのPeerを使ってパケットを送信する宛先IPアドレスのリストです。ここにLAN内のプライベートIPアドレスを追記することで、LAN内へのパケットがWireGuardを経由してルーティングされることになります。
クライアントのwg0.confにAllowedIPsを追記する
AllowedIPs = 10.0.0.0/24, 192.168.1.0/24 ← LAN内で使われているIPアドレスをカンマ区切りで追記する
wg0を一度終了してから、再起動する
$ sudo wg-quick down wg0
$ sudo wg-quick up wg0
LAN内の他のサーバーやPCに、VPN経由で接続できることを確認してみてください。
なおAllowedIPsに0.0.0.0/0と書けば、すべてのパケットをWireGuard経由で送信できます。WebブラウザからインターネットへのアクセスをVPN経由にしたい場合などに有効です。
WireGuardには、Linuxカーネルのメインラインにマージされていること、セットアップの容易さ、高いパフォーマンスといったメリットがあります。また本記事ではLinuxのコマンドラインからの使い方のみを紹介しましたが、Windows、Mac、Androidなどのクライアントも用意されているため、デスクトップPCやスマートフォンからVPNへ接続することも簡単です。今後LinuxでVPNサーバーを構築する際は、WireGuardの利用をぜひ検討してみてください。