WireGuardにサーバー/クライアントという概念は無いが、実運用では事実上サーバーなのでそのように呼称。
IPv4を想定する。v6は知らん。
目次
- 前提
- パッケージインストール
- IPフォワーディング設定
- iptables設定
- キーペア作成
- サーバー用設定ファイル作成
- WireGuardインターフェース起動
- クライアント側設定ファイル作成
- AllowedIPsについて
前提
今回は以下のようなネットワーク構成として話を進めるので、適宜読み替えて。
ルーターのポートフォワーディング設定などは完了しているものとする。
| LAN側 ネットワークアドレス | 192.168.100.0/24 |
| サーバー LAN側IPアドレス | 192.168.100.100 |
| サーバー ネットワークデバイス名 | ens192 |
| 管理用PC IPアドレス | 192.168.100.1 |
| サーバー WireGuard側IPアドレス | 192.168.200.100 |
| サーバー WireGuard待ち受けポート | 55555 |
| クライアント1 WireGuard側IPアドレス | 192.168.200.1 |
| クライアント2 WireGuard側IPアドレス | 192.168.200.2 |
なお、感覚的に変な感じはするがWireGuard用のネットワークアドレスみたいなことは実は考える必要がない。後述するがAllowedIPsに記述されたルールに従って動くので、例えばサーバー側192.168.200.1でクライアント側10.0.0.1などの割り当てでも問題がない。
パッケージインストール
ファイアウォールとかNATとかそのへんはiptablesを使用し、iptables-persistentで永続化するものとする。他に使い慣れているやつがあるならそれで問題ない。
qrencodeについては後述するが、別に入れなくてもいい。
iptables-persistentを入れる際に、今のiptables設定を保存するか聞かれる。そもそも入れたてなので設定も何もないのだが、rules.v4およびrules.v6ファイルをどうせ作る必要があるためYesを選択する。
# apt install wireguard-tools iptables iptables-persistent qrencodeIPフォワーディング設定
IPフォワーディングがデフォルトで無効のため、これを有効化する。
/etc/sysctl.d/以下に任意の名称で.confファイルを作って設定を読み込ませる。ここでは 10-ipforward.conf とする。
# vi /etc/sysctl.d/10-ipforward.conf
+=====[10-ipforward.conf]=====+
net.ipv4.ip_forward=1
+=============================+
# sysctl -piptables設定
iptables-persistentに任せるので、rules.v4にルールを書いておく。
例えばこんな感じ。
# vi /etc/iptables/rules.v4
+=====[rules.v4]=====+
*filter
# デフォルトのポリシーを設定
# 受信と転送は破棄、送信は許可
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# 確立している通信の戻りを許可
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# ローカルループバックを許可
-A INPUT -i lo -j ACCEPT
# 不正なループバックを破棄
-A INPUT -d 127.0.0.0/8 ! -i lo -j DROP
# WireGuard待ち受けポート宛への通信を許可
-A INPUT -p udp -m udp --dport 55555 -j ACCEPT
# 管理用PCからの全てのアクセスを許可
-A INPUT -s 192.168.100.1/32 -j ACCEPT
# 192.168.0.0/16からのICMPを許可
-A INPUT -s 192.168.0.0/16 -p icmp -j ACCEPT
# 192.168.100.0/24からのSSHの新規接続を許可
-A INPUT -s 192.168.100.0/24 -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
COMMIT
+====================+このルールを読み込んで適用する。
ミスって繋がらなくなったりしたらコンソールからflushするとiptablesのルールを全クリアできる。rules.v4の記述が消えるわけではない。
# netfilter-persistent reload
~~~ 消したいとき ~~~
# netfilter-persistent flushキーペア作成
まずサーバー側のキーペアを作成する。WireGuardに便利コマンドがあるのでそれを使う。
ついでにパイプでteeに渡しファイルに保存しておく。今後このファイルを使うことは無いが、忘れるとダルいため。
# mkdir /etc/wireguard/server_key
# cd /etc/wireguard/server_key
~~~ 秘密鍵の作成 ~~~
# wg genkey | tee server.key
AAAAAAAAAAAAAAA=
~~~ 公開鍵の作成 ~~~
# cat server.key | wg pubkey | tee server.pub
XXXXXXXXXXXXXXX=クライアント用のキーペアをクライアントの数だけ作成する。
事前共有鍵は任意なので無くても良い。
# mkdir /etc/wireguard/client_key
# cd /etc/wireguard/client_key
~~~ 秘密鍵の作成 ~~~
# wg genkey | tee client_01.key
BBBBBBBBBBBBBBB=
~~~ 公開鍵の作成 ~~~
# cat client_01.key | wg pubkey | tee client_01.pub
YYYYYYYYYYYYYYY=
~~~ 事前共有鍵の作成 ~~~
# wg genpsk | tee client_01_PSK.key
PPPPPPPPPPPPPPPP=サーバー用設定ファイル作成
まずconfファイルの記述ルールについて説明する。
[Interface]
PrivateKey = サーバーの秘密鍵
Address = サーバーのWireGuard側IPアドレス CIDR表記だがマスクを省略しても構わない(その場合/32扱い)
ListenPort = 待ち受けポート番号
# コメント行
PostUp = インターフェース起動時に実行するコマンド; だいたい追加のファイアウォールルールなどを書く; セミコロン区切りで複数可
PostDown = インターフェース終了時に実行するコマンド; だいたい追加したルールを消すのに使う
[Peer]
PublicKey = クライアントの公開鍵
PresharedKey = 事前共有鍵 必須ではない サーバーとクライアントで同じキーを記述
AllowedIPs = クライアントの割り当てIP CIDR表記
[Peer]
# クライアントが複数存在する場合は同様にしてPeerセクションを増やしていく
PublicKey = クライアントの公開鍵
AllowedIPs = クライアントの割り当てIP CIDR表記
...ファイル名がそのままインターフェース名になる。慣例的にwg0とする。
今回の場合は以下のようになる。
# vi /etc/wireguard/wg0.conf
+=====[wg0.conf]=====+
[Interface]
PrivateKey = AAAAAAAAAAAAAAA=
Address = 192.168.200.100
ListenPort = 55555
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o $(ip -4 route show default | awk '{print $5}') -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o $(ip -4 route show default | awk '{print $5}') -j MASQUERADE
[Peer]
# クライアント1
PublicKey = YYYYYYYYYYYYYYY=
PresharedKey = PPPPPPPPPPPPPPPP=
AllowedIPs = 192.168.200.1/32
[Peer]
# クライアント2
PublicKey = ZZZZZZZZZZZZZZZ=
AllowedIPs = 192.168.200.2/32
+====================+なおPostUpに書かれているルールはセミコロンで区切られた以下の3コマンドで、PostDownはiptablesからこれらの追加したルールを削除するもの。
iptables -A FORWARD -i %i -j ACCEPT
%iは自身のインターフェース名(wg0)に置き換えられる。
wg0インターフェースから入ってきたパケットを他インターフェースへ転送すること許可する。
iptables -A FORWARD -o %i -j ACCEPT
他インターフェースから入ってきたパケットをwg0インターフェースへ転送すること許可する。
iptables -t nat -A POSTROUTING -o $(ip -4 route show default | awk '{print $5}') -j MASQUERADE
$(ip -4 route show default | awk '{print $5}')はデフォルトルートが設定されているインターフェース(ens192)に置き換えられる。
パケットの送信元IPアドレスをサーバー(ens192)のIPに書き換える(NAPT)。WireGuardインターフェース起動
systemctlから wg-quick@インターフェース名 で制御できる。
# systemctl enable wg-quick@wg0
# systemctl start wg-quick@wg0
~~~ ピアの接続状況の表示 ~~~
# wg showwg-quickコマンドで直接実行も可能。
なおsystemctlの場合もサービスとして管理しているだけで、内部的には同じ。
# wg-quick up wg0
# wg-quick down wg0クライアント側設定ファイル作成
クライアント用の設定ファイルであるためサーバに置いておく必要は特に無く、管理用PCなどで書いてもいいしクライアント端末で直接書いてもいい。
サーバー用と若干異なるためこちらの記述ルールについても説明する。
[Interface]
PrivateKey = クライアントの秘密鍵
Address = クライアントのWireGuard側IPアドレス CIDR表記だがマスクを省略しても構わない
DNS = DNSサーバーの指定 必須ではないが書いておいたほうが良い
[Peer]
PublicKey = サーバーの公開鍵
PresharedKey = 事前共有鍵 必須ではない サーバーとクライアントで同じキーを記述
Endpoint = サーバーのIPアドレス ドメイン表記も可
AllowedIPs = ここに合致したトラフィックをWireGuard側に流す
PersistentKeepalive = キープアライブ設定 必須ではないが推奨よって今回の場合、クライアント1のconfファイルは以下のようになる。
DNSサーバーは指定しなければ元々の設定を使うが、全通信をWireGuard経由とする場合において元々設定されているDNSサーバーがローカルIPだった場合は見つからなくなり名前解決が出来なくなる。そのため明示的に指定することを推奨する。
キープアライブはWireGuard公式の推奨値である25秒を設定する。
+=====[client_01.conf]=====+
[Interface]
PrivateKey = BBBBBBBBBBBBBBB=
Address = 192.168.200.1
DNS = 1.1.1.1
[Peer]
PublicKey = XXXXXXXXXXXXXXX=
PresharedKey = PPPPPPPPPPPPPPPP=
Endpoint = server.domain.name:55555
AllowedIPs = 0.0.0.0/1,128.0.0.0/1
PersistentKeepalive = 25
+==========================+confファイルをサーバーで作成しており、かつqrencodeを入れていて、クライアントがスマートフォンの場合、QRコードに変換することで素早く設定できる。
~~~ コンソールに表示する場合 ~~~
# qrencode -t ansiutf8 -r client_01.conf
~~~ 画像ファイルに出力する場合 ~~~
# qrencode -o filename.png -r client_01.confあとはクライアント側のインターフェースを有効にすれば繋がるはず。
AllowedIPsについて
WireGuardの一番重要なところ。
サーバー側/クライアント側問わず、これの実際の意味は「通信の宛先がここに書かれたIPアドレス範囲に合致した場合のみWireGuardトンネルへ流す」というもので、ここの記載通りにルーティングテーブルが書き換えられる。またこの影響でWireGuardインターフェースの割り当てが32bitのホストマスクで指定されていたり、まるで異なるネットワークアドレスだったとしても問題なくなる。
管理が煩雑になるので実際にはそんなことはしないと思うが、サーバー側192.168.200.1でクライアント側10.0.0.1だった場合を考えてみる。
恐らくサーバー側では「AllowedIPs=10.0.0.1/32」と記載されていて、ルーティングとしては10.0.0.1宛は全てwg0に投げろと設定され、WireGuardトンネルの先に10.0.0.1が存在する。
クライアント側では「AllowedIPs=0.0.0.0/1,128.0.0.0/1」と記載されていたとして、全ての通信がWireGuardトンネルを経由し、その先に192.168.200.1が存在する。
よって相互に宛先を見つけることができ、問題なく繋がる。なぜならWireGuardはP2Pで接続されるシステムだから。
とすればサーバー側のAllowedIPs指定がクライアントのWireGuard側IP単体である理由も単純明快で、サーバー側から見ればクライアントから更に先へ行く通信は存在せず、全てクライアントに送るだけでいいからということがわかる。
クライアント側では少々複雑だが、理解してしまえば難しくない。
全通信を対象とするなら0.0.0.0/0で済むし、実際この設定をしても問題ない(場合が多い)が、これはデフォルトルートの書き換えと同義となりあまりよろしくない。0.0.0.0/1,128.0.0.0/1と指定した場合、これは範囲指定としては全く同じではあるものの、ロンゲストマッチのルールにより本来のデフォルトルートを残したままこちらの設定が優先されることになるのでより安全である。
お気づきかと思うが、「全通信をWireGuard経由でしたいが、例外的にローカルネットワークの192.168.0.0/16宛と172.16.0.0/16宛だけはWireGuardに流さず直接端末のインターフェースから送出したい」といったような柔軟な制御も可能である。例えばこの場合は
AllowedIPs = 0.0.0.0/1, 128.0.0.0/3, 160.0.0.0/5, 168.0.0.0/6, 172.0.0.0/12, 172.17.0.0/16, 172.18.0.0/15, 172.20.0.0/14, 172.24.0.0/13, 172.32.0.0/11, 172.64.0.0/10, 172.128.0.0/9, 173.0.0.0/8, 174.0.0.0/7, 176.0.0.0/4, 192.0.0.0/9, 192.128.0.0/11, 192.160.0.0/13, 192.169.0.0/16, 192.170.0.0/15, 192.172.0.0/14, 192.176.0.0/12, 192.192.0.0/10, 193.0.0.0/8, 194.0.0.0/7, 196.0.0.0/6, 200.0.0.0/5, 208.0.0.0/4, 224.0.0.0/3となるが、手動計算するにはダルすぎるのでWireGuard AllowedIPs Calculatorなどを使うと便利。