1. モチベーション
個人的なラボ環境としてAzureを契約した
自宅のネット環境とVPNでS2Sしたくなった
出先だと不便なので、ノートPCでP2Sもできるようにした
2. 簡易構成図
出先ノートPC ※WireGuardトンネルはインターネット経由で確立
|
(WireGuardトンネル)
|
Azure WireGuardサーバー (ハブ) --- (WireGuardトンネル) --- 自宅WireGuardサーバー
| |
| |
| |
Azure VNET (172.22.0.0/16) 自宅LAN (192.168.40.0/24)
✅ WireGuardについて
特徴 | 内容 |
---|---|
✅ 軽量設計 | コードが非常に少なく、監査が容易。OpenVPNやIPsecよりもシンプル。 |
✅ 高速 | 暗号化処理が効率的で、ネットワークのオーバーヘッドが少ない。 |
✅ セキュア | 最先端の暗号技術(Curve25519、ChaCha20、Poly1305など)を使用。 |
✅ クロスプラットフォーム | Linux, Windows, macOS, Android, iOSなど幅広く対応。 |
✅ 常時接続 | "常に接続されているように振る舞う"動作(モバイル端末にも適する)。 |
3. 設計方針
-
Azure側WireGuardサーバーがハブ
ノートPCと自宅WireGuardサーバーがAzure WireGuardサーバーに接続 -
Azure WireGuardサーバー
- VNET IP:
172.22.1.4
- 仮想IP:
10.100.0.1
- VNET IP:
-
自宅WireGuardサーバー
- LAN IP:
192.168.40.254
- 仮想IP:
10.100.0.2
- LAN IP:
-
出先ノートPC
- LAN IP:
出先のWIFIで配られたローカルIP
- 仮想IP:
10.100.0.150
- LAN IP:
-
仮想ネットワーク帯域
10.100.0.0/24
-
DDNSとAzure Functionsの合わせ技でNSGの動的制御
- Azure Automationだと1時間毎の実行間隔が最小なので、今回は5分毎に実行できるAzure Functionsを採用(従量課金で十分無料枠に収まる範囲)
-
トンネルの出口側のWGサーバーでSNAT(MASQUERADE)をかける
- SNATさせない方法もあるけど、Azure側NSGに192.168.40.0/24に許可設定を入れて回るのが面倒だったので・・・
4. 構築手順
4.1 Azure側WireGuardサーバー準備
- Ubuntu VMを作成(VNET IP:
172.22.1.4
) - WireGuardインストール・キーペア生成(※参考情報参照)
-
/etc/wireguard/wg0.conf
を作成
[Interface]
PrivateKey = <Azureプライベートキー>
Address = 10.100.0.1/24
ListenPort = 51820
PostUp = sysctl -w net.ipv4.ip_forward=1 && iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = sysctl -w net.ipv4-ip_forward=0 && iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <自宅サーバーパブリックキー>
AllowedIPs = 192.168.40.0/24, 10.100.0.2/32
[Peer]
PublicKey = <ノートPCパブリックキー>
AllowedIPs = 10.100.0.150/32
- WireGuard起動・自動起動設定(※参考情報参照)
- NSG設定(UDP 51820ポートを許可)
4.2 自宅側WireGuardサーバー準備
- Ubuntuサーバーを用意(LAN IP:
192.168.40.254
) - WireGuardインストール・キーペア生成(※参考情報参照)
-
/etc/wireguard/wg0.conf
を作成
[Interface]
PrivateKey = <自宅プライベートキー>
Address = 10.100.0.2/24
PostUp = sysctl -w net.ipv4.ip_forward=1 && iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = sysctl -w net.ipv4-ip_forward=0 && iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
PublicKey = <Azureサーバーパブリックキー>
Endpoint = <AzureグローバルIP>:51820
AllowedIPs = 172.22.0.0/16, 10.100.0.1/32
PersistentKeepalive = 25
- WireGuard起動・自動起動設定(※参考情報参照)
- ルーター側のポート開放は不要(アウトバウンド通信のみ)
4.3 出先ノートPC準備
- WireGuardクライアントをインストール
- キーペア生成(※参考情報参照)
- 接続用設定ファイル作成
[Interface]
PrivateKey = <ノートPCプライベートキー>
Address = 10.100.0.150/32
[Peer]
PublicKey = <Azureサーバーパブリックキー>
Endpoint = <AzureグローバルIP>:51820
AllowedIPs = 172.22.0.0/16, 192.168.40.0/24
PersistentKeepalive = 25
4.4 Azure側 UDR設定
- Azure仮想ネットワーク(VNET)にルートテーブルを作成
- プレフィックス:
192.168.40.0/24
- ネクストホップタイプ: Virtual Appliance
- ネクストホップアドレス:
172.22.1.4
(Azure WireGuardサーバーの実IP)
- プレフィックス:
- サブネットにルートテーブルを関連付ける
4.5 自宅側のstatic route設定
ちょっと悩みどころで、172.22.0.0/16
宛の通信は192.168.40.254をGWにするstatic routeを個別の機器ごとに書いてやる必要がある
※WIFIルーター(プロバイダ支給のモデム兼任)はブリッヂモード不可のDHCPも無効にできないし、2重ルーターにするのもなんか嫌だし、良い方法はないだろうか?
4.6 Azure FunctionでNSG制御
Azure側WGサーバーの【パブリックIP】:51820をノーガードで公開するのは危険なので、接続元IPに制限をかける
DDNS登録されたホスト名から自宅側のグローバルIPを取り出し、Azure FunctionでNSGの設定を動的に変更する実装例を提示する
※出先ノートPC用の許可設定はAzurePotalから都度手動で開けてます…
param($Timer)
Write-Output "タイマートリガー実行: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
# コンテキスト取得
$context = (Get-AzContext)
if (-not $context.Subscription.Id) {
$subs = Get-AzSubscription
$firstSub = $subs | Select-Object -First 1
Set-AzContext -SubscriptionId $firstSub.Id
Write-Output "Set-AzContext 実行: SubscriptionId = $($firstSub.Id)"
} else {
Write-Output "既にコンテキストが設定されています: SubscriptionId = $($context.Subscription.Id)"
}
# パラメータ
$resourceGroupName = "rg-wgvpn-edge"
$nsgName = "nsg-wgvpn-edge"
$ruleName = "Allow-Home-DDNS-IP"
$priority = 1000
$port = @("22","51820")
$ddnsHost = "your ddns host name"
# DDNS解決
try {
$resolvedIP = [System.Net.Dns]::GetHostAddresses($ddnsHost) | Where-Object { $_.AddressFamily -eq 'InterNetwork' } | Select-Object -First 1
if (-not $resolvedIP) {
throw "DDNS から IP アドレスの取得に失敗しました: $ddnsHost"
}
$targetIP = $resolvedIP.IPAddressToString
Write-Output "取得した DDNS の IP アドレス: $targetIP"
} catch {
Write-Error "DDNS の名前解決に失敗: $_"
return
}
# NSG取得
$nsg = Get-AzNetworkSecurityGroup -ResourceGroupName $resourceGroupName -Name $nsgName
$existingRule = $nsg.SecurityRules | Where-Object { $_.Name -eq $ruleName }
# 差分チェック
if ($existingRule -and $existingRule.SourceAddressPrefix -eq $targetIP) {
Write-Output "IP アドレスに変更がないため、更新不要です。"
return
}
if ($existingRule) {
Write-Output "既存のルールと IP が異なるため、ルールを削除します。旧IP: $($existingRule.SourceAddressPrefix)"
$nsg.SecurityRules.Remove($existingRule)
} else {
Write-Output "ルールが存在しないため新規作成します。"
}
# 共通:ルール作成
$rule = New-AzNetworkSecurityRuleConfig `
-Name $ruleName `
-Description "Allow Port from Home DDNS IP ($ddnsHost)" `
-Access Allow `
-Protocol * `
-Direction Inbound `
-Priority $priority `
-SourceAddressPrefix $targetIP `
-SourcePortRange * `
-DestinationAddressPrefix * `
-DestinationPortRange $port
$nsg.SecurityRules.Add($rule)
try {
Set-AzNetworkSecurityGroup -NetworkSecurityGroup $nsg
Write-Output "NSGルールを作成または更新しました。"
} catch {
Write-Error "NSGルールの適用に失敗: $_"
}
(参考情報)Ubuntu上でのWireGuardインストール手順
sudo apt update
sudo apt install wireguard
# プライベートキー生成
wg genkey | tee privatekey | wg pubkey > publickey
# 出力
cat privatekey # 秘密鍵
cat publickey # 公開鍵
# /etc/wireguard/wg0.confの作成
sudo vi /etc/wireguard/wg0.conf
sudo chmod 600 /etc/wireguard/wg0.conf
# 手動起動
sudo systemctl start wg-quick@wg0
# 自動起動設定
sudo systemctl enable wg-quick@wg0