Cloudflare Tunnel (cloudflared) に Tunnel Virtual Networks を使ったルーティング設定を行う

# Cloudflare Tunnel (cloudflared) に同じ IP ルートを定義しようとすると
怒られます。

ただ `Create private network for default network tunnel` とあるように

`default network` でない場合には定義が可能です。

そこで [Tunnel Virtual Networks](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/private-net/tunnel-virtual-networks/) を使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/3d1ed9f1-a092-7087-0eb0-0c33d4891c28.png)

# Tunnel Virtual Networks とは
要は、`default network` 以外にも複数の `Virtual Networks` を作成して、IP 重複があったとしても `Virtual Networks` として分かれていれば、選択して利用できます。

ただし2023/05/23時点では、ダッシュボード UI からは `default network` に対してしか設定できないため、API で設定する方法を書きます。

- [Tunnel Virtual Networks · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/private-net/tunnel-virtual-networks/)
> Cloudflare Tunnelは、仮想ネットワークの作成と設定をサポートしています。Tunnel Virtual Networksを使用すると、オーバーラップするIP範囲を持つ異なるプライベートネットワークを管理できます。
>たとえば、組織が「本番」と「ステージング」と考えている2つの異なる仮想プライベートクラウド（VPC）ネットワークを公開したい場合があります。ただし、2つのプライベートネットワークが同じRFC 1918 IP割り当てを受けた場合、同じIPアドレスを持つ2つの異なるリソースがある可能性があります。 2つの別々の仮想ネットワークを作成することにより、10.128.0.1 / 32 stagingおよび10.128.0.1 / 32 productionなど重複するプライバシーアドレスにトラフィックを決定的にルーティングできます。エンド ユーザーはWARP クライアント設定にアクセスして接続する ネット ワー ク を選択します。

# Tunnel Virtual Networks の確認

- [List virtual networks · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-virtual-network-list-virtual-networks)

```shell:
export EMAIL='YOUR_EMAIL'
export APIKEY='YOUR_APIKEY'
export ACCOUNT_ID='YOUR_ACCOUNT_ID'
```

```shell:
curl -s --request GET \
  --url https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/virtual_networks \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" | jq
```

```json:result.json
{
  "success": true,
  "messages": [],
  "errors": [],
  "result": [
    {
      "id": "604aa865-1642-405c-a745-3d448f69b8f1",
      "account_id": 00000000,
      "comment": "This network was autogenerated because this account lacked a default one",
      "name": "default",
      "is_default_network": true,
      "created_at": "2021-11-17T09:40:27.640842Z",
      "deleted_at": null
    },
    {
      "id": "ffe4c560-0a28-4253-808b-c327cfccf962",
      "account_id": 00000000,
      "comment": "Staging Virtual Network",
      "name": "kyouhei-virtual-network",
      "is_default_network": false,
      "created_at": "2023-04-20T14:32:01.436116Z",
      "deleted_at": null
    }
  ]
}
```

# Tunnel Virtual Networks の作成

- [Create a virtual network · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-virtual-network-create-a-virtual-network)

```shell:
curl -s --request POST \
  --url https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/virtual_networks \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" \
  --data '{
  "comment": "Staging VPC for data science",
  "is_default": false,
  "name": "us-east-1-vpc"
}'  | jq
```

```json:result.json
{
  "success": true,
  "messages": [],
  "errors": [],
  "result": {
    "id": "615c2f89-8084-4475-a40c-9e5542e3f3fa",
    "account_id": 00000000,
    "comment": "Staging VPC for data science",
    "name": "us-east-1-vpc",
    "is_default_network": false,
    "created_at": "2023-05-23T03:29:35.474147Z",
    "deleted_at": null
  }
}
```

# 【参考】 Tunnel Virtual Networks の削除
削除された `Virtual Networks` には `deleted_at` に値が入ります。

- [Delete a virtual network · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-virtual-network-delete-a-virtual-network)

```shell:
export VNET_ID='YOUR_VNET_ID'

curl -s --request DELETE \
  --url https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/virtual_networks/$VNET_ID \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" | jq
```

```json:result.json
{
  "success": true,
  "messages": [],
  "errors": [],
  "result": {
    "id": "615c2f89-8084-4475-a40c-9e5542e3f3fa",
    "account_id": 00000000,
    "comment": "Staging VPC for data science",
    "name": "us-east-1-vpc",
    "is_default_network": false,
    "created_at": "2023-05-23T03:29:35.474147Z",
    "deleted_at": "2023-05-23T03:32:54.727897Z"
  }
}
```

# Cloudflare Tunnel (cloudflared) の作成
以下の手順に沿って作成します。

- [Set up a tunnel through the dashboard · Cloudflare Zero Trust docs](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/remote/#3-connect-a-network)

# Tunnel ID の取得
作成した Cloudflare Tunnel (cloudflared) の Tunnel ID を取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/a920b854-8865-ac67-5e0f-684f15d49d4f.png)

# Tunnel Virtual Networks で Tunnel Routes を作成
`10.146.0.0/20` を作成したい場合は `10.146.0.0%2F20` のように URL エンコードした値を使います。

- [Create a tunnel route · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-route-create-a-tunnel-route)

```shell:
export TUN_ID='YOUR_TUN_ID'
export VNET_ID='YOUR_VNET_ID'
```

```shell:
curl -s --request POST \
  --url "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/routes/network/10.146.0.0%2F20" \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" \
  --data '{
  "tunnel_id":"'$TUN_ID'",
  "virtual_network_id":"'$VNET_ID'"
}' | jq
```

# Tunnel Virtual Networks で Tunnel Routes を確認
- [List tunnel routes · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-route-list-tunnel-routes)

```shell:
curl -s --request GET \
  --url "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/routes" \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" | jq '.result[] | select (.virtual_network_id == "'$VNET_ID'") | select (.deleted_at == null)'
```

```json:result.json
{
  "network": "10.146.0.0/20",
  "tunnel_id": "71ba0fd1-8678-4620-8de5-43444a7e4c13",
  "comment": "",
  "created_at": "2023-05-23T03:07:56.923139Z",
  "deleted_at": null,
  "virtual_network_id": "ffe4c560-0a28-4253-808b-c327cfccf962",
  "tunnel_name": "tunnel virtual network"
}
```

# 【参考】 Tunnel Virtual Networks で Tunnel Routes を削除
API で削除する必要があります。

- [Delete a tunnel route · Cloudflare API Documentation](https://developers.cloudflare.com/api/operations/tunnel-route-delete-a-tunnel-route)

`cloudflared` の実装によると、クエリパラメータで `virtual_network_id` を渡してあげたところ、削除できました。

- [cloudflared/ip_route.go at cb9725781517c142a3dc7ba3c7d0d5645ef5f7fa · cloudflare/cloudflared · GitHub](https://github.com/cloudflare/cloudflared/blob/cb9725781517c142a3dc7ba3c7d0d5645ef5f7fa/cfapi/ip_route.go)

```shell:
curl -s --request DELETE \
  --url "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/teamnet/routes/network/10.146.0.0%2F20?virtual_network_id=$VNET_ID" \
  --header 'Content-Type: application/json' \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $APIKEY" | jq
```

```json:result.json
{
  "success": true,
  "messages": [],
  "errors": [],
  "result": {
    "network": "10.146.0.0/20",
    "tunnel_id": "71ba0fd1-8678-4620-8de5-43444a7e4c13",
    "comment": "",
    "created_at": "2023-05-23T04:34:29.895735Z",
    "deleted_at": "2023-05-23T04:35:03.872589Z",
    "virtual_network_id": "ffe4c560-0a28-4253-808b-c327cfccf962"
  }
}
```

# WARP クライアントで接続する Tunnel Virtual Networks を選択
同じ宛先の IP だったとしても `Virtual Networks` として分かれていれば、選択して利用できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101361/9c5e2f21-3506-4b5c-9bbc-27f57b80abd9.png)