IBM Cloud CISの送信元IPアドレス変更を検知する

CIS (Cloudflare) の送信元アドレス情報について下記URLで公開されています。

CIS allowlisted IP addresses
https://cloud.ibm.com/docs/cis?topic=cis-cis-allowlisted-ip-addresses

今回はその変更を検知するスクリプト(bash)例を紹介します。


CISの送信元アドレスの変更頻度は少ないことが想定されますが、JSONで公開されているリストは、
下記記事作成時に取得された時点 と 本日(2023/04/27)時点 では若干の差分があるようです。

IBM Cloud Internet Services (CIS) の IP に対して Security Group を Shell で作成する
https://qiita.com/khayama/items/a6fd605cc0247be409e4

# CIS の 送信元IP アドレス

```console:
$ curl https://api.cis.cloud.ibm.com/v1/ips -s | jq -r .result
{
  "ipv4_cidrs": [
    "173.245.48.0/20",
    "103.21.244.0/22",
    "103.22.200.0/22",
    "103.31.4.0/22",
    "141.101.64.0/18",
    "108.162.192.0/18",
    "190.93.240.0/20",
    "188.114.96.0/20",
    "197.234.240.0/22",
    "198.41.128.0/17",
    "162.158.0.0/15",
    "104.16.0.0/13",
    "104.24.0.0/14",
    "172.64.0.0/13",
    "131.0.72.0/22"
  ],
  "ipv6_cidrs": [
    "2400:cb00::/32",
    "2606:4700::/32",
    "2803:f800::/32",
    "2405:b500::/32",
    "2405:8100::/32",
    "2a06:98c0::/29",
    "2c0f:f248::/32"
  ],
  "etag": "38f79d050aa027e3be3865e495dcc9bc"
}
```


# CIS の 送信元IP アドレス変更を検知するスクリプト

```console: cis-ips.sh
#!/bin/bash

echo "`date +%Y%m%d_%H:%M:%S` cis-ips.sh start"

CISIPS_URL="https://api.cis.cloud.ibm.com/v1/ips"
CISIPS_TMP=`date +%Y%m%d_%H%M`.json
TIMEOUT="20" # second

curl -w "status_code:%{http_code}\ntime_total:%{time_total}\n" $CISIPS_URL -o $CISIPS_TMP -s > curl-status

STATUS_CODE=`grep status_code curl-status | cut -d ':' -f 2`
TIME_TOTAL=`grep time_total curl-status | cut -d ':' -f 2 | cut -d '.' -f 1`

if [ $STATUS_CODE != 200 ] ; then
 echo "HTTP Status Code was $STATUS_CODE."
 exit 1
fi

if [ $TIME_TOTAL -gt $TIMEOUT ] ; then
 echo "Total time exceeded 20 seconds."
 exit 1
fi

if [ ! -e cis-ips.json ] ; then
 cp $CISIPS_TMP cis-ips.json
fi

if diff -q $CISIPS_TMP cis-ips.json ; then
 # echo "There is no difference."
 rm $CISIPS_TMP
else
 echo "There is a difference. CIS IPS has been changed."
 mv cis-ips.json cis-ips_$CISIPS_TMP
 mv $CISIPS_TMP cis-ips.json
fi

echo "`date +%Y%m%d_%H:%M:%S` cis-ips.sh stop"
```

既存のIPアドレスリスト と ダウンロードしたリスト の差分を検知した場合に、
スクリプトを動作させる監視サーバー側でAleartを上げる設定を追加する必要がある
(上記スクリプト例では、`There is no difference.`のメッセージが表示されるのみ)


# スクリプトの実行結果

```console: 特に変更がない場合
$ sh cis-ips.sh
20230427_15:15:42 cis-ips.sh start
20230427_15:15:47 cis-ips.sh stop
```

```console: 変更があった場合
$ sh cis-ips.sh
cis-ips.sh start
Files YYYYmmdd_HHMM.json and cis-ips.json differ
There is a difference. CIS IPS has been changed.
cis-ips.sh stop
```


# スクリプトの定期実行

上記スクリプトをcron等で1週間毎に稼働し、CISの送信元アドレス変更を検知する

```console: 毎週日曜1時に定期実行
# chmod +x cis-ips.sh

# ls -al
total 10
drwxr-xr-x. 2 root root 1024 Apr 27 15:58 .
drwxr-xr-x. 6 root root 1024 Apr 27 14:17 ..
-rw-r--r--. 1 root root  505 Apr 26 20:40 cis-ips.json
-rwxr-xr-x. 1 root root  871 Apr 27 13:19 cis-ips.sh
-rw-r--r--. 1 root root   33 Apr 27 15:58 curl-status

# crontab -e
crontab: installing new crontab
# crontab -l
MAILTO=""
1-59/10 * * * * /usr/bin/diskmonitor
0 */4 * * * /usr/bin/diskwearoutstat
29 22 * * * /usr/bin/updatecheck -a
29 22 24 * * /usr/bin/phonehome_upload
0 */6 * * * /usr/bin/coreexpiration
8 * * * * /usr/bin/copy_rrd save
0 1 * * 7 /home/cis/cis-ips.sh
```


# CISが応答しない場合のエラー処理

CISの送信元IPアドレスリストのURLに何度かアクセスした際に、下記エラー画面が応答された場合があった。
![Worker threw exception _ api.cis.cloud.ibm.com _ Cloudflare .png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329464/0ede06ac-2bf6-4aff-6619-5cd731c68db1.png)


curlコマンドのオプションで、HTTP Status Codeや処理時間を取得することが可能であるため、
エラー処理を実装する。
`curl -w "status_code:%{http_code}\ntime_total:%{time_total}\n" `

CISからHTTP Status Code 200が応答されなかった場合 は処理を終了する
```
STATUS_CODE=`grep status_code curl-status | cut -d ':' -f 2`
# curl-statusファイルの中のstatus_codeの行より、「:」区切りの2つ目を抽出
# status_code:200 ⇒ 200 が抽出される

if [ $STATUS_CODE != 200 ] ; then
 echo "HTTP Status Code was $STATUS_CODE."
 exit 1
fi
```


処理時間が設定閾値を超えた場合 は処理を終了する

```
TIMEOUT="20" # second
TIME_TOTAL=`grep time_total curl-status | cut -d ':' -f 2 | cut -d '.' -f 1`
# curl-statusファイルの中のtime_totalの行より、「:」区切りの2つ目、「.」区切りの1つ目を抽出
# time_total:5.066900 ⇒ 5 が抽出される

if [ $TIME_TOTAL -gt $TIMEOUT ] ; then
 echo "Total time exceeded 20 seconds."
 exit 1
fi
```

# 参考URL

IBM Cloud Internet Services (CIS) の IP に対して Security Group を Shell で作成する
https://qiita.com/khayama/items/a6fd605cc0247be409e4

curlでパフォーマンス測定
https://dev.classmethod.jp/articles/curl-benchmark/

【Linux】crontabの設定方法を解説！定期的にジョブを実行させる方法とは？
https://engineer-ninaritai.com/linux-crontab/