VPSとWireGuardを使ってVPN環境を構築した。
ただ、環境によって接続できる場合と接続できない場合があるため、原因と解決策を教えて欲しい。
こんなお悩みを解決します。
これまでに、WireGuardでVPN接続する方法を解説してきました。
一方、諸事情で自宅のプロバイダーを変更したところ、これまで利用できていたWireGuardによるVPN接続が利用できなくなりました。
実は、インフラ周りの変更に伴い、MTU(Maximum Transmission Unit)やMSS(Maximum Segment Size)の設定に影響が出ていたんですよね。
この記事では、複数の拠点にVPN接続している状況下において、特定の拠点間のみ通信できない場合の改善方法について解説します。
記事を読み終えると、「WireGuardでVPN接続できない」といったトラブル事例とその解決策について知ることができますよ!
前提条件
環境構築の方法は、下記の記事を参考にしてください。
【解説】VPS+VPN(WireGuard)による自宅サーバの公開方法を分かりやすく解説!(VPS契約編)
続きを見る
【解説】VPS+VPN(WireGuard)による自宅サーバの公開方法(VPN設定編)
続きを見る
【解説】VPS+VPN(WireGuard)による自宅サーバの公開方法(ルーティング設定編)
続きを見る
【番外編】VPS+VPN(WireGuard)による自宅サーバの公開方法(iptablesのサンプルconfig)
続きを見る
ネットワーク構成
以前の記事を参考に、今回は以下のような構成を考えます。
WireGuardはDockerコンテナで起動しているものとします。
スマホ、VPS、別宅ネットワーク、自宅ネットワークの構成はそれぞれ以下のようになります。
対象 | 割り当てられるIP | 備考 |
---|---|---|
スマホ(VPN Client Phone) | 【WireGuard】10.1.2.4 | - |
VPS(VPN Server) | 【WireGuard】10.1.2.1 | WireGuardで用いるポートは解放済み。 |
別宅ネットワーク:別宅サーバ2(VPN Client Other) | 【WireGuard】10.1.2.2 【WireGuardが動作するDocker】10.0.10.2 【LAN】192.168.0.2 | Dockerネットワーク(10.0.10.0/24)はLAN(192.168.0.0/24)と通信できる状態とする。 |
別宅ネットワーク:別宅サーバ3 | 【LAN】192.168.0.3 | WireGuardやDockerのネットワークにはアクセスできない。 |
自宅ネットワーク:自宅サーバ2(VPN Client Home) | 【WireGuard】10.1.2.3 【WireGuardが動作するDocker】10.0.20.2 【LAN】192.168.11.2 | Dockerネットワーク(10.0.20.0/24)はLAN(192.168.11.0/24)と通信できる状態とする。 |
自宅ネットワーク:自宅サーバ3 | 【LAN】192.168.11.3 | WireGuardやDockerのネットワークにはアクセスできない。 |
上記のネットワークにおいて、別宅ネットワーク内にあるサーバにアクセスした場合の経路は以下のようになります。
また、自宅ネットワーク内にあるサーバにアクセスした場合の経路は、以下のようになります。
VPSにおける設定
VPSにおける設定内容を以下に示します。
ちなみに、ベースは以下のリンクにあるものを利用しています。
https://github.com/yuruto-free/vpnaccess-wireguard-nginx
ディレクトリ構成
./
|-- docker-compose.yml
|-- envs/
| `-- wireguard/
| `-- .env
|-- wireguard
| |-- templates/
| | |-- server.conf
| | `-- peer.conf
| `-- iptables_script/
| |-- postup.sh
| |-- postdown.sh
| |-- conf.up.d/
| | |-- 01-routing-other-network.conf
| | `-- 02-routing-home-network.conf
| `-- conf.down.d/
| |-- 01-routing-other-network.conf
| `-- 02-routing-home-network.conf
`-- wrapper.sh
envs/wireguard/.env
環境変数は例として示すため、実際に理解する際は自身の環境に読み替えて設定してください。
SERVERURL=example.vpn.com
SERVERPORT=51820
PEERS=OtherNetwork,HomeNetwork,myPhone
PEERDNS=8.8.8.8,10.1.2.1
INTERNAL_SUBNET=10.1.2.0/24
MTU=1380
KEEP_ALIVE=25
ALLOWEDIPS=10.1.2.0/24
SERVER_ALLOWEDIPS_PEER_OtherNetwork=10.0.10.0/24
SERVER_ALLOWEDIPS_PEER_HomeNetwork=10.0.20.0/24
docker-compose.yml
version: '3.7'
x-logging:
&json-logging
driver: json-file
options:
max-size: "1m"
max-file: "3"
services:
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000 # VPS上で「id -u ${USER}」を実行し、その出力結果を反映
- PGID=1000 # VPS上で「id -g ${USER}」を実行し、その出力結果を反映
- TZ=Asia/Tokyo
env_file:
- ./envs/wireguard/.env
networks:
- vpn
ports:
- 51820:51820/udp
volumes:
- /lib/modules:/lib/modules
- ./wireguard:/config
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: always
logging: *json-logging
networks:
vpn:
name: vpn
templates/server.conf・peer.conf
templates/server.conf
は以下のようになります。
[Interface]
Address = ${INTERFACE}.1
ListenPort = 51820
MTU = ${MTU:-1420}
PrivateKey = $(cat /config/server/privatekey-server)
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth+ -j MASQUERADE; iptables -A FORWARD -i %i -o %i -j ACCEPT
PostUp = /config/iptables_script/postup.sh
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth+ -j MASQUERADE; iptables -D FORWARD -i %i -o %i -j ACCEPT
PostDown = /config/iptables_script/postdown.sh
また、templates/peer.conf
は以下のようになります。
[Interface]
Address = ${CLIENT_IP}
PrivateKey = $(cat /config/${PEER_ID}/privatekey-${PEER_ID})
ListenPort = 51820
MTU = ${MTU:-1420}
DNS = ${PEERDNS}
[Peer]
PublicKey = $(cat /config/server/publickey-server)
PresharedKey = $(cat /config/${PEER_ID}/presharedkey-${PEER_ID})
Endpoint = ${SERVERURL}:${SERVERPORT}
AllowedIPs = ${ALLOWEDIPS}
PersistentKeepAlive = ${KEEP_ALIVE:-25}
postup.sh・postdown.sh
postup.sh
はWireGuardのコンテナ起動時に呼び出されるスクリプトで、conf.up.d
以下にあるconfファイルに記載されたコマンドを実行するスクリプトになります。
#!/bin/bash
readonly base_dir=$(cd $(dirname $0) && pwd)
ls ${base_dir}/conf.up.d/*.conf | while read config_file; do
echo "[#] - ${config_file}"
cat ${config_file} | grep -v "^\W*$\|\s*#.*" | while read cmd; do
echo "[#] ${cmd}"
eval "${cmd}"
done
done
似たような位置づけのスクリプトとしてpostdown.sh
があり、WireGuardのコンテナ終了時に呼び出されます。
このスクリプトは、conf.down.d
以下にあるconfファイルに記載されたコマンドを実行するスクリプトになります。
#!/bin/bash
readonly base_dir=$(cd $(dirname $0) && pwd)
ls ${base_dir}/conf.down.d/*.conf | while read config_file; do
echo "[#] - ${config_file}"
cat ${config_file} | grep -v "^\W*$\|\s*#.*" | while read cmd; do
echo "[#] ${cmd}"
eval "${cmd}"
done
done
iptables
以下の2つは最も重要なiptablesの設定ファイルとなります。
まずは、別宅ネットワーク(OtherNetwork)向けの設定です。
# conf.up.d/01-routing-other-network.conf
# For Web Server
iptables -t nat -A PREROUTING -p tcp -d 192.168.0.3/32 --dport 80 -j DNAT --to-destination 10.0.10.2:80
# IP masquerade
iptables -t nat -A POSTROUTING -d 10.0.10.0/24 -j MASQUERADE
# conf.down.d/01-routing-other-network.conf
# For Web Server
iptables -t nat -D PREROUTING -p tcp -d 192.168.0.3/32 --dport 80 -j DNAT --to-destination 10.0.10.2:80
# IP masquerade
iptables -t nat -D POSTROUTING -d 10.0.10.0/24 -j MASQUERADE
続いて、自宅ネットワーク(HomeNetwork)向けの設定です。
# conf.up.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -A PREROUTING -p tcp -d 192.168.11.3/32 --dport 80 -j DNAT --to-destination 10.0.20.2:80
# IP masquerade
iptables -t nat -A POSTROUTING -d 10.0.20.0/24 -j MASQUERADE
# conf.down.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -D PREROUTING -p tcp -d 192.168.11.3/32 --dport 80 -j DNAT --to-destination 10.0.20.2:80
# IP masquerade
iptables -t nat -D POSTROUTING -d 10.0.20.0/24 -j MASQUERADE
自宅・別宅における設定
docker-compose.yml
で割り当てるIPアドレスが異なるだけであるため、自宅ネットワークを対象に設定情報を示します。
また、以降の設定は下記のリンクにあるものをベースに構築しています。
https://github.com/yuruto-free/wireguard-local-access-client
ディレクトリ構成
./
|-- docker-compose.yml
|-- config
| |-- wg0.conf
| `-- iptables_script/
| |-- postup.sh
| |-- postdown.sh
| |-- conf.up.d/
| | `-- 02-routing-home-network.conf
| `-- conf.down.d/
| `-- 02-routing-home-network.conf
`-- wrapper.sh
docker-compose.yml
version: '3.7'
x-logging:
&json-logging
driver: json-file
options:
max-size: "1m"
max-file: "3"
services:
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000 # 自宅サーバ上で「id -u ${USER}」を実行し、その出力結果を反映
- PGID=1000 # 自宅サーバ上で「id -g ${USER}」を実行し、その出力結果を反映
- TZ=Asia/Tokyo
networks:
backbone:
ipv4_address: 10.0.20.2
volumes:
- /lib/modules:/lib/modules
- ./config:/config
restart: always
logging: *json-logging
extra_hosts:
# LANで構成されるネットワーク(192.168.11.0/24)とDockerネットワーク(10.0.20.0/24)を共存させる
- "host.docker.internal:10.0.20.1"
networks:
backbone:
name: backbone
driver: bridge
driver_opts:
com.docker.network.driver.mtu: 1480 # 必要に応じて設定すること
ipam:
driver: default
config:
- subnet: 10.0.20.0/24
※postup.sh
、postdown.sh
はVPS側と同じ内容のため、省略します。
wg0.conf
VPS上で生成したconfig
(peer_OtherNetwork/peer_OtherNetwork.conf
やpeer_HomeNetwork/peer_HomeNetwork.conf
)をコピーし、PostUpとPostDownの記載を追記します。
[Interface]
Address = 10.1.2.3
PrivateKey = abcdefghijklmnOPQRSTUvwxyz0123456789!*+/?>=<
ListenPort = 51820
MTU = 1380
DNS = 8.8.8.8,10.1.2.1
PostUp = /config/iptables_script/postup.sh # 追加
PostDown = /config/iptables_script/postdown.sh # 追加
[Peer]
PublicKey = 0123456789ABCDEFGHijklmnopqrstuVWXYZ!*+/?>=<
PresharedKey = 0123456789!*+/?>=<0123456789abcdefghijklmnopqrstuvwxyz
Endpoint = example.vpn.com:51820
AllowedIPs = 10.1.2.0/24
PersistentKeepAlive = 25
iptables
以下は最も重要なiptablesの設定ファイルとなります。
conf.up.d/02-routing-home-network.conf
の設定内容は以下のようになります。
# conf.up.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.11.3:80
# IP masquerade
iptables -t nat -A POSTROUTING -d 192.168.11.0/24 -j MASQUERADE
また、conf.down.d/02-routing-home-network.conf
の設定内容は以下のようになります。
# conf.down.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -D PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.11.3:80
# IP masquerade
iptables -t nat -D POSTROUTING -d 192.168.11.0/24 -j MASQUERADE
トラブルの内容
今回発生したトラブルを解説します。
VPN接続中に通信できる例
別宅ネットワーク内にある別宅サーバ3にアクセスする時の通信経路は、以下のようになります。
このケースでは、問題なく接続できています。
以降で詳細の通信ログを紹介します。
VPS上の通信ログ
リクエスト(HTTP: GET / HTTP/1.1
)を出した後、レスポンス(HTTP: HTTP/1.1 200 OK
)が返ってきていることが分かります。
tcpdump -ttttt -n -i any tcp and \( net 10.1.2.0/24 or net 10.0.10.0/24 or net 192.168.0.0/24 \) and ! port 22
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
00:00:00.000000 wg0 In IP 10.1.2.4.43952 > 192.168.0.3.80: Flags [S], seq 2413258095, win 65535, options [mss 1340,sackOK,TS val 1027120945 ecr 0,nop,wscale 9], length 0
00:00:00.000030 wg0 Out IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [S], seq 2413258095, win 65535, options [mss 1340,sackOK,TS val 1027120945 ecr 0,nop,wscale 9], length 0
00:00:00.012366 wg0 In IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [S.], seq 1806506038, ack 2413258096, win 65160, options [mss 1460,sackOK,TS val 2637933584 ecr 1027120945,nop,wscale 7], length 0
00:00:00.012381 wg0 Out IP 192.168.0.3.80 > 10.1.2.4.43952: Flags [S.], seq 1806506038, ack 2413258096, win 65160, options [mss 1460,sackOK,TS val 2637933584 ecr 1027120945,nop,wscale 7], length 0
00:00:00.027284 wg0 In IP 10.1.2.4.43952 > 192.168.0.3.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1027120985 ecr 2637933584], length 0
00:00:00.027298 wg0 Out IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1027120985 ecr 2637933584], length 0
00:00:00.035072 wg0 In IP 10.1.2.4.43952 > 192.168.0.3.80: Flags [P.], seq 1:435, ack 1, win 128, options [nop,nop,TS val 1027120993 ecr 2637933584], length 434: HTTP: GET / HTTP/1.1
00:00:00.035084 wg0 Out IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [P.], seq 1:435, ack 1, win 128, options [nop,nop,TS val 1027120993 ecr 2637933584], length 434
00:00:00.047448 wg0 In IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [.], ack 435, win 506, options [nop,nop,TS val 2637933618 ecr 1027120993], length 0
00:00:00.047460 wg0 Out IP 192.168.0.3.80 > 10.1.2.4.43952: Flags [.], ack 435, win 506, options [nop,nop,TS val 2637933618 ecr 1027120993], length 0
00:00:00.048478 wg0 In IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [.], seq 1:1329, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 1328
00:00:00.048487 wg0 Out IP 192.168.0.3.80 > 10.1.2.4.43952: Flags [.], seq 1:1329, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 1328: HTTP: HTTP/1.1 200 OK
00:00:00.048576 wg0 In IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [P.], seq 1329:1349, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 20
00:00:00.048589 wg0 Out IP 192.168.0.3.80 > 10.1.2.4.43952: Flags [P.], seq 1329:1349, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 20: HTTP
00:00:00.062908 wg0 In IP 10.1.2.4.43952 > 192.168.0.3.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1027121021 ecr 2637933618,nop,nop,sack 1 {1329:1349}], length 0
00:00:00.062920 wg0 Out IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1027121021 ecr 2637933618,nop,nop,sack 1 {1329:1349}], length 0
(以下略)
別宅サーバ2の通信ログ
別宅サーバ3に渡したリクエスト(HTTP: GET / HTTP/1.1
)に対して、別宅サーバ3からのレスポンス(HTTP: HTTP/1.1 200 OK
)をVPS側に返せていることが確認できます。
tcpdump -ttttt -n -i any tcp and \( net 10.1.2.0/24 or net 10.0.10.0/24 or net 192.168.0.0/24 \) and ! port 22
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
00:00:00.000000 wg0 In IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [S], seq 2413258095, win 65535, options [mss 1340,sackOK,TS val 1027120945 ecr 0,nop,wscale 9], length 0
00:00:00.000104 eth0 Out IP 10.0.10.2.43952 > 192.168.0.3.80: Flags [S], seq 2413258095, win 65535, options [mss 1340,sackOK,TS val 1027120945 ecr 0,nop,wscale 9], length 0
00:00:00.000304 eth0 In IP 192.168.0.3.80 > 10.0.10.2.43952: Flags [S.], seq 1806506038, ack 2413258096, win 65160, options [mss 1460,sackOK,TS val 2637933584 ecr 1027120945,nop,wscale 7], length 0
00:00:00.000341 wg0 Out IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [S.], seq 1806506038, ack 2413258096, win 65160, options [mss 1460,sackOK,TS val 2637933584 ecr 1027120945,nop,wscale 7], length 0
00:00:00.027074 wg0 In IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1027120985 ecr 2637933584], length 0
00:00:00.027115 eth0 Out IP 10.0.10.2.43952 > 192.168.0.3.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1027120985 ecr 2637933584], length 0
00:00:00.034938 wg0 In IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [P.], seq 1:435, ack 1, win 128, options [nop,nop,TS val 1027120993 ecr 2637933584], length 434
00:00:00.034975 eth0 Out IP 10.0.10.2.43952 > 192.168.0.3.80: Flags [P.], seq 1:435, ack 1, win 128, options [nop,nop,TS val 1027120993 ecr 2637933584], length 434: HTTP: GET / HTTP/1.1
00:00:00.035094 eth0 In IP 192.168.0.3.80 > 10.0.10.2.43952: Flags [.], ack 435, win 506, options [nop,nop,TS val 2637933618 ecr 1027120993], length 0
00:00:00.035128 wg0 Out IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [.], ack 435, win 506, options [nop,nop,TS val 2637933618 ecr 1027120993], length 0
00:00:00.036173 eth0 In IP 192.168.0.3.80 > 10.0.10.2.43952: Flags [P.], seq 1:1349, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 1348: HTTP: HTTP/1.1 200 OK
00:00:00.036219 wg0 Out IP 10.0.10.2.80 > 10.1.2.1.43952: Flags [P.], seq 1:1349, ack 435, win 506, options [nop,nop,TS val 2637933619 ecr 1027120993], length 1348
00:00:00.062725 wg0 In IP 10.1.2.1.43952 > 10.0.10.2.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1027121021 ecr 2637933618,nop,nop,sack 1 {1329:1349}], length 0
00:00:00.062769 eth0 Out IP 10.0.10.2.43952 > 192.168.0.3.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1027121021 ecr 2637933618,nop,nop,sack 1 {1329:1349}], length 0
(以下略)
VPN接続中に通信できない例
一方、自宅ネットワーク内にある自宅サーバ3にアクセスする時の通信経路は、以下のようになります。
この時、自宅サーバ2からVPSに向けた通信が届いていませんでした。
詳細な通信ログは以下のようになります。
VPS上の通信ログ
リクエスト(HTTP: GET / HTTP/1.1
)を出した後、0.1秒経ってもレスポンスが返ってこないことが分かります。
tcpdump -ttttt -n -i any tcp and \( net 10.1.2.0/24 or net 10.0.20.0/24 or net 192.168.11.0/24 \) and ! port 22
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
00:00:00.000000 wg0 In IP 10.1.2.4.44486 > 192.168.11.3.80: Flags [S], seq 2282162397, win 65535, options [mss 1340,sackOK,TS val 1026978371 ecr 0,nop,wscale 9], length 0
00:00:00.000025 wg0 Out IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [S], seq 2282162397, win 65535, options [mss 1340,sackOK,TS val 1026978371 ecr 0,nop,wscale 9], length 0
00:00:00.014145 wg0 In IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [S.], seq 1716633062, ack 2282162398, win 65160, options [mss 1460,sackOK,TS val 3510018023 ecr 1026978371,nop,wscale 7], length 0
00:00:00.014169 wg0 Out IP 192.168.11.3.80 > 10.1.2.4.44486: Flags [S.], seq 1716633062, ack 2282162398, win 65160, options [mss 1460,sackOK,TS val 3510018023 ecr 1026978371,nop,wscale 7], length 0
00:00:00.028542 wg0 In IP 10.1.2.4.44486 > 192.168.11.3.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 0
00:00:00.028558 wg0 Out IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 0
00:00:00.028571 wg0 In IP 10.1.2.4.44486 > 192.168.11.3.80: Flags [P.], seq 1:431, ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 430: HTTP: GET / HTTP/1.1
00:00:00.028576 wg0 Out IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [P.], seq 1:431, ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 430: HTTP: GET / HTTP/1.1
00:00:00.043273 wg0 In IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], ack 431, win 506, options [nop,nop,TS val 3510018052 ecr 1026978400], length 0
00:00:00.043285 wg0 Out IP 192.168.11.3.80 > 10.1.2.4.44486: Flags [.], ack 431, win 506, options [nop,nop,TS val 3510018052 ecr 1026978400], length 0
00:00:00.102801 wg0 In IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 7969:8269, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 300: HTTP
00:00:00.102815 wg0 Out IP 192.168.11.3.80 > 10.1.2.4.44486: Flags [P.], seq 7969:8269, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 300: HTTP
00:00:00.117046 wg0 In IP 10.1.2.4.44486 > 192.168.11.3.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1026978488 ecr 3510018052,nop,nop,sack 1 {7969:8269}], length 0
00:00:00.117059 wg0 Out IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1026978488 ecr 3510018052,nop,nop,sack 1 {7969:8269}], length 0
(以下略)
自宅サーバ2上の通信ログ
リクエスト(HTTP: GET / HTTP/1.1
)を出した後、レスポンス(HTTP: HTTP/1.1 200 OK
)を何度も返していることが分かります。
ここから、自宅サーバ2からVPSにレスポンスが返せていないと考えられます。
tcpdump -ttttt -n -i any tcp and \( net 10.1.2.0/24 or net 10.0.20.0/24 or net 192.168.11.0/24 \) and ! port 22
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
00:00:00.000000 wg0 In IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [S], seq 2282162397, win 65535, options [mss 1340,sackOK,TS val 1026978371 ecr 0,nop,wscale 9], length 0
00:00:00.000084 eth0 Out IP 10.0.20.2.44486 > 192.168.11.3.80: Flags [S], seq 2282162397, win 65535, options [mss 1340,sackOK,TS val 1026978371 ecr 0,nop,wscale 9], length 0
00:00:00.001005 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [S.], seq 1716633062, ack 2282162398, win 65160, options [mss 1460,sackOK,TS val 3510018023 ecr 1026978371,nop,wscale 7], length 0
00:00:00.001040 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [S.], seq 1716633062, ack 2282162398, win 65160, options [mss 1460,sackOK,TS val 3510018023 ecr 1026978371,nop,wscale 7], length 0
00:00:00.028635 wg0 In IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 0
00:00:00.028642 wg0 In IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [P.], seq 1:431, ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 430: HTTP: GET / HTTP/1.1
00:00:00.028695 eth0 Out IP 10.0.20.2.44486 > 192.168.11.3.80: Flags [.], ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 0
00:00:00.028714 eth0 Out IP 10.0.20.2.44486 > 192.168.11.3.80: Flags [P.], seq 1:431, ack 1, win 128, options [nop,nop,TS val 1026978400 ecr 3510018023], length 430: HTTP: GET / HTTP/1.1
00:00:00.029642 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [.], ack 431, win 506, options [nop,nop,TS val 3510018052 ecr 1026978400], length 0
00:00:00.029676 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], ack 431, win 506, options [nop,nop,TS val 3510018052 ecr 1026978400], length 0
00:00:00.088407 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [P.], seq 1:2657, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP: HTTP/1.1 200 OK
00:00:00.088450 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 1:2657, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP: HTTP/1.1 200 OK
00:00:00.088505 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [P.], seq 2657:5313, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088536 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 2657:5313, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088575 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [P.], seq 5313:7969, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088605 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 5313:7969, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088636 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [P.], seq 7969:8269, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 300: HTTP
00:00:00.088663 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 7969:8269, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 300: HTTP
00:00:00.088681 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [P.], seq 8269:10925, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088710 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [P.], seq 8269:10925, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 2656: HTTP
00:00:00.088739 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [.], seq 10925:12253, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 1328: HTTP
00:00:00.088766 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], seq 10925:12253, ack 431, win 506, options [nop,nop,TS val 3510018109 ecr 1026978400], length 1328: HTTP
00:00:00.116795 wg0 In IP 10.1.2.1.44486 > 10.0.20.2.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1026978488 ecr 3510018052,nop,nop,sack 1 {7969:8269}], length 0
00:00:00.116823 eth0 Out IP 10.0.20.2.44486 > 192.168.11.3.80: Flags [.], ack 1, win 131, options [nop,nop,TS val 1026978488 ecr 3510018052,nop,nop,sack 1 {7969:8269}], length 0
00:00:00.117962 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [.], seq 12253:13581, ack 431, win 506, options [nop,nop,TS val 3510018140 ecr 1026978488], length 1328: HTTP
00:00:00.117982 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], seq 12253:13581, ack 431, win 506, options [nop,nop,TS val 3510018140 ecr 1026978488], length 1328: HTTP
00:00:00.151136 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [.], seq 1:1329, ack 431, win 506, options [nop,nop,TS val 3510018173 ecr 1026978488], length 1328: HTTP: HTTP/1.1 200 OK
00:00:00.151163 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], seq 1:1329, ack 431, win 506, options [nop,nop,TS val 3510018173 ecr 1026978488], length 1328: HTTP: HTTP/1.1 200 OK
00:00:00.391056 eth0 In IP 192.168.11.3.80 > 10.0.20.2.44486: Flags [.], seq 1:1329, ack 431, win 506, options [nop,nop,TS val 3510018413 ecr 1026978488], length 1328: HTTP: HTTP/1.1 200 OK
00:00:00.391081 wg0 Out IP 10.0.20.2.80 > 10.1.2.1.44486: Flags [.], seq 1:1329, ack 431, win 506, options [nop,nop,TS val 3510018413 ecr 1026978488], length 1328: HTTP: HTTP/1.1 200 OK
(以下略)
通信ログから分かること
以上を踏まえてリクエスト&レスポンス時に起きていることをまとめると、以下のようになります。
以降で解説しますが、これらの違いは自宅サーバ/別宅サーバの通信環境が影響しています。
最後に、通信できない原因と解決方法について解説します。
原因と解決方法
最後に、原因と解決方法について解説します。
原因
結論としては、VPS(VPN Server)とサーバ(VPN Client)間のMSS(もしくはMTU)の値が、「スマホとVPS」や「サーバが属するLAN内」と比較して小さいことが原因でした。
MTU(Maximum Transmission Unit)
ネットワークで一回に送信できる最大のデータサイズのこと。
https://atmarkit.itmedia.co.jp/aig/06network/mtu.html
MSS(Maximum Segment Size)
インターネットで標準的に用いられるプロトコル(通信規約)であるTCPで通信する際に指定する、一度にデータを受信できる単位(セグメント)の最大長。
https://e-words.jp/w/%E6%9C%80%E5%A4%A7%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88%E3%82%B5%E3%82%A4%E3%82%BA.html
「MSS = MTU - 40」という関係が成り立ちます。
実際、自宅ネットワーク内のサーバにアクセスした際に起きていた現象を図解すると以下のようになります。
LAN内では同じMSS値で通信できるため、わざわざICMPを受信してMSSやフラグメントの処理を見直す仕組みは取り入れていませんでした。
このため、TCP通信に用いるMSS値の更新が行われず通信できない状態となっていました。
解決方法
解決方法としては次の2パターンが考えられますが、フラグメント(≒分割送信)によるスループット低下を避けるため、今回はMSSの設定値を見直す方針としました。
解決方法 | メリット | デメリット |
---|---|---|
①フラグメントを許可する(DF=Falseとして送信する) | アプリケーション側がデータサイズを気にする必要がなくなる。 | IPパケットがフラグメントされるため、スループットが低下する。 |
②MSSの設定値を1240にする | 特定の通信に対するiptablesの設定のみ更新すればよいため、影響範囲を限定できる。 | 最大送信サイズが1280Byte(=1240Byte+40Byte)となるため、大容量通信に時間がかかる。 |
以降では、MSS値の目安の調べ方とiptablesの設定方法について解説します。
MSS値の目安の調べ方
以下のいずれかのパターンに対し、Dockerコンテナ内からpingを送信します。
コマンドを実行するマシン | ping送信先 | 実行コマンド |
---|---|---|
VPSサーバ上のDockerコンテナ | 別宅サーバ上のDockerコンテナ(10.0.10.2) | ping -c 1 -M do -w 2 -s <送信データサイズ> 10.0.10.2 |
VPSサーバ上のDockerコンテナ | 自宅サーバ上のDockerコンテナ(10.0.20.2) | ping -c 1 -M do -w 2 -s <送信データサイズ> 10.0.20.2 |
別宅サーバ上のDockerコンテナ | VPSサーバ上のDockerコンテナ(10.1.2.1) | ping -c 1 -M do -w 2 -s <送信データサイズ> 10.1.2.1 |
自宅サーバ上のDockerコンテナ | VPSサーバ上のDockerコンテナ(10.1.2.1) | ping -c 1 -M do -w 2 -s <送信データサイズ> 10.1.2.1 |
-c 1
:1回だけpingを送信する-M do
:フラグメントを禁止する(DF=Trueとする)-w 2
:タイムアウトを2秒に設定する
VPS上で実行した場合の例を以下に示します。
# 別宅サーバ宛のping(10.0.10.2)
ping -c 1 -M do -w 2 -s 1340 10.0.10.2
PING 10.0.10.2 (10.0.10.2) 1340(1368) bytes of data.
1348 bytes from 10.0.10.2: icmp_seq=1 ttl=64 time=12.8 ms
--- 10.0.10.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms # <- パケットが受信できている
# 自宅サーバ宛のping(10.0.20.2)
ping -c 1 -M do -w 2 -s 1340 10.0.20.2
PING 10.0.20.2 (10.0.20.2) 1340(1368) bytes of data.
--- 10.0.20.2 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1004ms # <- パケットロスが発生している
パケットロスが発生した場合は、送信データサイズを少しずつ小さくしていき、送信できるようになるデータサイズを探索します。
# 自宅サーバ宛のping(10.0.20.2)
ping -c 1 -M do -w 2 -s 1340 10.0.20.2 # -> パケットロスが発生
ping -c 1 -M do -w 2 -s 1339 10.0.20.2 # -> パケットロスが発生
ping -c 1 -M do -w 2 -s 1338 10.0.20.2 # -> パケットロスが発生
# (中略)
ping -c 1 -M do -w 2 -s 1242 10.0.20.2 # -> パケットロスが発生
ping -c 1 -M do -w 2 -s 1241 10.0.20.2 # -> パケットロスが発生
ping -c 1 -M do -w 2 -s 1240 10.0.20.2
PING 10.0.20.2 (10.0.20.2) 1240(1268) bytes of data.
1248 bytes from 10.0.20.2: icmp_seq=1 ttl=64 time=14.3 ms
--- 10.0.20.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms # <- パケットが受信できている
iptablesの設定方法
VPS側と自宅サーバでそれぞれ以下のフォーマットを参考に、iptablesを更新します。
対象 | フォーマット | 備考 |
---|---|---|
VPS | iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -d 10.0.20.0/24 -j TCPMSS --set-mss <調べたMSS値> | サブネットは自身の環境に合わせること |
自宅サーバ | iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o wg0 -j TCPMSS --set-mss <調べたMSS値> | 自宅サーバ側は「-o wg0 」となる点に注意 |
今回の例では、それぞれ以下のように更新すればよいことになりますね。(conf.up.d
側のみ記載)
# 【VPS】conf.up.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -A PREROUTING -p tcp -d 192.168.11.3/32 --dport 80 -j DNAT --to-destination 10.0.20.2:80
# IP masquerade
iptables -t nat -A POSTROUTING -d 10.0.20.0/24 -j MASQUERADE
# ==================
# === 下記を追加 ===
# ==================
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -d 10.0.20.0/24 -j TCPMSS --set-mss 1240
# 【自宅サーバ】conf.up.d/02-routing-home-network.conf
# For Web Server
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.11.3:80
# IP masquerade
iptables -t nat -A POSTROUTING -d 192.168.11.0/24 -j MASQUERADE
# ==================
# === 下記を追加 ===
# ==================
iptables -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN -o wg0 -j TCPMSS --set-mss 1240
まとめ
今回は、MTU値やMSS値の違いにより通信できない原因とその解決策について解説しました。
普段意識することのないパラメータですが、環境の違いに発生しうる現象かつ原因解明がしづらい現象のため、似たようなことで困っている方の助けになれば幸いです。
いずれの場合においても、どこまで上手く動いており、どこでつまづいているかをハッキリさせることが重要になります。
今後もトラブルが発生した段階で解決策をまとめ、記事にしていこうと思います。
素早く環境構築したい方へ
環境構築が比較的容易で、共有サーバよりも、カスタマイズの自由度が高いものを求めている場合、仮想専用サーバ(VPS)を利用することをおすすめします。
詳細は、以下の記事をご覧ください。
【比較】おすすめのVPS 4選
続きを見る