IBM Cloud: CIS Rangeを利用したFTP接続構成

#1. はじめに

インターネット経由でftp接続をするのであれば、通信が暗号化されてActive mode/Passive modeなども意識する必要がないsftpやscpを利用するべきだと思うが、（暗号化されていなくてもいいので）どうしてもftpを利用してCIS Range(= Cloudflare Spectrum)経由で接続したいというケースのために検証した。
なお、Originサーバーは、IBM Cloud上のVPCサーバーを利用。

以下は注意点

- [Cloudflare社のdocs](https://developers.cloudflare.com/spectrum/ftp)によるとactive modeはサポートされておらず、passive modeを利用する必要がある（そもそもFTPサーバー側から接続を開始するactive modeだとCIS Rangeでの保護にはならないし、FTPサーバーから見た接続元はCISなんだから、そりゃあうまく行かないだろうなとは思う）。
- CIS Rangeを利用するためにはEnterprise planが必要。[プラン比較表はこちら](https://cloud.ibm.com/docs/cis?topic=cis-cis-plan-comparison&locale=en)
- あくまでftpをCIS Range経由で構成できるかどうかの確認のために検証を実施したのであって、上述のようにインターネット上では**ftpではなく、sftpやscpを利用するべき**だと思う。（sftpやscpをCIS Range経由にするのは、単一ポートを利用するため特に大きな考慮点はない）。

最終的な構成は以下のようになり、CIS RangeでSourceとDestinationのIPアドレスがNATされる。

```
Client  -> CIS Range(Control Port:20021, Data Port:20000-20005) -> Origin Server(Control Port:20021, Data Port:20000-20005)
```

以下では、`example.tk`がCISで構成済みのドメインであり、`ftp.example.tk`がCIS Range上のendpointと仮定して設定および検証例を記載する。


#2. 設定

##2-1. Originサーバーへのvsftpdの導入と設定

```txt:vsftpdの導入
# cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
# yum install -y vsftpd
# vsftpd -v
vsftpd: version 3.0.2
```

https://developers.cloudflare.com/spectrum/ftp のガイドに従い、vsftpd.confに以下を追記。

- 最低限の設定しか入れていないので、セキュリティー観点の考慮は別途参照して下さい。
- なぜかCIS RangeではEdge portやOrigin Portとして、21番(FTPのControl portのデフォルト値）を指定できなかったので、20021を指定しています（まぁ、21番をそのまま公開したくないのでそれでもいいかという気もします）。
- data portは20000-20005を指定（本当は20個ぐらい開けても良いのですが、CIS rangeで20個近く設定を入れるのが面倒だったので今回はこれぐらいにしておきました）

```txt:/etc/vsftpd/vsftpd.conf
#以下に修正（IPv6でListenしているとPassiveモードでの接続に失敗したため）
listen=YES
listen_ipv6=NO

#以下を新規に追加
listen_port=20021
pasv_min_port=20000
pasv_max_port=20005
pasv_enable=YES
pasv_address=ftp.example.tk
pasv_addr_resolve=YES
pasv_promiscuous=YES
```


```txt:vsftpdの起動
# systemctl start vsftpd
```

```txt:ftp用のローカルユーザーの追加とパスワードの設定
# useradd -U syasuda
# id syasuda
uid=1001(syasuda) gid=1001(syasuda) groups=1001(syasuda)
# passwd syasuda
ユーザー syasuda のパスワードを変更。
新しいパスワード:
新しいパスワードを再入力してください:
passwd: すべての認証トークンが正しく更新できました。
```

転送用のデータも適当に作っておいてください。

##2-2. (IBM Cloud VPC上のOrigin Serverに対する）Security Groupの設定


Security GroupでControl Port(20021)およびData Port(20000-20005)を開ける。
なお、接続元IPも絞る方が望ましい。CISの接続元IPは、https://cloud.ibm.com/docs/cis?topic=cis-cis-allowlisted-ip-addresses&locale=en を参照。


##2-3. CIS Rangeの設定

「はじめに」に記載したとおり、`example.tk`がCISで構成済みのドメインであり、`ftp.example.tk`がCIS Range上のendpointと仮定して設定例を記載する。

- 以下のようにControl Port(20021)およびData Port(20000-20005)に対してそれぞれCIS Range Applicationを作成する（これだけでも7個作る必要がある）。
<img width="50%" alt="image.png名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/485b8112-a248-bb85-4114-1ef229156f21.png">

- 最終的な結果は以下のようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/2db599a5-3890-0bab-7451-e76983d47282.png)


※2021/07/01追記
Edge portおよびOrigin portとして21を選択した場合は、UIではエラーになってしまう。これはCIS Rangeを実装したタイミングでCloudflare Specturmでも使用できなかった（？）ポートだったらしく、UIで意図的に利用できないように構成していたらしい。今はこの制約は取り除かれているが、UIでは現時点で未対応らしい（修正依頼中）。そのため、21を指定したい場合はCLIで実施する必要がある。以下はその実行例。

```
$ ibmcloud cis instance-set <CIS Instance Name>
Setting context service instance to '<CIS Instance Name>' ...
OK
Context service instance set succeeded.

$ ibmcloud cis domains
Listing domains for service instance '<CIS Instance Name>' ...
OK
ID                                 Name          Status   Paused   Type
a4135402d38fff24e32ef13c82c1ab4a   example.tk    active   false    full

$ export ZONEID=a4135402d38fff24e32ef13c82c1ab4a

$ ibmcloud cis range-app-create $ZONEID --name ftp.example.tk --edge-port 21 --origin-direct tcp://162.xx.xx.xx:21 --ip-firewall on
Creating range application for domain 'a4135402d38fff24e32ef13c82c1ab4a' ...
OK

ID                      ba1866a2e48048d4b645d5a516c82694
Name                    ftp.example.tk
Edge Port               tcp/21
Origin
                        tcp://162.xx.xx.xx:21
Proxy Protocol          off
IP Firewall             On
Edge IPs Connectivity   all
Edge Tls                off
Traffic Type            direct
Created On              2021-06-30T20:11:29.140333Z
Modified On             2021-06-30T20:11:29.140333Z

```

#3. 接続テスト

```
$ ftp
ftp> open ftp.example.tk 20021
Trying 172.xx.xx.xx...
Connected to ftp.example.tk (172.xx.xx.xx).
220 (vsFTPd 3.0.2)
Name (ftp.example.tk:syasuda): syasuda
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
227 Entering Passive Mode (xx,xx,xx,xx,xx,xx).
150 Here comes the directory listing.
-rw-r--r--    1 1000     1000            0 Jun 30 00:03 hoge
226 Directory send OK.
ftp> get hoge
local: hoge remote: hoge
227 Entering Passive Mode (xx,xx,xx,xx,xx,xx).
150 Opening BINARY mode data connection for hoge (0 bytes).
226 Transfer complete.
ftp>
```