IBM Cloud: VPCで透過的ファイアーウォールを作る方法(2)

# 1. はじめに
前回、[IBM Cloud: VPCで透過的ファイアーウォールを作る方法(1)](https://qiita.com/testnin2/items/24a19ea0800748d578bd)という記事で、透過的ファイアーウォールの構成を示したが、この構成には色々と不満がある。最大の不満はVNFがシングル構成であることだ。これでは、VNF障害の時に通信が止まってしまう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/6c785e08-e50e-c0f5-1e0b-08254ae80d5d.png)


Custom RouteのDestinationとして複数のVNFのIPアドレスを指定することはできるが、その場合はECMPでアクセスされてしまうので、仮に一方のVNFに障害が発生していても割り振り続けてしまう。

以下は、[IBM Cloud docs](https://cloud.ibm.com/docs/vpc?topic=vpc-about-custom-routes)からの引用。

>The implicit router performs equal-cost multi-path (ECMP) routing (multiple routes with the same destination, but different next hop addresses) with the following limitations:
> 
>    - This only applies to routes with an action of deliver.
>    - Only two identical destination routes are permitted per zone, and each must have different next hop addresses.
>    - When ECMP is used, the return path might not take the same path.

というので、VNFに割り振る際には、ヘルスチェックをして正常なVNFにだけ**そのままパケットを転送する**機能が必要となるのだが、それを解決するのが[NLBのroute mode](https://cloud.ibm.com/docs/vpc?topic=vpc-nlb-vnf&interface=ui)だ。これにより、通常のNLBのようなIPアドレス変換が実施されることなく、透過的ファイアーウォールをHA構成で実現できる。


| | NLB（デフォルト） | NLB route mode |
|:-----------|:------------|:------------|
| DNAT     | 実施される （バックエンドサーバーからの戻りにおいては、HypervisorでNLBのIPアドレスを付与することでDirect Server Returnを実現する）       | 実施されない（単にVNFにパケットをアドレス変換なしにそのままフォワードする。）  |
|利用できるプロトコル|TCP/UDP|TCP/UDP([NLB route modeでもICMPはサポートされてない](https://cloud.ibm.com/docs/vpc?topic=vpc-deploy-nlb)）|
|Public or Private|PublicとPrivateを両方サポート|Privateのみサポート|
|Listenするポート番号|- 複数のListenerを作成することで、複数のポートを指定可能。<br>- Publicの時は1つのListnerでポート番号をrange(最小値/最大値）で指定可能。|1つのNLBに対してListnerは1つしか作成できず、`1-65535`というrangeのみが選択可能。|
|バックエンドプールに登録するサーバー|ターゲットサーバー。|典型的にはVNF（VNFでセキュリティーチェックなどを実施した後に、VNFからターゲットサーバーに転送されるような構成にすることが多い）。|
|配置上の制約| - ~~バックエンドサーバーはNLBと同一Zoneに存在する必要がある~~ バックエンドサーバーは、NLBと別Zoneでも可能になりました。<br>- primary intefaceにしか割り振れない。|- バックエンドサーバーはNLBと同一Subnetに存在する必要がある。<br>- 必ずCustom Routeと組み合わせて利用する。<br> - primary intefaceにしか割り振れない。|
|HA構成|単一のIPアドレスが利用可能であり、メンテナンスによってそのIPアドレスが変更されることはない。そのため、アプリケーションではそのIPアドレスを固定して使い続けることが可能である。|Active/StandbyのそれぞれのIPアドレスが割り当てられているが、Custom Routeではnext hopとして現在Activeである方のIPアドレスを指定しておけばよい。[NLBにメンテナンスが発生した場合は、ActiveであるIPアドレスがnext hopになるように自分のVPC内部にあるRoute tableが自動的に更新される](https://cloud.ibm.com/docs/vpc?topic=vpc-config-custom-routes&locale=en)。<br>（元々Custom routeのnext hopではホスト名を指定できないが、逆に言えばこの挙動のためにnext hopでホスト名を指定する必要もない。）|


参考docs
https://cloud.ibm.com/docs/vpc?topic=vpc-nlb-vnf&interface=ui
https://cloud.ibm.com/docs/vpc?topic=vpc-nlb-limitations&interface=ui

これにより、以下のような構成が可能になる。custom routeにおけるdestinationではNLB(route mode)を指定する。

- 往路<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/6546500e-fb33-3471-d669-d47484c43050.png)
- 復路<br> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/e478e637-ad04-2da4-1ed7-af37ec52eb36.png)





# 2. route modeのNLBの作成と構成。

## 2-1. route modeのNLBの注文

IAMで以下のアクセス権を与える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/6b8428a4-3ae8-f44d-b7c5-4be433de945b.png)

NLBの注文画面

- NLBの選択<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/92f18626-de27-eac8-3eca-63cbdd6e315b.png)
- type/routing mode/subnetの選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/7bbdf2b0-4df8-2efb-960a-5b90bca1d7e7.png)
    - Direct Link経由でアクセスするので、TypeはPrivateを選択する（というか、Privateでないと`route mode`は選択できない）。
    - `routing mode`を有効化
    - VNFと同じsubnetを選択
- BackendPoolの構成<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/2cbea1f8-3494-a7fd-285d-d9edced71bd7.png)
    - ヘルスチェックのポート番号は空にしているとメンバーポートでヘルスチェックされてしまうため、今回はSSHポートでヘルスチェックするように22番を指定した（本来はVNFが提供するサービスに対してヘルスチェックした方が良いが、今回VNFとして見立てているサーバーは単にパケット転送を実施するLinuxサーバーにしか過ぎないので、他にヘルスチェックをさせるべきサービスが存在していません）。
    - Session StickinessはNoを選択する（本稿執筆時点では、有効化すると動かないというバグがありました）
- BackendServer(VNF)の追加。<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/68658c62-5483-3b5e-653f-1c5c5c0aef0f.png)<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/a0410f9a-e8b4-019e-07e6-c673cd56228a.png)
    - vnfを2台追加する。
    - ポート番号はここでは80としているが何でも良い（ヘルスチェックには、BackendPoolで設定したHeatlch Checkポートが使われるので。もしそこでHealth Checkポートを定義していないのであれば、このポート番号がヘルスチェックに使われる。）
- FrontEnd Listnerの構成
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/ef11450e-f37d-b171-0cd2-a476449b2570.png)







作成されると、以下のように`Type=Private(Routing)`と表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/b39caafb-bdd8-1473-89a9-7f751b8ed4a0.png)





## 2-2. route modeのNLBの確認

NLBのIPアドレスを控えておく(NLBのIPアドレスは変更されない）

```
$ dig +short xxxxxxxx-jp-tok.lb.appdomain.cloud
172.16.0.13
```

ヘルスチェックも問題ないことを確認。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/cc8cb791-899d-21e0-1539-c7fa8be43cbb.png)

# 2-3. Custom Routeの構成

- ターゲットサーバー宛のパケットがNLBに転送されるように構成<br> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/a85ebf94-deee-1c6f-a086-045cef15f729.png)
- オンプレミス宛のパケットがNLBに転送されるように構成<br>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/b7ce0451-06aa-1437-17d3-566ef02e3544.png)

（なお、NLBに障害が発生した場合は、このCustom Routeが自動的に更新されてfailoverしたNLBのIPアドレスにパケットが届くように更新される。）


# 3. Direct Link経由でのテスト

## 3-1. 80番ポートへのアクセス

```
bash-4.3# curl -I 10.4.0.19
HTTP/1.1 200 OK
Date: Fri, 22 Oct 2021 07:03:32 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips
Last-Modified: Fri, 01 Oct 2021 23:43:55 GMT
ETag: "d-5cd5322eda505"
Accept-Ranges: bytes
Content-Length: 13
Content-Type: text/html; charset=UTF-8
```

2回ずつ同様のパケットが表示されているのは、VNFに入ってくるものと出ていくものをカウントしているから。

```txt:VNFでのtcpdump
06:40:20.978901 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [S], seq 3309025527, win 65535, options [mss 8960,nop,wscale 3,nop,nop,TS val 1638976899 ecr 0], length 0
06:40:20.978961 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [S], seq 3309025527, win 65535, options [mss 8960,nop,wscale 3,nop,nop,TS val 1638976899 ecr 0], length 0
06:40:20.979389 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [S.], seq 686757678, ack 3309025528, win 28960, options [mss 1460,nop,nop,TS val 1752332962 ecr 1638976899,nop,wscale 7], length 0
06:40:20.979398 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [S.], seq 686757678, ack 3309025528, win 28960, options [mss 1460,nop,nop,TS val 1752332962 ecr 1638976899,nop,wscale 7], length 0
06:40:20.980764 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [.], ack 1, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332962], length 0
06:40:20.980769 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [.], ack 1, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332962], length 0
06:40:20.980775 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [P.], seq 1:74, ack 1, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332962], length 73: HTTP: GET / HTTP/1.1
06:40:20.980777 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [P.], seq 1:74, ack 1, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332962], length 73: HTTP: GET / HTTP/1.1
06:40:20.981106 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [.], ack 74, win 227, options [nop,nop,TS val 1752332964 ecr 1638976899], length 0
06:40:20.981111 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [.], ack 74, win 227, options [nop,nop,TS val 1752332964 ecr 1638976899], length 0
06:40:20.981439 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [P.], seq 1:274, ack 74, win 227, options [nop,nop,TS val 1752332964 ecr 1638976899], length 273: HTTP: HTTP/1.1 200 OK
06:40:20.981443 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [P.], seq 1:274, ack 74, win 227, options [nop,nop,TS val 1752332964 ecr 1638976899], length 273: HTTP: HTTP/1.1 200 OK
06:40:20.982996 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [F.], seq 74, ack 274, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332964], length 0
06:40:20.983000 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [F.], seq 74, ack 274, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332964], length 0
06:40:20.983240 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [F.], seq 274, ack 75, win 227, options [nop,nop,TS val 1752332966 ecr 1638976899], length 0
06:40:20.983245 IP 10.4.0.19.80 > 192.168.50.223.51958: Flags [F.], seq 274, ack 75, win 227, options [nop,nop,TS val 1752332966 ecr 1638976899], length 0
06:40:20.984473 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [.], ack 275, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332966], length 0
06:40:20.984477 IP 192.168.50.223.51958 > 10.4.0.19.80: Flags [.], ack 275, win 32761, options [nop,nop,TS val 1638976899 ecr 1752332966], length 0
```

## 3-2. 8080番ポートへのアクセス

```
bash-4.3# curl -I 10.4.0.19:8080
HTTP/1.1 200 OK
Date: Fri, 22 Oct 2021 07:03:58 GMT
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips
Last-Modified: Fri, 01 Oct 2021 23:43:55 GMT
ETag: "d-5cd5322eda505"
Accept-Ranges: bytes
Content-Length: 13
Content-Type: text/html; charset=UTF-8
```

2回ずつ同様のパケットが表示されているのは、VNFに入ってくるものと出ていくものをカウントしているから。

```txt:VNFでのtcpdump
06:59:21.149722 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [S], seq 2644742540, win 65535, options [mss 8960,nop,wscale 3,nop,nop,TS val 1638979180 ecr 0], length 0
06:59:21.149761 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [S], seq 2644742540, win 65535, options [mss 8960,nop,wscale 3,nop,nop,TS val 1638979180 ecr 0], length 0
06:59:21.150182 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [S.], seq 29432477, ack 2644742541, win 28960, options [mss 1460,nop,nop,TS val 1753473141 ecr 1638979180,nop,wscale 7], length 0
06:59:21.150190 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [S.], seq 29432477, ack 2644742541, win 28960, options [mss 1460,nop,nop,TS val 1753473141 ecr 1638979180,nop,wscale 7], length 0
06:59:21.151710 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [.], ack 1, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473141], length 0
06:59:21.151715 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [.], ack 1, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473141], length 0
06:59:21.151721 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [P.], seq 1:79, ack 1, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473141], length 78: HTTP: GET / HTTP/1.1
06:59:21.151723 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [P.], seq 1:79, ack 1, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473141], length 78: HTTP: GET / HTTP/1.1
06:59:21.152086 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [.], ack 79, win 227, options [nop,nop,TS val 1753473143 ecr 1638979180], length 0
06:59:21.152090 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [.], ack 79, win 227, options [nop,nop,TS val 1753473143 ecr 1638979180], length 0
06:59:21.152981 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [P.], seq 1:274, ack 79, win 227, options [nop,nop,TS val 1753473144 ecr 1638979180], length 273: HTTP: HTTP/1.1 200 OK
06:59:21.152986 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [P.], seq 1:274, ack 79, win 227, options [nop,nop,TS val 1753473144 ecr 1638979180], length 273: HTTP: HTTP/1.1 200 OK
06:59:21.154530 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [F.], seq 79, ack 274, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473144], length 0
06:59:21.154534 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [F.], seq 79, ack 274, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473144], length 0
06:59:21.154797 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [F.], seq 274, ack 80, win 227, options [nop,nop,TS val 1753473145 ecr 1638979180], length 0
06:59:21.154801 IP 10.4.0.19.8080 > 192.168.50.223.52218: Flags [F.], seq 274, ack 80, win 227, options [nop,nop,TS val 1753473145 ecr 1638979180], length 0
06:59:21.156246 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [.], ack 275, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473145], length 0
06:59:21.156250 IP 192.168.50.223.52218 > 10.4.0.19.8080: Flags [.], ack 275, win 32761, options [nop,nop,TS val 1638979180 ecr 1753473145], length 0
```

## 3-3. pingでのアクセス

```
bash-4.3# ping 10.4.0.19
（通信できず）
```

VNFにもパケットは現れず。これはNLBがTCPしか対応していないから仕方がない。


## 3-4. UDPでのアクセス。

あれ、UDPでも動いてるけど、、、これは想定とは違う挙動でありバグとのことです。本稿検証時点では機能していますが、今後動かなくなる可能性があります。

```
bash-4.3# nmap -sU 10.4.0.19 -p 30000
```

```txt:VNFでのtcpdump
[root@transfw1 ~]# tcpdump -i any not port 22 -nn
07:12:30.824031 IP 192.168.50.223.64175 > 10.4.0.19.30000: UDP, length 0
07:12:30.824069 IP 192.168.50.223.64175 > 10.4.0.19.30000: UDP, length 0
```