AmazonLinux2023(ARM64版)にwireguardを入れて使ってみた

とある野望のために入れてみる。うまくいったらそれも記事にすると思う

WireGuardとは

www.wireguard.com

VPNっぽい感じになるやつ。LinuxKernelのモジュールで提供されており、UDPポートを1個空ければ使えて、設定内容も分かればシンプル。その代わり管理機能が無いのでTailScaleなどで補うとよさそう。

クイックスタートのデモをみるとわかるが、WireGuardでは公開鍵を交換しあうことでお互いの信頼関係を構築している感じ(IDとパスワードの替わり)なので、AはBの公開鍵、BはAの公開鍵を登録しあう必要がある。接続時はどちらかが受ける必要がある(パブリックに公開しないといけない)

最近のLinux Kenel(5.6以降)なら組み込まれているので、その場合はwireguard-toolsを入れればwgコマンドで動く。

公式のquickstartはインタラクティブな感じでちょっと扱いにくい。次のガイドが参考になるかもしれない。

wireguard.how

ArchLinuxのWikiが日本語で読みやすく丁寧

wiki.archlinux.jp

Amazon Linux 2023

AWSでt4g.microインスタンスが無償トライアル中なのでそれで実施する。 普通に金額載ってた。私には無償トライアル分はもう無かった。

バージョン情報

[ssm-user@ip-10-0-0-30 bin]$ uname -a
Linux ip-10-0-0-30.ap-northeast-1.compute.internal 4.14.330-250.540.amzn2.aarch64 #1 SMP Tue Nov 21 09:57:37 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux

[ssm-user@ip-10-0-0-30 bin]$ cat /etc/os-release 
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
SUPPORT_END="2025-06-30"

WireGuardの導入

公式のインストール手順を見てもAmazon Linux 2023のインストール方法はないが、5.6以降のKernelはamazon-linux-extrasで導入ができる。

wireguard-tools(wgコマンド等)はepelで入れることができるのはx64_86のみでaarch64(ARM)は存在しない模様。なのでソースコードから入れる事となる。とはいえwireguard-toolsだけなら単純なので特に問題なくビルドできるだろう。

# Kernelを5.6以上にする(5.15があったのでそれを使う)
sudo amazon-linux-extras install -y kernel-5.15 
# 再起動(kernel-headersなどのバージョンを合わせるためここで実施すること)
sudo reboot

# 以降は再起動後に行う

# 参考: https://www.wireguard.com/compilation/
# 開発ツールなどを導入。
sudo yum install elfutils-libelf-devel kernel-devel pkgconfig "@Development Tools"

# ソースコードの取得
cd ~
git clone https://git.zx2c4.com/wireguard-tools
# ビルド
make -C wireguard-tools/src -j$(nproc)
# インストール
sudo make -C wireguard-tools/src install


# 使わないものの後始末(任意)
# yumでkernel 5.15を入れた時点のIDを確認
sudo yum history
# yumでkernel 5.15を入れた時点のIDを指定してrollback(開発ツールなどを削除する)
sudo yum history rollback ID

インストール確認

$ wg -v
wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/

お試しでWireGuardで繋ぐ

お手元に別のWireGuardを用意しておく(WSL2のUbuntuにいれる、Windowsにいれる等)

VPNセグメントとして 172.30.255.0/24 を使うことにする。これを各PeerでIPを被らないように割り振る想定。

今回は便宜上サーバ・クライアントに分け、クライアントからサーバに繋いで接続を確立できるか試す。(WireGuard的にはただのPeerだが、イメージしにくいので適当に決めているだけ)

サーバ (AmazonLinux)

# 変数
SERVER_IP=172.30.255.1/32
CLIENT_IP=172.30.255.2/32

# 0. 作業ディレクトリを移動
cd ~

# 1. WireGuard用にネットワークデバイス wg0 を作成
sudo ip link add dev wg0 type wireguard

# 2. IPを設定
sudo ip address add dev wg0 "${SERVER_IP}"

# 3. Private Key生成
touch ./private && chmod 0600 ./private && wg genkey > ./private

# 4. private keyを設定
sudo wg set wg0 private-key ./private

# 5. wg0 を Link up
sudo ip link set wg0 up

# 6. Listenポート指定
sudo wg set wg0 listen-port 51280

# 7. Public Keyを確認
sudo wg 

# クライアントの接続を受け付け、クライアント宛ての通信をクライアントに送る
sudo wg set wg0 peer "CLIENT_IPのPublic Key" allowed-ips "${CLIENT_IP}" 

# END

クライアント (Ubuntu on WSL2)

# 変数
SERVER_IP=172.30.255.1/32
CLIENT_IP=172.30.255.2/32
SERVER_HOST=52.0.0.1

# 0. 作業ディレクトリを移動
cd ~

# 1. WireGuard用にネットワークデバイス wg0 を作成
sudo ip link add dev wg0 type wireguard

# 2. IPを設定(IPずらす)
sudo ip address add dev wg0 "${CLIENT_IP}"

# 3. Private Key生成
touch ./private && chmod 0600 ./private && wg genkey > ./private

# 4. private keyを設定
sudo wg set wg0 private-key ./private

# 5. wg0 を Link up
sudo ip link set wg0 up

# 6. Public Keyを確認
sudo wg 

# サーバに繋ぐ
sudo wg set wg0 \
  peer "サーバの公開鍵" \
  allowed-ips "${SERVER_IP}" \
  endpoint "${SERVER_HOST}:51280"

# サーバに通信をしてみる(デフォルトは何か通信が発生しないと接続されない)
ping "${SERVER_IP}"

# 状況を確認。latest handshakeなどが記録される
sudo wg

# END

全てのIPv4の通信をPeerへ中継させたい

  • クライアント側: AllowedIPs で中継させたい宛先を指定する(全部なら0.0.0.0/0を指定すればそうなる)
  • サーバ側: フォワードするようにし、NATする必要がある。

iptablesで行う場合は以下でよい。

# ipv4のフォワード有効化と永続化(デフォルト無効)
sudo sysctl -w net.ipv4.ip_forward=1 | sudo tee -a /etc/sysctl.conf

# iptablesで行う場合、IPマスカレード設定
sudo iptables -t nat -A POSTROUTING -s 送信元CIDR -j MASQUERADE
# 例: sudo iptables -t nat -A POSTROUTING -s 172.30.255.0/24 -j MASQUERADE
# iptablesの変更の永続化(現在の内容をファイルに書き出す)
sudo iptables-save | sudo tee /etc/network/iptables

# iptablesのサービスが入ってないっぽいので入れる
sudo yum install -y iptables-services
sudo systemctl start iptables
sudo systemctl enable iptables

Interfaceのほうは、Peer名前解決するようにするのと、

[Interface]
PrivateKey = 秘密鍵

# IP指定
Address = 172.30.255.3/24

# 適当なDNSサーバ
# (指定しないと無しになってしまうので名前解決ができなくなってしまう)
DNS = 1.1.1.1

[Peer]
PublicKey = Peerの公開鍵

# あらゆる通信をこのPeerにルーティングするようにする
AllowedIPs = 0.0.0.0/0

# 接続確立時の接続先
Endpoint = 接続先PeerのIP:ポート

EC2にルーティングするようにWindowsクライアントで設定してブラウザでパブリックIPを確認した様子。パブリックIPがEC2のモノになっている。

設定の永続化と起動時の自動設定

  • /etc/wireguard/デバイス名.confへ設定ファイルを書く
  • 起動時に設定するよう wg-quickのサービス登録

これをピアの数だけ行う必要があるし、新規で繋げたい場合は繋ぐピア全てに追記が必要(こういうのが煩雑なのでTailScaleがウケている認識)

設定ファイルはwg showconf で現状の内容をを確認できるが、PeerのEndpointなど余計な情報も多い

例えば、A⇔B, A⇔Cの通信だけ行うなら、Aの設定は以下のようになる。

# A: AmazonLinux2023
[Interface]
# Listenポートの固定
ListenPort = 51280
# wg-quick up時に割り当てるIPv4
Address = 172.30.255.1/24
PrivateKey = {秘密鍵}

# B: Ubuntu on WSL2
[Peer]
PublicKey = {Bの公開鍵}
AllowedIPs = 172.30.255.2/32

# C: Windows
[Peer]
PublicKey = {Cの公開鍵}
AllowedIPs = 172.30.255.3/32

自動起動は以下

# 対象のデバイス名を決める
DEVICE_NAME=wg0

# DEVICE_NAME のWireGuardの自動起動実行を有効化
sudo systemctl enable wg-quick@${DEVICE_NAME}

# WireGuard有効: DEVICE_NAME で wg-quick up を実行
sudo systemctl start wg-quick@${DEVICE_NAME}

# WireGuard無効: DEVICE_NAME で wg-quick down を実行
sudo systemctl stop wg-quick@${DEVICE_NAME}

メモ

設定

man wg で設定ファイルについては確認できる。manにないがDNSも指定できるらしい。

[Interface]: 自身の設定

  • PrivateKey: 自身の秘密鍵
  • Address: WireGuardデバイスに割り当てるIPアドレス。wg-quick時にIPを決めたいので設定する
  • DNS:名前解決先。通信をVPNに向ける場合、名前解決できなくなるかもしれない。

[Peer]: 相手の設定

  • PublicKey: 相手(Peer)の公開鍵
  • AllowIps: 許可するIP(CIDR) もしくは ルーティング先(カンマ区切りで複数指定可能)
    • 例えば 0.0.0.0/0 を追加すれば、そのクライアントはそのPeerにルーティングするようになる。すべての通信をそのPeerを経由することになる。

allowd-ipsとは

受信時はどの接続元のIPを許可するか、送信時はある宛先の通信をどのPeerでルーティングするかという設定でもある。名前的に前者だけのイメージに捉えそうだが、ルーティングも兼ねていることに気を付ける。

  • allowed-ipsを設定するとその宛先の通信が そのwireguardデバイスに送られる
  • なので…
    • AはB宛ての通信を送るために allowd-ipsにBのIPを含むように設定しないといけない
    • BもA宛ての通信を送るために allowd-ipsにAのIPを含むように設定しないといけない
  • 同じサブネットで2つ以上指定すると後勝ちになり1つしか指定できない。これはどちらにルーティングすればよいか判別できなくなるため。
    • なのでクライアント側のPeerはサブネットで広い範囲にするのではなく/32で指定する
    • もちろん狙ってやっているならサブネットでもよい。外出先からホームネットワークにつなぎたいな、というときはサブネットで設定することになるはず。

ただ、ルーティングの追加で wg set [デバイス名] allowed-ips "172.30.255.0/24,192.168.99.0/24" としてもルーティングテーブルが書き変わらない(なんで?)

設定を用意してwg-quickを使うとコマンドからルーティングテーブルをいじっていることが分かる。

endpointとは

接続先のIPを指定する。少なくともどちらかはリクエストを受けなければならない。接続する側から通信を始めないとコネクションが確立されないのがデフォルトの挙動

トラブルシューティング

繋がらない

  • EC2のセキュリティグループを確認。独自ポートを使うので明示的に穴あけが必要なはず
  • iptables等でDenyになっていないか?(デフォルトはAllowなので多分該当しない)
  • WireGuardのポート番号はあっているか?
  • tcpdumpでパケットを受け取れているか確認
    • 受け側: sudo tcpdump -vv -X dst port 51280
    • 送る側: echo "hello" | nc -u "${SERVER_HOST}" 51280
    • これでpingを飛ばしたら通信が飛んできているように見える場合、allowed-ipsで拒絶されている可能性があるので確認
  • allowed-ipsがお互い正しく設定されているか?

感想

噂通りシンプルだった。単純なVPNの踏み台にはお手軽そう。AmazonLinux2023はあまり触ったことない上にARM64アーキテクチャも初めてでちょっと悩んだが、わかってしまえばあっさりだった。それ以上に設定の理解で時間が持っていかれた感がある。allowed-ipsにルーティングの意味があるとか

最近ApexLegendsの通信が非常に不安定でラグ0.1秒以上画面が止まるみたいなことが頻発してゲームにならなかった。(これ通信のせいじゃないと思うかもしれないけど、感覚的に多分通信が悪い)そしてこのWireGuardを介したら致命的なラグは起きにくくなった感触がある。

あとは以下が辛そうなのでTailScaleを試すかもしれない。

  • IPが変わってしまう環境をどうするか
  • 設定辛そう