WireGuardによる踏み台用VPNサーバーの構築手順と運用における工夫

はじめに

以前、IDCFクラウド上で踏み台用SoftEther VPNサーバーを構築する手順を書きました。

・SoftEtherによる踏み台用VPNサーバーの構築手順 on IDCFクラウド CentOS 7
https://inaba-serverdesign.jp/blog/20170817/softether_vpn_idcf_cloud_centos7.html

・SoftEtherによる踏み台用VPNサーバーの構築手順 on IDCFクラウド ~VPN接続時の通信速度を改善
https://inaba-serverdesign.jp/blog/20241111/softether-vpn-idcf-cloud-speedup.html

ここでいう「踏み台」とは、動的IPアドレスのクライアントから、アクセス元IPアドレスが限定されているサーバー・サービスにアクセスするための「踏み台(リレー)」という意味です。

今回は同様の「踏み台用VPNサーバー」として、SoftEtherとは別のVPNソフトウェア WireGuard を試してみました。

WireGuardは、近年注目を集めているオープンソースのVPNプロトコル・実装で、以下のような特徴があります。

  • SoftEther, OpenVPNやIPsecと比べて、設定がシンプル。
  • UDP通信を前提とした設計で、オーバーヘッドが少ないため、高速。
  • Linuxカーネルモジュールとして標準搭載されている。

今回構築するVPNサーバー、クライアントの構成は以下のとおりです。

  • クラウドサーバー: AWS EC2 t3.micro
  • OS: AlmaLinux 9
  • サーバーホスト名: wg-test
  • VPNサーバー: WireGuard
  • VPN接続ポート: UDP/51820
  • VPNクライアントOS: Windows 11
  • VPNプライベートネットワーク: 172.16.0.0/24

VPNサーバー、クライアントの大まかな構成イメージは以下のようになります。

以下は、2026年7月時点の構築手順です。

また、WireGuardの設定については、以下の記事などを参考にしました。ありがとうございます。

WireGuard VPNの構築

WireGuardのインストール

AlmaLinux 9(RHEL 9)では、WireGuardはすでにカーネルモジュールとして組み込み済みです。
以下のコマンドで確認できます。

 # modinfo wireguard

filename:       /lib/modules/5.14.0-687.20.1.el9_8.x86_64/kernel/drivers/net/wireguard/wireguard.ko.xz
alias:          net-pf-16-proto-16-family-wireguard
alias:          rtnl-link-wireguard
version:        1.0.0
author:         Jason A. Donenfeld <Jason@zx2c4.com>
description:    WireGuard secure network tunnel
license:        GPL v2
rhelversion:    9.8

↑WireGuardモジュールが最初から含まれていることがわかります。

次に、管理ツール wireguard-tools をインストールします。
EPELリポジトリを有効にして、インストールします。

 # dnf --enablerepo=epel install wireguard-tools

...

インストール済み:
  systemd-resolved-252-67.el9_8.4.alma.1.x86_64
  wireguard-tools-1.0.20210914-4.el9.x86_64

インストールされた主なファイルを確認します。

 # rpm -ql wireguard-tools

...

/etc/wireguard
/usr/bin/wg
/usr/bin/wg-quick
...
/usr/lib/systemd/system/wg-quick@.service

wg と wg-quick コマンド、および wg-quick@.service という systemd サービステンプレートがインストールされました。

WireGuardクライアントの準備

サーバーの設定を進める前に、クライアント側でも準備を行います。

WireGuard公式サイト より、Windows用WireGuardをダウンロードしてインストールします。

インストール後、クライアントを起動して「トンネルの追加」→「空のトンネルを追加」を選択します。

接続名(ここでは wg-test とします)を入力して「保存」をクリックすると、クライアント側の秘密鍵と公開鍵が生成されます。

表示される公開鍵はサーバー側の設定で使用するため、控えておきます。

WireGuard サーバーの鍵生成

サーバー側の秘密鍵と公開鍵を生成します。

 # wg genkey | tee /etc/wireguard/$HOSTNAME.private.key |\
   wg pubkey > /etc/wireguard/$HOSTNAME.public.key

鍵ファイルのパーミッションを設定します。

 # chmod 600 /etc/wireguard/$HOSTNAME.private.key \
   /etc/wireguard/$HOSTNAME.public.key

 # ls -l /etc/wireguard/

-rw------- 1 root root 45  7月  9 14:53 wg-test.private.key
-rw------- 1 root root 45  7月  9 14:53 wg-test.public.key

秘密鍵と公開鍵の内容を確認し、それぞれ控えておきます。

 # cat /etc/wireguard/$HOSTNAME.private.key

...

 # cat /etc/wireguard/$HOSTNAME.public.key

...

WireGuard サーバーの設定(wg0.conf)と起動

wg-quick サービスを使用する方法でWireGuardを設定します。

今回は、VPN用プライベートネットワークは 172.16.0.0/24 とします。

 # vim /etc/wireguard/wg0.conf
[Interface]
# VPNプライベートネットワーク上の、WireGuardサーバーのプライベートアドレス
Address = 172.16.0.1/24
# WireGuardの公開ポート
ListenPort = 51820
# WireGuardサーバーの秘密鍵
PrivateKey = <サーバーの秘密鍵>

[Peer]
# クライアントAの公開鍵
PublicKey = <クライアントAの公開鍵>
# クライアントAの接続時に割り当てるVPNプライベートネットワーク上のIPアドレス
AllowedIPs = 172.16.0.11

補足します。

[Interface] ブロックで、WireGuard VPNサーバー側の設定を行います。

[Peer] ブロックでは、VPNクライアント側の設定を行います。
PublicKey で、先ほどWindows 11のクライアント側で作成した公開鍵を指定します。
ポイントとしては、WireGuardはOpenVPNやIPsecのように、サーバーがクライアントへIPアドレスを動的に払い出すしくみを持っていません。
代わりに、AllowedIPs でクライアントごとにVPNプライベートネットワーク内の静的IPアドレスを指定します。

※AllowedIPs は、厳密には、「PublicKeyで指定した公開鍵を使用したVPN接続を許可する、VPNプライベートネットワーク上のIPアドレス」という意味だと思うのですが、クライアント側の [Interface] ブロックの Address で指定するIPアドレスをここに記載します。
サーバー側の AllowedIPs と クライアント側の Address で同じIPアドレスを設定するということで、「割り当てる」といってよいと思います。

クライアントが複数の場合は、[Peer] ブロックを追加していきます。

[Peer]
# クライアントAの公開鍵
PublicKey = <クライアントAの公開鍵>
# クライアントAの接続時に割り当てるVPNプライベートネットワーク上のIPアドレス
AllowedIPs = 172.16.0.11

[Peer]
# クライアントBの公開鍵
PublicKey = <クライアントBの公開鍵>
# クライアントBの接続時に割り当てるVPNプライベートネットワーク上のIPアドレス
AllowedIPs = 172.16.0.12

設定ができたら、wg-quickサービスを起動します。

 # systemctl start wg-quick@wg0

 # systemctl status wg-quick@wg0 | less

● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0
     Loaded: loaded (/usr/lib/systemd/system/wg-quick@.service; disabled; preset: disabled)
     Active: active (exited) since Thu 2026-07-09 14:56:19 JST; 6s ago
...
 7月 09 14:56:19 wg-test wg-quick[5533]: [#] ip link add wg0 type wireguard
 7月 09 14:56:19 wg-test wg-quick[5533]: [#] wg setconf wg0 /dev/fd/63
 7月 09 14:56:19 wg-test wg-quick[5533]: [#] ip -4 address add 172.16.0.1/24 dev wg0
 7月 09 14:56:19 wg-test wg-quick[5533]: [#] ip link set mtu 8921 up dev wg0
 7月 09 14:56:19 wg-test systemd[1]: Finished WireGuard via wg-quick(8) for wg0.

‘ip link add wg0 type wireguard’ というログからわかるとおり、デーモンプロセスではなく、Linuxカーネルのネットワークインタフェース wg0 が作成されます。

wg0デバイスのインタフェース設定を確認します。

 # wg show wg0

interface: wg0
  public key: <サーバーの公開鍵>
  private key: (hidden)
  listening port: 51820

peer: <クライアントの公開鍵>
  allowed ips: 172.16.0.11/32

 # ip address show wg0

3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8921 qdisc noqueue state UNKNOWN group default qlen 1000
    link/none
    inet 172.16.0.1/24 scope global wg0
       valid_lft forever preferred_lft forever

VPN接続用のファイアウォール設定

サーバー上位ネットワークのファイアウォール設定

AWSのセキュリティグループ、IDCFクラウドのファイアウォール、さくらのクラウドのパケットフィルタなど、サーバー上位ネットワークのファイアウォールがある場合は、ファイアウォールでWireGuard接続用のポートのアクセス許可を追加します。

  • WireGuard接続用: UDP/51820

サーバー内ファイアウォールの設定

サーバー内部で nftables, firewalld, iptables等のファイアウォールを使用している場合は、設定を追加します。
僕は nftables が好きなので、ここでは nftables の設定を記載します。

WireGuard VPNに関して、以下を追加します。

  • INPUTチェーンで、UDP/51820 へのアクセス許可ルール
  • (VPN接続時にクライアントからSSH接続を許可する場合のみ)INPUTチェーンで、VPN接続時のSSHアクセス許可ルール
  • FOWARDチェーン
  • NATテーブル

VPNクライアントがサーバー経由でインターネットへ通信できるよう、nftablesにFORWARDとNATの設定を追加するのがポイントです。

 # vim /etc/sysconfig/nftables.conf
table inet filter {

  chain INPUT {

...

    iifname "wg0" tcp dport 22 counter accept # SSH on WireGuard

    ct state new udp dport 51820 ip saddr counter accept # WireGuard VPN

  }

  chain FORWARD {
    type filter hook forward priority 0; policy drop;

    ct state established,related counter accept

    iif "wg0" oif "eth0" counter accept
  }
}

...

table ip nat {

    chain POSTROUTING {
        type nat hook postrouting priority 100;

        oif "eth0" ip saddr 172.16.0.0/24 masquerade
    }
}

...

補足します。

    iifname "wg0" tcp dport 22 counter accept # SSH on WireGuard

VPN接続時にクライアントからのSSH接続を許可するルールです。
これだと、VPNクライアントすべてからのSSH接続が可能となりますが、特定のクライアントのみに限定したい場合は、ソースIPアドレス(VPNで割り当てるIPアドレス)を指定します。

    iifname "wg0" tcp dport 22 ip saddr 172.16.0.11 counter accept # SSH on WireGuard

FORWARDチェーンで wg0 から eth0 へのパケットを許可し、POSTROUTING のNATテーブルで、VPNネットワーク(172.16.0.0/24)からインターネットへの通信をサーバーのIPアドレスに変換(マスカレード)します。
これがないと、インターネット側から返ってくるパケットの宛先がわからなくなります

nftablesをreloadして、設定を反映し、ルールセットを確認します。

 # systemctl reload nftables

 # nft list ruleset | less

カーネルのIPフォワード設定

VPNクライアントのパケットをサーバーが転送(ルーティング)できるよう、カーネルのIPフォワードを有効にします。

 # vim /etc/sysctl.conf
net.ipv4.ip_forward = 1
 # sysctl -p

一時的に設定するだけなら、以下のコマンドで設定します。

 # sysctl -w net.ipv4.ip_forward=1

WireGuardクライアントの設定

先ほど作成したWindowsクライアントのトンネル設定 wg-test を編集します。
「トンネルの編集」をクリックして、以下のように設定を追加します。

[Interface]
# クライアントの秘密鍵(自動生成済み)
PrivateKey = <クライアントの秘密鍵>
# クライアントに割り当てるVPNプライベートネットワーク上のIPアドレス
Address = 172.16.0.11/24
# WireGuardにはDNS配布機能がないため、DNSサーバーを指定する
DNS = 1.1.1.1

[Peer]
# WireGuardサーバーの公開鍵
PublicKey = <サーバーの公開鍵>
# スプリットトンネル。VPN経由でルーティングするIPアドレス
AllowedIPs = 0.0.0.0/0
# WireGuardサーバーのパブリックIPとポート
Endpoint = <サーバーのグローバルIPアドレス>:51820

補足します。

[Interface] ブロックで、クライアント側の設定を行います。
Address で指定するIPアドレスが、自身のVPNプライベートネットワーク内の静的IPアドレス割り当てとなります。
WireGuardにはDNS配布機能がないため、DNSでDNS名前解決サーバーを指定します。
ここでは、Google Public DNSの 1.1.1.1 を指定しています。

[Peer] ブロックでは、WireGuard VPNサーバー側の設定を行います。
PublicKey で、先ほどサーバー側で作成した公開鍵を指定します。
AllowedIPs は、「VPN接続を許可するIPアドレス」という意味でしょうが、実質的にはスプリットトンネル(VPN経由とするIPアドレス)の設定となります。
AllowedIPs = 0.0.0.0/0 とすると、すべての通信がVPN経由となります(フルトンネル)。
メンテナンス用の踏み台として使う場合はこの設定が一般的です。
特定のIPアドレスのみVPN経由とする場合は、VPNサブネットの 172.16.0.0/16 と、対象とするグローバルIPアドレスをカンマ区切りで指定します。

VPNサーバーの設定と、クライアント側の設定を見比べると、[Interface] で自身の設定を、[Peer] で対向するVPN接続相手の設定を、それぞれ行っていることがよくわかります。

トンネル設定を記述して「保存」をクリックします。

VPN接続の確認

VPN接続の実行

クライアントからVPN接続するときは、トンネルを「有効化」します。

VPN接続が確立されると、「インタフェース」の「状態」が「有効」となります。
また、「ピア」の「転送」で、「受信済み」「送信済み」の転送量の数値が増えていきます。

※「状態」が「有効」と表示されていても、「受信済み」が「0」のままのときはVPN接続がうまくいっていないので、サーバーとクライアントの設定を見直します。

クライアント側の確認

Windowsのコマンドプロンプトで ipconfig を実行し、VPN用のIPアドレスが付与されていることを確認します。

> ipconfig

...

不明なアダプター wg-test:

   接続固有の DNS サフィックス . . . . .:
   リンクローカル IPv6 アドレス. . . . .: fe80::xxxx:xxxx:xxxx:xxxx
   IPv4 アドレス . . . . . . . . . . . .: 172.16.0.11
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 0.0.0.0

VPNプライベートネットワーク内のIPアドレス 172.16.0.11 が付与されていることが確認できます。

Webブラウザで、インターネット上のWebサイトにアクセスし、VPN接続していないときと同じようにページが表示されることを確認します。
また、IPinfo ip のように、アクセス元IPアドレスを表示するページにアクセスして、表示されるIPアドレスがVPNサーバーのグローバルIPアドレスと一致することを確認しましょう。

一致していれば、意図したとおり踏み台用VPNサーバーとして動作しています。

通信速度の確認

通信速度を確認するため、FAST にアクセスしてみました。
通常時は 300Mbps 程度のところ、VPN接続時は100Mbps 程度を確認しました。
VPN接続時も、実用上十分な速度が出ています。

ちなみに、以前SoftEther高速化のブログ記事を書いたときと同じIDCFクラウドのサーバー(S1.Light, 1vCPU, メモリ1GB)では、120Mbps を記録し、SoftEhterと同じぐらいでした。
ただ、レイテンシが大きいのか、Webサイトのページ表示は、AWS EC2でVPNサーバー構築をしたときよりも、もっさりしていました。

サーバー側の確認

サーバー側のコマンドでも、接続を確認します。
WireGuard経由でSSH接続する場合は、ホスト名として、サーバーのVPNプライベートネットワーク内のサーバーIPアドレス(今回は 172.16.0.1)を指定して接続します。

サーバー上で wg show コマンドを実行すると、接続中のピア情報を確認できます。

 # wg show

interface: wg0
  public key: <サーバーの秘密鍵>
  private key: (hidden)
  listening port: 51820

peer: <クライアントの公開鍵>
  endpoint: <クライアントのグローバルIPアドレス>:46473
  allowed ips: 172.16.0.11/32
  latest handshake: 53 seconds ago
  transfer: 152.59 MiB received, 161.47 MiB sent

endpoint にクライアントの接続元IPアドレス
latest handshake に最終通信時刻
transfer に転送量
が表示されます。

以上で、WireGuard VPNサーバーの構築はひとまず完了です。

SoftEtherに比べると、作業ステップがかなり少なく、とても簡単ですね。
SoftEtherのようなGUI管理マネージャーは存在しないので、管理機能への不正アクセス対策を考慮しなくてもよいのも、長所といえます。

運用における工夫

ここまで書いたとおり、WireGuard VPNはとてもシンプルな設計、構成のため、サーバーの構築はとても簡単です。
ただ、シンプルさを重要視した設計のせいか、機能が少ないため、セキュリティ的には、以下のような気になる点があります。

  • クライアント側の設定と鍵情報の受け渡し。
  • 鍵にパスフレーズがない。
  • カーネルやファイアウォールでIPフォワードを有効にすること。
  • ログ出力がない。
  • wg-quickサービスの起動・停止コマンドが覚えにくい。

クライアントのConfigにサーバーの公開鍵情報を書き込む、というのは、エンジニアでないとやや難易度が高いように思います。
また、クライアントの公開鍵をサーバー管理者に伝える際に、誤って秘密鍵が書かれたConfigをそのまま送ってしまう可能性があります。

クライアントのConfigが漏洩すると、秘密鍵が記載されており、鍵のパスフレーズがないため、比較的容易に不正アクセスできてしまうのが怖いところです。
漏洩に気づいた時点で、すぐにサーバー側でそのクライアントのPeer設定を削除できればよいのですが、漏洩に気づかないときが怖い。。。
パスフレーズなしSSH鍵での運用と同じようなことだけど、それがどれぐらいのリスクか。。

ということで、僕は、WireGuard VPNサーバーの実運用では、以下のような対策をとります。
(SoftEther VPNのセキュリティ対策と同じような内容です。)

  1. ファイアウォールでVPN接続元IPアドレスを日本国内に限定する。
  2. wg-quickサービスはVPN接続時のみ起動し、そのときのみカーネルやファイアウォールを追加設定する。(VPN接続が必要なタイミングが少ないため)
  3. 公開Webサーバーやメールサーバーと併用しない。
  4. 接続ログを残す。
  5. wg-quick サービス起動・停止の簡略化。

ファイアウォールでVPN接続元IPアドレスを日本国内に限定

アクセス元を日本国内に限定すれば絶対安全、というわけでもないのですが、不正アクセスのアクセス元は、海外のほうが圧倒的に多いです。
不正アクセスを減らすため、iptables や nftables等のサーバー内ファイアウォールでアクセス元IPアドレスを日本国内に限定します。

例えば、以下などを参考に、VPN接続ポート UDP/51820 へのアクセス元IPアドレスを、日本国内のIPアドレスに限定しましょう。

(参考)
・ipsetとiptablesでSSHを日本国内からの接続に限定する (CentOS 7)
https://inaba-serverdesign.jp/blog/20150209/ipset_iptables_country_centos7.html

・nftablesでSSHを日本国内からの接続に限定する (CentOS 8)
https://inaba-serverdesign.jp/blog/20191219/nftables-country-ipaddress-centos8.html

wg-quickサービスは、VPN接続時のみ起動し、そのときのみカーネルやファイアウォールを追加設定する

通常時。

  • wg-quickサービスは停止しておく。
  • カーネルのIPフォワード設定を無効。
  • ファイアウォールで、WireGuard VPNに必要な設定はしない。UDP/51820ポートを塞ぎ、FOWARDチェーンNATテーブルは設定しない。

wg-quickの起動時に以下を実施。

  • カーネルのIPフォワード設定を有効化。
  • ファイアウォールで、WireGuard VPNに必要な設定を追加。

wg-quickの停止時には、通常時に戻します。

カーネルのIPフォワード設定の有効無効

カーネルのIPフォワードを有効にするとき、無効にするときのコマンドは、それぞれ以下となります。

 # sysctl -w net.ipv4.ip_forward=1 // IPフォワード有効化
 # sysctl -w net.ipv4.ip_forward=0 // IPフォワード有効化

ファイアウォールの切り替え

以下は、ひとつのアイデアですが、例えば、nftablesの設定を、通常時用とWireGuard VPN用の2ファイルを用意して、切り替えます。

・通常時用: /etc/sysconfig/nftables-normal.conf
・WireGuard VPN用: /etc/sysconfig/nftables-normal.conf

systemd nftablesサービスでは、/etc/sysconfig/nftables.conf を参照するため、シンボリックリンクで切り替え、サービスのreloadで反映します。

通常時。

 # ln -s -f /etc/sysconfig/nftables-normal.conf /etc/sysconfig/nftables.conf

WireGuard VPN起動時。

 # ln -s -f /etc/sysconfig/nftables-wg.conf /etc/sysconfig/nftables.conf

また、AWSのセキュリティグループなど、クラウドのファイアウォールを使用しているのであれば、CLI/APIを使用して、ファイアウォールルールを切り替えるのもよいでしょう。

WireGuard起動、停止時のカーネルパラメータ、nftables切り替え対応

wg-quick 設定の PostUp, PreDown で、WireGuardの起動後や停止前の実行コマンドを指定できるため、先ほどのIPフォワード有効無効コマンドや、nftablesの切り替えコマンドを記載します。

(参考)
・wg-quick(8) – Linux manual page
https://man7.org/linux/man-pages/man8/wg-quick.8.html

 # vim /etc/wireguard/wg0.conf
# VPNプライベートネットワーク上の、WireGuardサーバーのプライベートアドレス
Address = 172.16.0.1/24
# WireGuardの公開ポート
ListenPort = 51820
# WireGuardサーバーの秘密鍵
PrivateKey = (サーバーの秘密鍵)

# wg0インタフェースが起動したあとに実行するコマンド
PostUp = sysctl -w net.ipv4.ip_forward=1
PostUp = ln -s -f /etc/sysconfig/nftables-wg.conf /etc/sysconfig/nftables.conf
PostUp = systemctl reload nftables

# wg0インタフェースが停止する前に実行するコマンド
PreDown = ln -s -f /etc/sysconfig/nftables-normal.conf /etc/sysconfig/nftables.conf
PreDown = systemctl reload nftables
PreDown = sysctl -w net.ipv4.ip_forward=0

[Peer]
# クライアントAの公開鍵
PublicKey = (クライアントの公開鍵)
# クライアントAの接続時に割り当てるVPNプライベートネットワーク上のIPアドレス
AllowedIPs = 172.16.0.11
[Peer]
...

nftablesのルールは、wg0インタフェースの設定を含むので、PreUpではなく、WireGuard起動後の PostUp で指定するのがポイントです。

以上のような設定で、VPN接続時のみ必要なカーネルパラメータ設定やファイアウォール設定を切り替えます。

接続ログの保存

WireGuard自体にはログ出力機能がありません。
そのため、wg show コマンドのステータス情報をもとに、接続ログをファイルに出力するスクリプトを作成し、cronで定期実行します。

ユーザーを特定しやすいよう、サーバー側の wg0.conf のクライアントごとの各 [Peer] セクションにコメントで FriendlyName を記載しておきます。

 # vim /etc/wireguard/wg0.conf
...

# FriendlyName: testuser1
[Peer]
PublicKey = <クライアントtestuser1の公開鍵>
AllowedIPs = 172.16.0.11

# FriendlyName: testuser2
[Peer]
PublicKey = <クライアントtestuser2の公開鍵>
AllowedIPs = 172.16.0.12

ログ出力スクリプトを作成します。

 # vim /root/bin/wg-logger.sh
#!/bin/bash

# 設定(インタフェース名と設定ファイルのパス、ログ出力先)
INTERFACE="wg0"
CONF_FILE="/etc/wireguard/${INTERFACE}.conf"
LOG_FILE="/var/log/wireguard_access.log"

TMP_FILE="/tmp/wg_status.tmp"

# 現在のステータスを取得
wg show "$INTERFACE" dump > "$TMP_FILE"

while read -r line; do
    [[ -z "$line" ]] && continue
    [[ "$line" =~ ^[A-Za-z0-9+/=]+[[:space:]]+[A-Za-z0-9+/=]+ ]] && continue

    PUBKEY=$(echo "$line" | awk '{print $1}')
    REMOTE_IP=$(echo "$line" | awk '{print $3}')
    INTERNAL_IP=$(echo "$line" | awk '{print $4}')
    LAST_HANDSHAKE=$(echo "$line" | awk '{print $5}')
    TX_BYTES=$(echo "$line" | awk '{print $6}')
    RX_BYTES=$(echo "$line" | awk '{print $7}')

    # まだ一度もハンドシェイクしていないクライアントはスキップ
    if [ "$LAST_HANDSHAKE" -eq 0 ]; then
        continue
    fi

    # wg0.conf から対応する PublicKey の直前にある FriendlyName を取得
    CLIENT_NAME=$(grep -B 5 "$PUBKEY" "$CONF_FILE" | grep "# FriendlyName:" | awk -F': ' '{print $2}' | tr -d ' \r\n')

    if [ -z "$CLIENT_NAME" ]; then
        CLIENT_NAME="Unknown"
    fi

    HANDSHAKE_TIME=$(date -d "@$LAST_HANDSHAKE" "+%Y-%m-%d %H:%M:%S")

    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [CONN] Name: $CLIENT_NAME | Remote: $REMOTE_IP | Internal: $INTERNAL_IP | LastHandshake: $HANDSHAKE_TIME | Tx: ${TX_BYTES}B | Rx: ${RX_BYTES}B" >> "$LOG_FILE"

done < "$TMP_FILE"

rm -f "$TMP_FILE"
--

スクリプトに実行権限を付与します。

 # chmod 700 /root/bin/wg-logger.sh

cronに登録して、5分ごとに実行します。
(もちろん、systemd timerでも構いません。)

 # crontab -e
# WireGuard Logger
*/5 * * * * /root/bin/wg-logger.sh >/dev/null 2>&1

logrotateでログローテーションを設定します。

 # vim /etc/logrotate.d/wireguard
/var/log/wireguard_access.log {
    monthly
    missingok
    rotate 24
    compress
    delaycompress
    notifempty
    create 0640 root root
}

ログの出力例は以下のとおりです。

[2026-07-09 15:40:01] [CONN] Name: testuser1 | Remote: <接続元IPアドレス>:46473 | Internal: 172.16.0.11/32 | LastHandshake: 2026-07-09 15:20:39 | Tx: 160659596B | Rx: 172120656B
[2026-07-09 16:00:01] [CONN] Name: testuser2 | Remote: <接続元IPアドレス>:25991 | Internal: 172.16.0.12/32 | LastHandshake: 2026-07-09 15:57:37 | Tx: 464184B | Rx: 726360B

各項目の意味は以下のとおりです。

  • Name: 接続しているクライアントのFriendlyName
  • Remote: クライアントの接続元IPアドレス(WAN側IP)
  • Internal: クライアントのVPN内IPアドレス
  • LastHandshake: 最後に通信が行われた時刻
  • Tx / Rx: その時点までの累計データ転送量

wg-quick サービス起動・停止の簡略化

(セキュリティ対策とは関係ありませんが)
wg-quick@wg0 というサービス名は(僕には)覚えにくいので、シェル関数を定義して、wbsコマンドで起動・停止できるよう簡略化してみました。

一般ユーザーの場合は sudo を使う形で定義します。

 $ vim ~/.bashrc
# WireGuard wg-quick
wgs() {
    sudo systemctl "$1" wg-quick@wg0
}

rootユーザーの場合はそのまま。

 # vim ~/.bashrc
# WireGuard wg-quick
wgs() {
    systemctl "$1" wg-quick@wg0
}

設定を反映し、動作確認します。

 # source ~/.bashrc

 # wgs start

 # wgs status

OS起動時に自動起動させる場合は enable します。

 # systemctl enable wg-quick@wg0

まとめ

インターネット上のサーバーをメンテナンスするための「固定IPアドレスをもつ踏み台サーバー」として、AWS EC2上の AlmaLinux 9 に、WireGuardによる踏み台用VPNサーバーを構築する手順と、実際に運用する際の工夫やセキュリティ対策について記載しました。

SoftEther VPNと比較したWireGuardの印象は以下のとおりです。

WireGuardの良い点。

  • RHEL 9系ではカーネル組み込み済みのため、追加のビルド作業が不要。
  • 設定ファイルがシンプルで、コマンドも少なく理解しやすく、構築がとても簡単。
  • UDP通信のため、通信速度も良好(FASTによる計測で100Mbps〜120Mbps)。

WireGuardの注意点。

  • サーバーからクライアントへのIPアドレス動的割り当て機能(DHCP相当)がなく、サーバー側の AllowedIPs や、クライアント側の Address で静的に管理する必要がある。
  • AllowedIPs パラメータの意味は、少しわかりにくい。
  • ログ出力機能がない。

踏み台VPNとしての用途では、SoftEther VPN同様に、十分実用的であることが確認できました。
シンプルな設定を好む方には、WireGuardも良い選択肢となるでしょう。

ただし、VPNサーバー自体に不正アクセスをされてしまっては本末転倒です。
以下のようなセキュリティ対策を実施するとよいでしょう。

  1. ファイアウォールでVPN接続元IPアドレスを制限する(国内限定など)。
  2. クライアントの公開鍵を適切に管理し、退職・離脱者の鍵、Peer設定はすぐに削除する。
  3. (VPN接続が必要なタイミングが少なければ)WireGuardサービスは必要なときのみ起動する。
  4. 自前でスクリプトを用意して接続ログを残す。

僕は、当面(ずっと?)、SoftEther VPNの代わりに、WireGuard VPNを使用してみます。

なお、WireGuardを使用したオープンソースVPNソフトウェア HeadScale は、管理機能を備えており、認証やログといった上記の注意点は改善されそうです。
機会があれば、HeadScale も試してみようと思います。

(参考)
・tailscaleのオンプレ版headscaleをつかってみる
https://qiita.com/matsu3365/items/ff62dbe456f1d4de8de2

・HeadScale × HeadPlane による自前Tailscale互換サーバを立てるための本
https://zenn.dev/yuron3141/books/4bb83096011ecc

タイトルとURLをコピーしました