WireguardでVPNを構築し443ポートをhttpsと共存

※記事内に商品プロモーションを含む場合があります

8月に、Ubuntuに移行が完了しましたが
VPNで問題が発生しています。
【関連】VPSのサーバOSをCentOS8からUbuntuに移行

L2TP/IPsecで構築し、NAT traversalを設定しましたが
PCとiPhoneで同時接続してしばらくすると、接続が切れてしまいます。

ログを見ると、
xl2tpd[81723]: Can not find tunnel 81723 (refhim=0)
xl2tpd[81723]: network_thread: unable to find call or tunnel to handle packet.
と出て、トンネルが見つからなくなっています。

本当は、VPSと自宅サーバをVPNで繋げたいのですが、
VPNが不安定ではそれも実現しません。

iPhoneでL2TP/IPsec接続すると、スリープさせたり、1時間ぐらい経過すると
接続は途切れてしまうのが、難点でした。

紆余曲折の後、Wireguardを導入しましたが、
PC+iPhone+自宅サーバが同一のグローバルIPからアクセスしても
全然途切れません。

WireguardでVPNを構築

WireguardはP2Pで本来はサーバとクライアントとは言いませんが
便宜上、接続を受ける方をサーバ
iPhoneやPCなど、接続する方をクライアントとします。

Wireguardでは秘密鍵と対となる公開鍵で認証するので秘密鍵が漏れると
簡単に第三者がVPNにアクセス出来てしまいます。
公開鍵は秘密鍵から作れます。

鍵の管理には、十分に注意してください。

手順としては、

  • 鍵の生成
  • 設定ファイルを作成
  • サービスの起動

で行います。

鍵の生成

rootでログインするか、sudoでsu -を実行しrootユーザーに切り替えます。
/etc/wireguard/にカレントディレクトリを変更します。

/etc/wireguard/はrootでないと表示されず
sudo cd /etc/wireguard/をroot以外で実行してもエラーになります。

sudo su -
cd /etc/wireguard/

このコマンドで、サーバの秘密鍵とサーバの公開鍵を生成します。

wg genkey | tee server_private.key | wg pubkey > server_public.key

このコマンドで、秘密鍵と公開鍵を一気に作成出来ます。
このコマンドは実行するたびに、ランダムな鍵が生成されます。

この場合、
サーバの秘密鍵が、 /etc/wireguard/server_private.key
サーバの公開鍵が、 /etc/wireguard/server_public.key
に保存されます。

鍵はこの様なランダムな文字列となっています。
6MibeBkof3hzPZF1WkxJ/Z/bNDWbmsS4Q9NFdoi0bm4=

クライアントの鍵もサーバと同じコマンドで生成できます。

wg genkey | tee peer1_private.key | wg pubkey > peer1_public.key
wg genkey | tee peer2_private.key | wg pubkey > peer2_public.key

こんな感じに、一部変更してください。

セキュリティ強度を上げるため、今回は事前共有鍵も設定するので
事前共有鍵も生成します。

wg genpsk | tee peer1_psk.key

生成した鍵は、そのまま置いておく場合は、パーミッションを
600にしておきます。

chmod 600 *.key

生成した鍵は、catコマンドで確認できますし、
各設定が完了したら、消しても動作には支障しません。

サーバ側の設定ファイルを作成

今回の設定ファイルは、wg0.conf(/etc/wireguard/wg0.conf)とします。
このwg0がそのまま、インターフェイス名になります。


[Interface]
PrivateKey = 【サーバの秘密鍵】
Address = 10.0.0.1
ListenPort = 51820

PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE

 [Peer]
## Peer1 クライアント用設定
PublicKey = 【Peer1の公開鍵】
PresharedKey = 【Peer1の事前共有鍵】
AllowedIPs = 10.0.0.2/32

[Peer]
## Peer2 クライアント用設定
PublicKey = 【Peer2の公開鍵】
PresharedKey = 【Peer2の事前共有鍵】
AllowedIPs = 10.0.0.3/32

項目ごとに説明します。

Address はVPNのIPアドレスを指定します。
10.0.0.1以外にも、192.168.2.1なども使えますが
自宅のLANとかぶらない様に設定します。

ListenPort は接続を待ち受けるポートを指定します。
Wireguardでは、51820が多く使われています。

PostUp VPNからインターネットへ行くパケットを
転送する設定を行っています、Wireguard起動時に適用されます。
その中に、ens3とありますが、サーバによって異なるので
書き換えてください(CentOSではeth0が多い)。

ip a

で確認できます。

PostDown Wireguard終了時に、PostUpで設定を
削除しています。

[Peer]は各クライアントごとに必要です。
PresharedKeyは事前共有鍵を使わない場合は不要です。

AllowedIPsは各クライアントのIPアドレスです。
クライアント側の設定と一致しないと動作しません。

IPフォワーディングの設定

Linuxのデフォルト設定では異なるネットワーク間の転送を
行わない設定になっています。

/etc/sysctl.confにある
net.ipv4.ip_forward=0
  ↓
net.ipv4.ip_forward=1

sudo vi /etc/sysctl.confなどで変更し、

sudo sysctl -p で設定を反映させます。

この設定を行わないと、VPNからインターネットへの
接続ができません。

サービスの起動

今回作った、wg0を起動するには、このコマンドを実行します。

wg-quick up wg0

終了する場合は、このコマンドを実行します。

wg-quick down wg0

調べた所、再読み込みの機能は無さそうなので
クライアントを追加したなど、設定ファイルを変更した場合は
wg0を終了させてから、起動させる必要があります。

システム起動時から、自動起動する場合はこのコマンドを実行します。

systemctl enable wg-quick@wg0

これで、接続待ちの状態ですが、ufwなどのファイアウォールで
ポート開放しておく必要があります。

サービスの起動前に、接続に使用するポート開放を行います。

ufwの場合以下のコマンドでポート開放できます。

ufw allow 51820

クライアント側の設定ファイルを作る

サーバ側に、鍵があるので接続に必要な設定ファイルを作ります。
基本的にはこの様になっています。

[Interface]
PrivateKey = 【Peer1の秘密鍵】
Address = 10.0.0.3/32

[Peer]
PublicKey = 【サーバ公開鍵】
PresharedKey =【Peer1の事前共有鍵】
AllowedIPs = 10.0.0.0/24, 【サーバのグローバルIP】/32
Endpoint = サーバ:51820
PersistentKeepalive = 25

項目ごとに説明します。

Address はサーバで設定したIPアドレスを設定します。

Endpoint はサーバとポート(ListenPort)を指定します。

PresharedKeyは事前共有鍵を使わない場合は不要です。

PersistentKeepalive はこの場合25秒に1回適当な通信をサーバと行う事で
接続を維持します。
NAT下から接続する場合は、この設定が無いと接続が切れる様です。

Windows,iOS両方ともデフォルトではWireguardに対応していないので
アプリをインストールしてVPNを使用します。

WindowsならSSHからコピペで設定を移行しました。

WireguardのWindowsアプリでの画面

AllowedIPs

AllowedIPsはサーバ側と、働きが異なります。

クライアント側で設定するAllowedIPsが
どのIPアドレス範囲を、VPNで通信するかを設定します。

パソコンや、自宅サーバなど
VPNのアドレス以外VPNを使いたくない場合は上と同じ様に設定します。
私の場合は、サーバと通信する場合は、VPNを使いたいので、サーバIPも入れています。

iPhoneなどで、すべての通信をVPN経由でしたい場合は
この様に設定します。

AllowedIPs = 0.0.0.0/0
DNS = 8.8.8.8

すべての通信をVPN経由で流しているのでプロバイダやキャリアの
DNSサーバが使えなくなります。

AllowedIPs = 0.0.0.0/0にする場合は、DNSでDNSサーバの設定
が必要です。

8.8.8.8はGoogle Public DNSのIPアドレスです。

QRコードで設定を転送する

パソコンだったら、SSH経由でも良いですが、問題はiPhoneなどスマホです。

上の設定を適当なテキストファイルに保存して以下のコマンドで
QRコードを表示させます。

qrencode -t ansi -r /etc/wireguard/tekitou.txt

設定ファイルのサイズによっては、全体を表示しきれないので
Teratermでは設定⇢フォントでフォントサイズを小さくして
QRコード全体を表示しました。

表示したQRコードはスマホのWireGuardアプリから
右上の+をタップして、QRコードから作成で、設定を読み込むことができます。

QRコード作成で使用したテキストファイルは使用予定がなければ
削除しておきます。

VPNでアクセスできているかの確認

Wireguardの接続ができているかは、
設定ソフトや、sudo wgコマンドで確認出来ます。

実際にインターネットへの接続がVPNサーバを経由しているかは
分かりません。

VPN経由でアクセス出来ているかは、アクセス情報確認ツールなどに
アクセスしてIPアドレスがサーバのIPアドレスなら
VPNでアクセスできています。

443番ポートをApacheと共存させる

一部の公衆Wifiではメジャーなポート(53/80/443)以外のポートへの
接続ができない場合があり、Apacheの443ポートと共存させるが良いと思います。

SSHとSSLを分けるsslhでもWireguard対応可能でしたが
アクセス元が全て127.0.0.1となり使えませんでした。

いろいろ調べたiptablesでWireguardのパケットを振り分けて
ApacheとWireguardを共存できる方法が見つかりました。

【参考】クラウドから撤退して自前サーバに自分でwebアプリを建てるおはなし

Wireguardのパケットは識別できるので、
443ポートに届いたパケットの内Wireguardのパケットのみを
51820ポートに転送します。

iptables -t nat -I PREROUTING -p udp -m udp --dport 443 -m string --algo kmp --from 28 --to 31 --hex-string '|01000000|' -j DNAT --to-destination 127.0.0.1:51820

参考元のコマンドではiptables -t nat -A となっていますが、
このままでは動作しませんでした。

ufwは、バックエンドではiptablesで動いているので
iptables -t nat -Iで設定を上書きする様にしたら動作しました。

このままだと、システム終了時に設定が消えてしまうので
wg0.confに設定を追加します。

PostUp = iptables -t nat -I PREROUTING -p udp -m udp --dport 443 -m string --algo kmp --from 28 --to 31 --hex-string '|01000000|' -j DNAT --to-destination 127.0.0.1:51820
PostDown = iptables -t nat -D PREROUTING -p udp -m udp --dport 443 -m string --algo kmp --from 28 --to 31 --hex-string '|01000000|' -j DNAT --to-destination 127.0.0.1:51820

この設定を追加することで、Wireguard起動時に転送設定が反映され
Wireguard終了時には転送設定が削除されます。

参考にしたサイト

WireGuardでVPNサーバーを構築する

【Ubuntu】WireGuardで簡単VPN環境を構築