自宅とAzureと出先ノートPCをWireGuardでつなぐ！

## 1. モチベーション

個人的なラボ環境としてAzureを契約した
自宅のネット環境とVPNでS2Sしたくなった
出先だと不便なので、ノートPCでP2Sもできるようにした

---

## 2. 簡易構成図

```plaintext
出先ノート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`
- **自宅WireGuardサーバー**
  - LAN IP: `192.168.40.254`
  - 仮想IP: `10.100.0.2`
- **出先ノートPC**
  - LAN IP: `出先のWIFIで配られたローカルIP`
  - 仮想IP: `10.100.0.150`
- **仮想ネットワーク帯域**
  - `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` を作成

```ini
[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` を作成

```ini
[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クライアントをインストール
- キーペア生成（※参考情報参照）
- 接続用設定ファイル作成

```ini
[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から都度手動で開けてます…

```powershell
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インストール手順

```bash
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
```
---