UFWとNetplanだけでポリシーベースルーティングは出来るか否か？

# A.

:::note info
**割とできます(内容にもよる)。**
:::

## 前提

### 現在のおうちクラウドの問題

新年, 筆者はVultrと自宅サーバーを結ぶ3回線のスループットやレイテンシの問題に頭を悩ませていました.
+ FTTH(マンション共有無料インターネット/JPIX系):3回線中最も太い回線だが**Vultrとのレイテンシが16ms前後(な上に過去に通信先を問わず16~25%ものパケットロスを長期間起こした実績がある)**
+ VDSL(ドコモ光/OCNバーチャルコネクト):3回線中2番目に太い回線だが100Mbpsに律速される上に**VultrとUDP接続するとレイテンシが110msを超える(TCP接続の場合は6msとかだが20Mbpsぐらいしか出ない)**
+ 5G(ホームルーター/楽天モバイル):3回線中最も細い回線で瞬間的には400Mbps出ることもあるが**長時間連続で流れる通信に弱い(帯域が安定しない)**

それぞれに長所と短所があり, MPTCPで回線を束ねてみたり, リンクアグリゲーションに変更してみたりして冗長化を図り, どうにかこうにかVultrとの接続を取ってきました.

https://qiita.com/daisuke_yanase/items/011cacaf45d6d73f091f

https://qiita.com/daisuke_yanase/items/8e71d75a61ca5e411d25

### 現在の接続図

```plantuml
object おうちクラウド
object FTTH_JPIX
object VDSL_OCN
object Rakuten5G
object Vultr

おうちクラウド --> FTTH_JPIX
おうちクラウド --> VDSL_OCN
おうちクラウド --> Rakuten5G
FTTH_JPIX --> Vultr
VDSL_OCN --> Vultr
Rakuten5G --> Vultr
```

が, どうにも現行の構成で開発中の長時間高帯域を要するWebサービスを全てVultrへ寄せて運用するのは難しいという結論に至りました.

:::note warn
+ **Vultrと自宅サーバーの疎通が安定しない(レイテンシが不安定で割と頻繁にリンクアグリゲーションの経路が変わる)**
+ **Vultrと自宅サーバー間の通信がどう足掻いてもFTTHで200Mbpsを超えられない(140Mbpsぐらい/パケロス解消後の回線ポテンシャルから言うともっと出る)**
+ **~~Vultrはドル決済なので昨今の円安ドル高を無視出来なくなってきた~~**
+ **~~Vultrの決済に使ってるカードブランドへの不審~~**
:::

ではOCIはどうやねん, というと,

:::note warn
+ **大体通信の傾向がVultrと同じ(致命的)**
:::

だったりしました.
**~~ていうかOCNのIPv6通信がVultr/OCIとのUDP通信だけレイテンシが遅い現象が無ければこんなことしてないんですがそれは~~**

### 代替VPSの模索

そこで, おうちクラウドへの入り口に出来そうなサービス探しを年末に進めていました。

+ **おうちクラウドとIPv6で接続が取れる**
+ **おうちクラウドとのスループットがVultrクラス(最低でも300Mbps程度)**
+ **おうちクラウドとのレイテンシが25ms以内**
+ **ホストサーバーが大阪周辺~西日本に所在**
+ **お手軽なトンネリングサービスではなく, 隅々まで自分で管理しなくてはならない個人利用可能なVPS or クラウド(※重要)**
+ **出来れば国内企業によって提供**

この難解な条件を満たすサービスを探して行った結果, **「cloud tap」というカゴヤ系クラウドサービス(※カゴヤ・ジャパンのデータセンターはけいはんなにあります)のサーバーサービスが安価(当社比)に使え(知名度低いけど……)て回線速度もVPSの数倍は出る**らしいと言う話を見つけました.

https://ascii.jp/elem/000/004/096/4096864/

VPSと言うよりは, パブリッククラウドをより簡単簡潔に, 小中規模のサービスを必要とする方向けに落とし込んだクラウドサービスですね.

https://www.cloudtap.jp/

料金はVPSよりは割高になりますが, 個人利用も可能だしIPv6アドレスも(明示はないけどマニュアルサイトを見ると)1アドレスだけだけどくれるし, 見つけて仕様適合するのを確認した結果, **「コイツは試してみるっきゃねええええ!!!」** と **テンション爆上がりしました(※未明1時のノリ).**

ただ, **Vultrだと/64ブロックの範囲で自由にIPv6アドレスを使わせてくれてた** ので複数IPv6アドレスをVultrに持たせて, ローカル側にはそのIPv6アドレス毎にデフォルトルートをNetplanで指定してやれば, ポリシーベースルーティングしなくても簡単に3経路使ってWireGuardを張ることが出来たんですが, 今回はIPv6アドレスは1つだけですので, **宛先ポート毎に通信経路を変更(ポリシーベースルーティング)するように設定してやる必要があります.**

### そんなわけで.

今回は **「カゴヤ系クラウドサービス"cloud tap"を使って」**

https://qiita.com/daisuke_yanase/items/454ce8f6e441736ba474

**「過去に一度実装したけど途中で力尽きた(※上掲記事参照)ポリシーベースルーティングの再実装を試み」** ます.

### 今回の目指す構成図(本当はもっと色々繋がっている)

```plantuml
object おうちクラウド
object Rakuten_Bridge
object FTTH_JPIX
object VDSL_OCN
object Rakuten5G
object Vultr
object cloud_tap

おうちクラウド --> FTTH_JPIX
おうちクラウド --> VDSL_OCN
おうちクラウド --> Rakuten_Bridge
FTTH_JPIX --> Vultr
FTTH_JPIX --> cloud_tap
VDSL_OCN --> Vultr
VDSL_OCN --> cloud_tap
Rakuten_Bridge --> Rakuten5G
Rakuten5G --> Vultr
Rakuten5G --> cloud_tap
```

はい, 何か **突然"Rakuten_Bridge"なる謎の接続** が出現したので, cloud tapのアカウント開設作業の前に説明が必要ですね.

:::note info
+ Vultrの場合は **/64のアドレスブロックでIPv6アドレスを配布してくれるので,** 複数のIPv6アドレスをWireGuard接続用にNetplanへ設定してやり, あとは特に難しく考えることなく **"IPv6リンクローカルアドレス(デフォルトゲートウェイ)が確実に固定アドレス"** である2回線のみ, **"VultrのIPv6アドレス1にはFTTHのリンクローカルアドレス, VultrのIPv6アドレス2にはVDSLのリンクローカルアドレス(HGW)" がデフォルトゲートウェイである** という趣旨の設定をしていました
:::

:::note info
+ そうすれば, **"ホームルーターのリンクローカルアドレスが動的に変更されてしまう"** ためNetplanで固定的なアドレスのデフォルトゲートウェイを設定することが出来ないRakuten5G回線については **"DHCPでデフォルトゲートウェイを取得して残りの基本的な全トラフィック全てをRakuten5G回線から流す"** ことになり, 自ずと3回線でそれぞれ別のIPv6アドレスへ向けて自動的に発呼することになるので, ポリシーベースルーティングは不要でした
:::

:::note alert
+ 駄菓子菓子, **cloud tapの場合は前述した通り1インスタンスにつき1IPしか貰えませんので, ポリシーベースルーティングに絡め取れない通信は, 最もレイテンシの小さい回線を経路として接続が図られます(本当はNetplanで最後に固定のデフォルトゲートウェイが設定された回線を使って発呼しているのかも知れませんが, 手元環境上はレイテンシ基準であるように見えています)**
:::

:::note warn
+ このため, Rakuten5G回線もポリシーベースルーティングに組み込むには, おうちクラウドのルーターとRakuten5G回線に接続しているホームルーターとの間に, **"おうちクラウド側のリンクローカルアドレスないしはIPv6ULAアドレスが固定のNAT66(ついでに普通のIPv4もNAT)するルーター"** が必要になります
:::

:::note info
+ 今回は **~~逸般の誤家庭なら一家に1台は転がっている~~ 5500円で入手したNICが2つ付いているWindows10Proの入った超小型デスクトップPC(Windows11に上げることも出来ないスペックなので, 当然ながらサポート期限切れが迫っている)** がありましたので, **躊躇することなくWindows10Proを消し飛ばして** Linuxを入れて **"UFWでNAT/NAPT/NAT66/NAPT66するルーター"** を仕立て上げました
:::

:::note info
+ これにより, **"おうちクラウド(のルーター)側から見えているリンクローカルアドレスまたはULA(デフォルトゲートウェイ)が固定アドレス"** な環境を実現し, Netplanでポリシーベースルーティングを設定することが可能になります
:::

**UFWでNAT66するルーターの作り方については下掲で記事にしておりますので, 本記事では割愛いたします.**

https://qiita.com/daisuke_yanase/items/d84065370acad1991353

## cloud tapアカウント開設

前置きが長くなりましたが, 早速cloud tapのアカウントを開設します.

### ⚠️難しいことは全然ないけど注意⚠️

cloud tapのアカウント開設はVPSやレンタルサーバーを借りたことのある方なら特に難しいことは何もないかと思いますが, 1点だけ注意が必要です.

:::note alert
+ **macOSからChromeを使ってcloud tapのポータルにログインしようとするとエラーになるし, カゴヤアカウント経由でログイン出来てもサーバー作成や認証キー作成で操作エラーになります(※実体験/2025年1月上旬時点).**
:::

この問題を回避するには,

:::note info
+ **macOS + Safariの組み合わせでcloud tapポータルにログインしてください.**
:::

**~~筆者はこの罠に気付くのに1時間費やしました.~~**

## cloud tap側サーバー作成

cloud tapの公式マニュアル通りに作成するだけです.

### レシピ

注意点としては,

:::note warn
+ **先にSSH認証鍵を作成してダウンロードしておきます.**
+ **無料お試し枠の構成(ディスクサイズが80GBのもの)でサーバーを作成すると, 無料お試し枠終了後に月3000円近いインスタンスにジョブチェンジします.**
+ **ディスクサイズのダウングレード(ダウンサイズ)は非対応なので, 最初のディスクサイズの選択を誤ると請求額で爆死します.**
+ **月何円のインスタンスになるかページ下部を参照してきちんと意図した通りのインスタンスになっているか確認しましょう.**
+ **OSイメージは安定性重視のためかあまり最新版のものが用意されていません(何となくUbuntu 24.04 LTSがメモリ512MBでは起動できないせいだと思う/アップグレード自体は手動で出来そう).**
:::

**今回は「Ubuntu Server 22.04 LTS/1vCPU/1GB RAM/20GBディスク/インターネット接続有り(※インスタンスに直接グローバルIP付与)」で作成しました.**

:::note warn
+ **ルーターをインスタンスの前に配置することは可能ですが, 何故かスループットが100Mbpsに律速されたり, IPv6対応の有無の記載がなかったり(多分ないのだと思う), PPTPという古のプロトコルに何故か対応していたりと, 個人的な感想ですが「おいおい大丈夫かこのルーター」という感じなので, あまりおすすめはしません.**
+ **~~というかぶっちゃけサーバーインスタンスを建ててルーターにして, 後方でスイッチ接続した方がスループット出るし, よりセキュアでモダンなプロトコルのVPNが使えるんじゃないかなあ……~~**
+ **謎なのが, ファイアウォールルールで不特定多数のIPからのSSHを通す指定にしていても, 何故か楽天モバイル回線からのSSHを受け付けませんでした(今回はさくらのVPSを経由してSSH接続することで対処).**
:::

### 実速度確認

**実際にどれぐらいの速度が出せるのかを知っておかないと無駄足を踏んでしまいますので**, サーバー作成が完了し一通りアップデートの適用と必要なソフトウェアのインストールが完了したところで, **他のサーバーの邪魔にならなそうな時間帯にみんな大好きspeedtest-cliを走らせました.**

```shell
Selecting best server based on ping...
Hosted by IPA CyberLab 400G (Tokyo) [401.97 km]: 25.234 ms
Testing download speed................................................................................
Download: 275.20 Mbit/s
Testing upload speed......................................................................................................
Upload: 270.72 Mbit/s
```

その他, iperf3もTCP,UDP共に試してみましたが, **大体270Mbps~300Mbpsぐらいで律速する**みたいですね.
なお, NICのリンクスピードは取得出来ませんでした.
**レイテンシはFTTH(JPIX)で12ms, VDSL(OCN)で7ms, 5G(楽天モバイル)で25ms前後でした.**

**速度は「100Mbps以上は欲しいけど1Gbps以上はやり過ぎ」という向きの人にとって, 必要十分なものがあると言って良いでしょう.**

## 実装

サクサクやっていきましょう.

### 必要なソフトをインストール

今回Ubuntu Server 22.04 LTSをOSに選択しましたので, Netplanは元から入っています(UFWも確か入っていたはず). 不足しているソフトウェアはwireguardとopenvswitch-switchだけです.

```shell
sudo apt install wireguard openvswitch-switch
```

### WireGuardの共有鍵を生成

リンクアグリゲーションを行う回線の数だけ共有鍵を生成します(cloud tap側とローカル側の双方で).

```shell
wg genkey > private
mv private private.n.key
cat private.n.key | wg pubkey > public.n.key
```

**鍵を必要数生成したら, 必ずその値と組み合わせの対応を正しくメモしておいてください.**

### Netplan

以前設定したもの

https://qiita.com/daisuke_yanase/items/8e71d75a61ca5e411d25

に「基本的には」順じます.
異なるのはルーター(ローカル)側のIPv6とWireGuardの記述です.
ポート番号やテーブル番号などは適宜自分の環境に置き換えて読んでください.
(※筆者宅のルーターの実際のyamlはmacaddressにマッチさせてset-nameするような設定も入っていますが, ここでは省いています)

```99-netplan-init.yaml
  ethernets:
    eth0: #FTTH/JPIX回線との接続用インターフェイス
      accept-ra: false
      addresses:
      - '<FTTH/JPIX回線のルーターから振り出されたIPv6アドレス>/64'
      dhcp4: true
      dhcp6: false
      mtu: 1460
      nameservers:
        addresses: []
      optional: true
      routes:
      - to: '<cloud tap側が待ち受けているIPv6アドレス>'
        via: '<FTTH/JPIX回線のルーターのリンクローカルアドレス>'
        metric: 2
        table: 200 # <-このインターフェイスに紐付くtable番号
      routing-policy:
      - to: '<cloud tap側が待ち受けているIPv6アドレス>'
        mark: 200
        table: 200
    eth1: #VDSL/OCN回線との接続用インターフェイス
      accept-ra: false
      addresses:
      - '<HGWから振り出されたIPv6アドレス>/64'
      dhcp4: false
      dhcp6: false
      mtu: 1460
      nameservers:
        addresses: []
      optional: true
      routes:
      - to: '<cloud tapが待ち受けているIPv6アドレス>'
        via: '<HGWのリンクローカルアドレス>'
        metric: 2
        table: 201 # <-このインターフェイスに紐付くtable番号
      routing-policy:
      - to: '<cloud tap側が待ち受けているIPv6アドレス>'
        mark: 201
        table: 201
    eth2: #5G/楽天モバイル回線との接続用インターフェイス
      accept-ra: true
      dhcp4: true
      dhcp6: true
      mtu: 1440
      nameservers:
        addresses: []
      optional: true
      routes:
      - to: '<cloud tapが待ち受けているIPv6アドレス>'
        via: '<5G/楽天モバイル回線に接続しているホームルーターとの間に居るブリッジのULA>'
        metric: 2
        table: 202 # <-このインターフェイスに紐付くtable番号
      routing-policy:
      - to: '<cloud tapが待ち受けているIPv6アドレス>'
        mark: 202
        table: 202
  tunnels:
    wireguard1:
      addresses: [ <ルーター側のwireguard1が持つIPアドレス>/30 ]
      key: '<wireguard1のプライベートKey>'
      link: <リンクさせたい回線のインターフェイス名(ここではFTTH/JPIX回線のものとする)>
      local: '<マンションの無料インターネット回線のルーターから振り出されたIPv6アドレス>'
      mark: 200 # <-発呼したいインターフェイスに設定されているtable番号
      mode: wireguard
      mtu: 1330
      optional: true
      peers:
      - allowed-ips: [ <cloud tap側のwireguard1が持つIPアドレス>/30 ]
        endpoint: '[<cloud tapが待ち受けているIPv6アドレス>]:4649'
        keepalive: 25
        keys:
          public: '<wireguard1用にcloud tap側で生成したパブリックKey>'
      port: 4649
    wireguard2:
      addresses: [ <ルーター側のwireguard2が持つIPアドレス>/30 ]
      key: '<wireguard2のプライベートKey>'
      link: <リンクさせたい回線のインターフェイス名(ここではVDSL/OCN回線のものとする)>
      local: '<HGWから振り出されたIPv6アドレス>'
      mark: 201 # <-発呼したいインターフェイスに設定されているtable番号
      mode: wireguard
      mtu: 1330
      optional: true
      peers:
      - allowed-ips: [ <cloud tap側のwireguard2が持つIPアドレス>/30 ]
        endpoint: '[<cloud tapが待ち受けているIPv6アドレス>]:4670'
        keepalive: 25
        keys:
          public: '<wireguard2用にcloud tap側で生成したパブリックKey>'
      port: 4670
    wireguard3:
      addresses: [ <ルーター側のwireguard3が持つIPアドレス>/30 ]
      key: '<wireguard3のプライベートKey>'
      link: <リンクさせたい回線のインターフェイス名(ここでは5G/楽天モバイル回線のものとする)>
      local: '<5G/楽天モバイル回線に接続しているホームルーターとの間に居るブリッジと対向させているULA>'
      mark: 202 # <-発呼したいインターフェイスに設定されているtable番号
      mode: wireguard
      mtu: 1330
      optional: true
      peers:
      - allowed-ips: [ <cloud tap側のwireguard3が持つIPアドレス>/30 ]
        endpoint: '[<cloud tapが待ち受けているIPv6アドレス>]:4671'
        keepalive: 25
        keys:
          public: '<wireguard3用にcloud tap側で生成したパブリックKey>'
      port: 4671
```

**ポリシーベースルーティング用に「table」や「routing-policy」や「mark」などの記述が入っているのが変化です.**

### UFW

以前設定したIPv4用ポリシーベースルーティングの応用になります.
「/etc/ufw/before6.rules」の一番下の「COMMIT」の下に、以下の内容を付け足します(もちろんご自分の環境に合わせてください).

```/etc/ufw/before6.rules
# User Rules
*mangle
-F
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:PREROUTING ACCEPT [0:0]

-A OUTPUT -p udp --sport 4649 -j MARK --set-mark 200
-A POSTROUTING -p udp --sport 4649 -o eth0 -j MARK --set-mark 200
-A PREROUTING -i eth0 -p udp --dport 4649 -j MARK --set-mark 200
-A OUTPUT -p udp --sport 4670 -j MARK --set-mark 201
-A POSTROUTING -p udp --sport 4670 -o eth1 -j MARK --set-mark 201
-A PREROUTING -i eth1 -p udp --dport 4670 -j MARK --set-mark 201
-A OUTPUT -p udp --sport 4671 -j MARK --set-mark 202
-A POSTROUTING -p udp --sport 4671 -o eth2 -j MARK --set-mark 202
-A PREROUTING -i eth2 -p udp --dport 4671 -j MARK --set-mark 202

COMMIT
```

### cloud tap側

:::note warn
+ 対向(cloud tap)側の設定は**特に従前と変わりありません.**
cloud-initで自動生成されているNetplanのyamlファイルを別名(99-netplan-init.yamlなど)にしてコピーし, 適宜WireGuardとBondインターフェイスの設定を行なってください.
:::

### 適用

cloud tap側で先にNetplanを適用してから, 

```shell
sudo netplan try --timeout 20
```

でNetplanの記述が正しいか確認した後, 問題なければ,

```
sudo ufw reload && sudo netplan apply
```

でUFWの設定の再読み込みとNetplanの適用を行います.

### 確認

ローカルルーター側で,

```shell
sudo ip -6 rule show
```

で現在のルーティングテーブルが表示されます.

```shell
0:	from all lookup local
32763:	from all to <cloud tap側のIPv6アドレス> fwmark 0xc8 lookup 200 proto static
32764:	from all to <cloud tap側のIPv6アドレス> fwmark 0xc9 lookup 201 proto static
32765:	from all to <cloud tap側のIPv6アドレス> fwmark 0xca lookup 202 proto static
32766:	from all lookup main
```

例えばこのようにルール・テーブルが追加されていて, cloud tapのインスタンス側で,

```shell
sudo wg
```

して, **WireGuardが3本ともリンクアップしていること, WireGuardの対向側アドレスがちゃんと3本とも別のIPになっていることを確認してください.**
なっていない場合はcloud tap側とローカルルーター側の双方を再起動してみてください.

**成立していた場合は, NetplanにWireGuard内で対向するIPアドレスとの間にVXLANを張って, それをbondインターフェイスに取り込んで, bondインターフェイスを適当なブリッジインターフェイスに取り込む設定を投入して, 好きなIPを設定して自分好みに料理してください.**

## 総括

### ポリシーベースルーティングについて

:::note info
+ **態々スクリプトを組んでルーティングテーブルなどを従前のように追加するスクリプトを組まなくても, NetplanとUFWを適切に設定すればこの2つだけでポリシーベースルーティングを実現出来ることが理解出来た.**
+ **これにより, ポリシーベースルーティングの設定工程を従来手順より幾つか省略出来ることが理解出来た.**
+ **対向IPアドレスが1つしかなくても、発呼ポートと宛先ポートさえズレていればUFWとNetplanだけでデフォルトルートとは違う経路を設定出来ることが理解出来た.**
:::

### cloud tapについて

:::note info
+ **大体250~300Mbps安定して出るようになり、レイテンシも大幅に下がって安定した.**
+ **転送料(転送量)が従量課金ではなくサーバー代に含まれており,(常識的な範囲内で)無制限.**
+ **250~300Mbps程度の回線速度で転送量無制限の安価なサーバーが欲しければ選択肢として大いにアリ.**
**(※例えばConoHa VPSには300Mbpsへの帯域拡大オプションがあるが, 月額9000円台もする)**
+ **一般的なVPSの様にCPU/メモリを求めるとディスク容量も比例して上がっていく様なプランではなく、「CPUとメモリ」と「ディスク容量」は分離されているので柔軟性が高い. ここはちゃんとパブリッククラウド感がある.**
:::

:::note warn
+ **前述した様に, macOS + Safariの組み合わせでないとコンパネにログイン出来なかったり操作に失敗したりする.
ここは改善してほしい.**
+ **セキュリティグループ(Firewall)がアドレス範囲・ポート範囲指定出来るか, 一目見て分かるような作りではない(試していないが多分出来ない?).**
+ **ルーターのVPN機能にPPTPが組み込まれているのは大丈夫なんだろうか……?**
+ **追加ディスクの容量が1TB×3本までであることに留意.**
**それ以上の容量を求めるのであれば複数インスタンスを契約してCephなどで束ねるなどする必要があると思う(多分そこまで行くと他のパブリッククラウドが選択肢になってくると思う).**
+ **元々はカゴヤ・ジャパンのIaaSがコンパネを一新して2022年にリブランドされたものという出自なので, 公になっている様な知見に乏しい感がある(もしかして明示的にcloud tap使ってる記事はこれが初?).**
+ **というか明らかに良いサービスなのに中々宣伝している風でもないのが謎.**
**VPSの方は色々先行知見があるみたいなのに……**
:::