不正アクセスが横行しているので、VPS 上の管理者だけがアクセスするプログラムを VPN 経由に制限することにしました。
お手軽に Tailscale を使っても良かったのですが、知識を深めるために WireGuard を使って自前で構築したので記録しておきます。
環境
- OS:AlmaLinux release 9.8 (Olive Jaguar)
- Python:3.9.25
- ウェブサーバー:OpenLiteSpeed 1.9.4
- ホスティング:ConoHa VPS(メモリ 2GB)
この記事では説明用にサーバーを 10.0.0.1、iPhone を 10.0.0.2 とします。
WireGuard のインストール
dnf install wireguard-tools net-tools git
(out) Dependencies resolved.
(out) =============================================================================================================================================================
(out) Package Architecture Version Repository Size
(out) =============================================================================================================================================================
(out) Installing:
(out) wireguard-tools x86_64 1.0.20210914-4.el9 appstream 113 k
(out) Installing dependencies:
(out) systemd-resolved x86_64 252-67.el9_8.6.alma.1 baseos 344 k
(out)
(out) Transaction Summary
(out) =============================================================================================================================================================
(out) Install 2 Packages
(out)
(out) Total download size: 457 k
(out) Installed size: 1.0 M
(out) Is this ok [y/N]: yカーネルの WireGuard 機能を読み込みます。
modprobe wireguard正常に読み込まれた場合、通常は何も表示されません。続いて、操作ツールを確認します。
wg --version
(out) wireguard-tools v1.0.20210914 - https://git.zx2c4.com/wireguard-tools/設定ファイルの保存先を作成します。
install -d -m 700 /etc/wireguardこれで WireGuard の導入は完了です。VPN のアドレスや、接続端末の設定は後で行います。
ファイアウォールの設定
ファイアウォール(nftables)の設定に関しては過去の記事をご覧ください。

WireGuard のデフォルトポートは UDP 51820 が一般的ですが、以前に設定した TURN サーバー用の UDP 49152 - 65535 とバッティングしているので、今回は UDP 41820 ポートで設定することにします。
ファイアウォール設定(/etc/nftables/filter.nft)の chain UDP に、WireGuard 用のルールを追加します。
table inet filter {
…
省略
…
chain UDP {
# 日本国内にのみ開放
# TURN, TURNS, Janus, WireGuard
udp dport { 3478, 5349, 20000-40000, 41820, 49152-65535 } ip saddr @domestic accept
# 全開放
# HTTP/3
udp dport { 443 } accept
}
}設定の構文チェックをしてから、ファイアウォールの設定を反映します。
nft -c -f /etc/sysconfig/nftables.conf
systemctl reload nftablesWGDashboard のインストール
WGDashboard は、WireGuard の設定や端末管理をブラウザから行うためのアプリです。

Python 3.13 のインストール
AlmaLinux 9 標準の Python のバージョンは 3.9 で、WGDashboard の要件(3.10〜3.13)を満たさないため、EPEL リポジトリから 3.13 をインストールします。
EPEL・CRB が未設定の場合は、有効にします。
dnf install dnf-plugins-core epel-release
dnf config-manager --set-enabled crbdnf install python3.13 python3.13-pip
(out) Dependencies resolved.
(out) =============================================================================================================================================================
(out) Package Architecture Version Repository Size
(out) =============================================================================================================================================================
(out) Installing:
(out) python3.13 x86_64 3.13.16-1.el9 epel 27 k
(out) python3.13-pip noarch 25.1.1-1.el9 epel 2.5 M
(out) Installing dependencies:
(out) mpdecimal x86_64 2.5.1-3.el9 appstream 85 k
(out) python3.13-libs x86_64 3.13.16-1.el9 epel 9.4 M
(out) python3.13-pip-wheel noarch 25.1.1-1.el9 epel 1.2 M
(out)
(out) Transaction Summary
(out) =============================================================================================================================================================
(out) Install 5 Packages
(out)
(out) Total download size: 13 M
(out) Installed size: 55 M
(out) Is this ok [y/N]: yバージョンを確認します。
python3 --version
(out) Python 3.9.25
python3.13 --version
(out) Python 3.13.16通常のシェルでは python 3.9、python3.13 として呼ぶと 3.13 を使う事ができます。
WGDashboard のダウンロード
/usr/share/wgdashboard に配置する事にします。
git clone https://github.com/WGDashboard/WGDashboard.git /usr/share/wgdashboard
(out) Cloning into '/usr/share/wgdashboard'...
(out) remote: Enumerating objects: 20285, done.
(out) remote: Counting objects: 100% (7211/7211), done.
(out) remote: Compressing objects: 100% (886/886), done.
(out) remote: Total 20285 (delta 6751), reused 6325 (delta 6325), pack-reused 13074 (from 3)
(out) Receiving objects: 100% (20285/20285), 56.77 MiB | 29.48 MiB/s, done.
(out) Resolving deltas: 100% (14938/14938), done.Python 3.13 の仮想環境を作成
python3.13 -m venv /usr/share/wgdashboard/src/venv
/usr/share/wgdashboard/src/venv/bin/python3 --version
(out) Python 3.13.16
/usr/share/wgdashboard/src/venv/bin/python3 -m pip --version
(out) pip 26.2.2 from /usr/share/wgdashboard/src/venv/lib64/python3.13/site-packages/pip (python 3.13)WGDashboard をインストール
chmod u+x /usr/share/wgdashboard/src/wgd.sh
env -C /usr/share/wgdashboard/src PATH="/usr/share/wgdashboard/src/venv/bin:$PATH" ./wgd.sh installWireGuard の設定ファイルを保護します。
chmod 700 /etc/wireguard
find /etc/wireguard -type f -name '*.conf' -exec chmod 600 {} +WGDashboard の設定ファイルを作成します。
[Server]
app_ip = 127.0.0.1
app_port = 10086
auth_req = true設定ファイルを root だけが読み書きできる権限にします。
chmod 600 /usr/share/wgdashboard/src/wg-dashboard.ini自動起動設定
サービスに登録して自動起動するように設定します。
[Unit]
Description=WGDashboard
Wants=network-online.target
After=network-online.target nftables.service
ConditionPathIsDirectory=/etc/wireguard
[Service]
Type=forking
User=root
WorkingDirectory=/usr/share/wgdashboard/src
PIDFile=/usr/share/wgdashboard/src/gunicorn.pid
ExecStart=/usr/share/wgdashboard/src/wgd.sh start
ExecStop=/usr/share/wgdashboard/src/wgd.sh stop
Restart=on-failure
RestartSec=5
TimeoutSec=120
PrivateTmp=true
UMask=0077
[Install]
WantedBy=multi-user.targetパーミッションを変更し、サービス定義を読み込み、自動起動を有効にして起動します。
chmod 644 /etc/systemd/system/wg-dashboard.service
systemctl daemon-reload
systemctl enable --now wg-dashboard.service
(out) Created symlink /etc/systemd/system/multi-user.target.wants/wg-dashboard.service → /etc/systemd/system/wg-dashboard.service.WGDashboard が起動しているか、待ち受けポートを確認します。
ss -ltnp 'sport = :10086'
(out) State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
(out) LISTEN 0 2048 127.0.0.1:10086 0.0.0.0:* users:(("gunicorn",pid=12106,fd=18),("gunicorn",pid=12104,fd=18))OpenLiteSpeed の設定
OLS の管理画面にログインし、外部アプリとして WGDashboard を登録します。

- 名前:WGDashboard
- アドレス:
127.0.0.1:10086
バーチャルホストの設定から、リライトを開き Rewrite ルールを追加します。
RewriteCond %{REMOTE_ADDR} ^198\.51\.100\.21$
RewriteRule ^/wg-dashboard/(.*) http://WGDashboard/$1 [P,L]グレースフル再起動をして、OLS の設定を反映します。
WGDashboard の設定
ブラウザで https://(ドメイン名)/wg-dashboard/ にアクセスして、WGDashboard のログイン画面が表示されることを確認してください。
- 初期ユーザー名:
admin - 初期パスワード:
admin
初期ユーザー名と初期パスワードでログインして、管理者用パスワードを変更します。
また、設定メニューで言語を日本語に変更しておくと良いでしょう。
WireGuard 構成
ホーム画面から構成を追加します。
- 構成名:
wg0 - 秘密鍵 & 公開鍵:自動生成されたものを使用
- 待ち受けポート:
41820(通常は51820) - IPアドレス/CIDR:
10.0.0.1/24

一度保存したあと、WireGuard 構成一覧から wg0 を開き、構成設定を開きます。
設定項目が増えて Override Peer Settings が設定できるようになりますので、DNS と Endpoint の設定を追加して保存します。
- DNS:
10.0.0.1 - Endpoint:(VPS のグローバル IP アドレス)

wg コマンドでステータスを確認
WGDashboard で作成した wg0 が正しく反映されているか、ターミナルで確認します。
wg show wg0
(out) interface: wg0
(out) public key: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
(out) private key: (hidden)
(out) listening port: 41820設定ファイルの権限を整えます。
chmod 600 /etc/wireguard/wg0.confwg0 の自動起動を有効にします。
systemctl enable wg-quick@wg0.serviceiPhone 用のピアを追加
再び WGDashboard に戻り、iPhone 用の Peer(接続端末) を作成します。
- ホームの
wg0で「ピアを追加」から以下の内容で追加しました。(DNS と Endpoint Allowed IPs は追加設定を展開すると出てきます。)- 名前:
iPhone - Allowed IPs:
10.0.0.2/32(初期値) - DNS:
10.0.0.1 - Endpoint Allowed IPs:
10.0.0.1/32
- 名前:

- 作成したピアのメニュー(…)から、QR コードのアイコンをクリックします。
- QR コードが表示された状態で、iPhone 側の操作をします。
iPhone の設定
iPhone で App Store から WireGuard アプリをインストールします。

アプリを起動し「トンネルの追加」>「QR コードから作成」で、WGDashboard に表示されたQR コードを読み取ります。
「WireGuard が VPN 構成の追加を求めています」と出たら許可をタップして追加します。
以上で WireGuard の設定は完了です。
DNS の設定
WireGuard 自体には、ドメイン名を VPN 用 IP アドレスに変換する機能はありません。今回のドメインは公開 DNS にサーバーの公開 IP を登録しているため、そのままでは公開 IP に接続します。10.0.0.1/32 だけを VPN の対象にした構成では、この通信は VPN を通りません。
そこで VPS に Unbound を導入し、対象ドメインへの問い合わせに 10.0.0.1 と回答するよう設定します。iPhone の WireGuard 設定では、DNS サーバーに 10.0.0.1 を指定します。
これにより、既存の URL と HTTPS 証明書をそのまま使いながら、対象サーバーへの通信を VPN 経由にできます。公開 DNS の登録内容を変更する必要はありません。
Unbound をインストール
dnf install unbound bind-utils
(out) Package bind-utils-32:9.16.23-40.el9_8.12.x86_64 is already installed.
(out) Dependencies resolved.
(out) =============================================================================================================================================================
(out) Package Architecture Version Repository Size
(out) =============================================================================================================================================================
(out) Installing:
(out) unbound x86_64 1.24.2-3.el9_8.8 appstream 1.0 M
(out) Installing dependencies:
(out) unbound-libs x86_64 1.24.2-3.el9_8.8 appstream 580 k
(out)
(out) Transaction Summary
(out) =============================================================================================================================================================
(out) Install 2 Packages
(out)
(out) Total download size: 1.6 M
(out) Installed size: 5.2 M
(out) Is this ok [y/N]: y設定ファイルで、追加設定を読み込むように変更します。
grep -nE '^[[:space:]]*include(-toplevel)?:' /etc/unbound/unbound.conf
(out) 794: include: /etc/unbound/local.d/*.conf
(out) 1019:include: /etc/unbound/conf.d/*.confWireGuard 用の設定
VPN 用の設定ファイルを作成します。
server:
# DNS の問い合わせを受け付ける IP
interface: 10.0.0.1
# wg0 より先に Unbound が起動しても待ち受け可能にする
ip-freebind: yes
# VPN 内の送信元からの DNS 問い合わせを許可
access-control: 10.0.0.0/24 allow
# ドメインを VPN 用 IP に対応させる
local-zone: "example.com." transparent
local-data: "example.com. 60 IN A 10.0.0.1"
# (サブドメインを個別に指定する場合)
local-zone: "example.net." transparent
local-data: "example.net. 60 IN A 10.0.0.1"
local-data: "www.example.net. 60 IN A 10.0.0.1"
local-data: "subdomain.example.net. 60 IN A 10.0.0.1"
# (サブドメインをまとめて指定する場合)
local-zone: "example.jp." redirect
local-data: "example.jp. 60 IN A 10.0.0.1"
# 上流の証明書検証に使う CA バンドル(DoT する場合のみ)
tls-cert-bundle: /etc/pki/tls/certs/ca-bundle.crt
# その他のドメインは外部 DNS(Cloudflare)に問い合わせる
forward-zone:
name: "."
forward-tls-upstream: yes
# IP@ポート#証明書のホスト名(この名前で証明書を検証する)
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 1.0.0.1@853#cloudflare-dns.com設定を検証して起動
管理用の鍵を準備してから、設定を検証します。
systemctl start unbound-keygen.service
unbound-checkconf
(out) unbound-checkconf: no errors in /etc/unbound/unbound.confUnbound の自動起動を設定して起動します。
systemctl enable --now unbound.service
(out) Created symlink /etc/systemd/system/multi-user.target.wants/unbound.service → /usr/lib/systemd/system/unbound.service.VPS 上で名前解決を確認します。
ip -brief address show wg0
(out) wg0 UNKNOWN 10.0.0.1/24
dig @10.0.0.1 example.com A +short
(out) 10.0.0.1ファイアウォールの VPN 用設定
以下の記事をベースにしたファイアウォール設定に、VPN を追加します。

WireGuard 経由の IP アドレス(10.0.0.0/24)のリストを作成し、DNS 用のポート(TCP 53, UDP 53)を通すように、ファイアウォールの設定(/etc/nftables/filter.nft)を変更します。また、国内向けに開放しているポートにも届くように修正します。
# 現在のルールセットを消去
flush ruleset
# 管理者用 IP アドレスリストを読み込む
include "/etc/nftables/list/admin"
# 日本国内 IP アドレスリストを読み込む
include "/etc/nftables/list/domestic"
table inet filter {
set admin {
type ipv4_addr
elements = $admin
# admin リストを CIDR(10.0.0.0/24)にするなら flags interval を有効にする
#flags interval
}
set domestic {
type ipv4_addr
flags interval
auto-merge
elements = $domestic
}
set vpn {
type ipv4_addr
flags interval
elements = { 10.0.0.0/24 }
}
…
省略
…
chain TCP {
# 管理者にのみ解放
# SSH, OLS 管理画面
tcp dport { 22, 7080 } ip saddr @admin accept
# 日本国内にのみ開放
# SUBMISSION, IMAPS, TURN, TURNS
tcp dport { 587, 993, 3478, 5349 } ip saddr @domestic accept
# WireGuard 経由(DNS:53 + 国内解放のポート)
iifname "wg0" ip saddr @vpn ip daddr 10.0.0.1 tcp dport { 53, 587, 993, 3478, 5349 } counter accept
# 全開放
# SMTP, HTTP, HTTPS
tcp dport { 25, 80, 443 } accept
}
chain UDP {
# 日本国内にのみ開放
# TURN, TURNS, Janus, WireGuard
udp dport { 3478, 5349, 20000-40000, 41820, 49152-65535 } ip saddr @domestic accept
# WireGuard 経由(DNS:53 + 国内解放のポート - WireGuard:41820)
iifname "wg0" ip saddr @vpn ip daddr 10.0.0.1 udp dport { 53, 3478, 5349, 20000-40000, 49152-65535 } counter accept
# 全開放
# http/3
udp dport { 443 } accept
}
}管理者用の IP アドレスリストに、VPN 越しで管理者にする IP アドレスを追加します。
define admin = {
198.51.100.21, # 自宅
203.0.113.1, # 会社
10.0.0.2 # WireGuard 経由の管理者端末
}設定の構文チェックをしてから、ファイアウォールの設定を反映します。
nft -c -f /etc/sysconfig/nftables.conf
systemctl reload nftablesWeb サイトの IP アドレス制限
例えば WordPress の管理画面(/wp-admin)へのアクセス許可リストを、OpenLiteSpeed の静的コンテキストで絞っている場合などは、10.0.0.* に限定する事で VPN 経由のアクセスだけを通すように設定できます。

コメント