LoginSignup
1
0

Cloudflareの使用IPレンジのプレフィックスリストを定期的に更新する処理を導入する。

Posted at

はじめに

とある案件で、Cloudflareが保持するIPレンジを自動で取得する機能があればなぁと思い、Cloudflareの使用IPレンジを更新する処理を作ったので紹介したいと思います。

機能を導入したきっかけ

先日始めてCDNサービスのCloudflareを使う機会がありました。

同じCDNサービスの1つであるAWSのCloudFrontは仕事でも使用しており、その際、オリジンサーバへ直接アクセスさせないようにするため、CloudFrontからオリジンサーバへアクセスするときに使用するIPアドレスが登録されているマネージドプレフィックスリストをセキュリティグループと一緒に使用して、オリジンサーバへのアクセスはCloudFrontからのアクセスに限定するようなことを行っておりました。

CloudFrontであれば、VPC設定の「マネージドプレフィックスリスト」にCloudFrontが使用するIPレンジの一覧が登録されているため、簡単にセキュリティグループ設定で送信元IPリストとして使用することができますが、CloudflareはAWSサービスではないので登録されておりません。

幸い、Cloudflareが使用するIPレンジは以下で公開されており、APIも存在するということで、じゃあ定期的に公開サイトから一覧を取得して登録してしまおうと思ったのがきっかけです。

マネージドプレフィックスリストとは

AWSのVPC設定の中にある「マネージドプレフィックスリスト」は複数IPアドレスをまとめたリストで、セキュリティグループ等を作成する際に送信元・送信先IPアドレスとしてまとめて指定するようなことができる便利な機能です。

また、CloudFrontの使用IPやRoute53のヘルスチェック時のIPなど、AWSのサービスが使用するIPはあらかじめ登録されているため、登録済みのマネージドプレフィックスリストを指定するだけで、各サービスからのアクセスに限定するようなことができます。

あらかじめ登録されているリスト以外でも自分で新たなリストを登録することもできるので、今回はCloudflare用のマネージドプレフィックスリストを作成しようと思います。

マネージドプレフィックスリスト定期更新処理

以下より実際にCloudflareの使用IPを登録したマネージドプレフィックスリストを更新する処理を作成していきたいと思います。

今回導入する機能の概要は以下となります。

CloudflareIPリスト更新処理.png

処理の内容としてはEventBridge Schedulerで定期的にCodeBuildを実行し、CodeBuildCloudflareの使用IPレンジ公開サイトと、あらかじめ登録済みのマネージドプレフィックスリストのアドレスを比較して、IPアドレスが追加・削除されていればマネージドプレフィックスリストを更新するといったものとなります。

今回はCodeBuildで処理を実行していますが、Lambdaが書けるならLambdaでも問題ありません。

以下より実際に各種サービスを作成していきます。

マネージドプレフィックスリストの作成

CloudflareのIPアドレスを格納するマネージドプレフィックスリストをあらかじめ作成しておきます。

VPCダッシュボードより「マネージドプレフィックスリスト」の「プレフィックスリストの作成」から作成していきます。

IPv4用、IPv6用それぞれ作成しようと思うので、以下のように設定しました。

項目 IPv4プレフィックスリスト用設定 IPv6プレフィックスリスト用設定
プレフィックスリスト名 cloudflare.global.ipv4.ips cloudflare.global.ipv6.ips
最大エントリ 20 10
アドレスファミリー IPv4 IPv6
プレフィックスリストのエントリ 未設定 未設定
タグ ※今回は無し ※今回は無し

プレフィックスリスト名は特に指定があるわけではないので任意の名前で問題ありませんが、最大エントリには注意する必要があります。

マネージドプレフィックスリストは作成時、あらかじめ「最大エントリ」で記載されている値の分の領域を確保する必要がありますが、あまりに大きい値を最初から確保してしまうと、セキュリティグループで使用する際にセキュリティグループのデフォルトのルール上限の60に引っかかってしまいます。
※見かけ上IPをまとめているだけで実際には1エントリずつ登録しているのと変わらないため

最大エントリ」で設定している上限に達した場合、拡張はできますが、縮小はできず、拡張する場合も自動で拡張されるわけではなく、オペレーションを行う必要があるので、ギリギリの値に設定すると、アドレスの追加があった場合、エラーとなってしまいます。

2024年6月現在、CloudflareのIPv4アドレス使用レンジ数が15アドレスレンジ、IPv6アドレス使用レンジ数が7アドレスレンジのため、多少余裕を持たせてIPv4の最大エントリを20、IPv6の最大エントリを10としました。

また、プレフィックスリストのエントリですが、後ほどCodeBuildでまとめて登録するため、何も登録しなくてOKです。

CodeBuildの設定

更新処理を行うCodeBuildを作成していきます。

CodeBuild用IAMポリシーの作成

後ほどCodeBuildに割り当てる必要があるため、あらかじめIAMポリシーとIAMロールを作成しておきます。

IAMダッシュボードの「ポリシー」→「ポリシーの作成」から、ポリシーエディタより以下のJSONポリシーを流し込みます。

今回はリソースを「」としてしまっているので、実際にシステムに導入するような場合はリソース指定するなど適宜調整してください。

CodeBuild用IAMポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CodeBuildPolicy",
      "Effect": "Allow",
      "Action": [
        "codebuild:CreateReportGroup",
        "codebuild:CreateReport",
        "codebuild:UpdateReport",
        "codebuild:BatchPutCodeCoverages",
        "codebuild:BatchPutTestCases"
      ],
      "Resource": ["*"]
    },
    {
      "Sid": "ManagedPrefixListPolicy",
      "Effect": "Allow",
      "Action": [
        "ec2:GetManagedPrefixListEntries",
        "ec2:ModifyManagedPrefixList",
        "ec2:DescribeManagedPrefixLists"
      ],
      "Resource": ["*"]
    },
    {
      "Sid": "CloudWatchLogsPolicy",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": ["*"]
    }
  ]
}

今回はポリシー名を「cloudflare-update-ip-codebuild-policy」として進めます。

CodeBuild用IAMロールの作成

作成したIAMポリシーを使い、IAMロールも作成します。

ロール」→「ロールを作成」から、以下のように設定していき、CodeBuild用IAMロールを作成します。

項目 設定
信頼されたエンティティタイプ AWSのサービス
サービスまたはユースケース CodeBuild
ユースケース CodeBuild
許可ポリシー cloudflare-update-ip-codebuild-policy
許可の境界を設定 許可の境界なしでロールを作成
ロール名 cloudflare-update-ip-codebuild-role
説明 ※デフォルトのまま
タグ ※今回は無し

CodeBuildの作成

CodeBuildダッシュボードの「ビルドプロジェクト」→「プロジェクトを作成する」よりCodeBuildを作成していきます。

項目 設定
プロジェクト名 cloudflare-update-ip-codebuild-proj
追加設定 ※デフォルトのまま
ソースプロバイダ 指定なし
プロビジョニングモデル オンデマンド
環境イメージ マネージド型イメージ
コンピューティング EC2
オペレーティングシステム Amazon Linux
ランタイム Standard
イメージ aws/codebuild/amazonlinux2-x86_64-standard:5.0
イメージのバージョン このランタイムバージョンには常に最新のイメージを使用してください
GPU強化コンピューティングを使用 チェック無し
サービスロール 既存のサービスロール
ロールのARN ※先ほど作成したCodeBuild用IAMロールのARNを指定
AWS CodeBuildにこのサービスロールの編集を許可し、このビルドプロジェクトでの使用を可能にする チェック
追加設定 ※環境変数のみ指定(環境変数は次表に記載)
Buildspec ビルドコマンドの挿入
※Buildspecは後述
バッチ設定を定義 チェック無し
アーティファクト1タイプ アーティファクトなし
追加設定 ※デフォルトのまま
CloudWatch Logs チェック
グループ名 /codebuild/cloudflare-update-ip-logs
ストリーム名のプレフィックス cloudflare-update-ip-stream
S3ログ チェックなし

上記の追加設定で行う環境変数では以下IPv4用、IPv6用の変数を追加し、値には先ほど作成したマネージドプレフィックスリストID(pl-xxxで始まるプレフィックスID)を入力します。

名前 タイプ
PREFIX_LIST_ID_IPV4 pl-xxxxxxxxxxxxxxxxx プレーンテキスト
PREFIX_LIST_ID_IPV6 pl-yyyyyyyyyyyyyyyyy プレーンテキスト

Buildspecについては以下を貼り付けます。

Buildspec
version: 0.2

env:
  variables:
    # AWS_DEFAULT_REGION      : デフォルトリージョン
    # OLD_LIST_FILE_IPv4_FILE : 古いIPv4アドレスリストファイル名
    # NEW_LIST_FILE_IPv4_FILE : 新しいIPv4アドレスリストファイル名
    # DELETE_LIST_IPv4_FILE   : 削除IPv4アドレスリストファイル名
    # ADD_LIST_IPv4_FILE      : 追加IPv4アドレスリストファイル名
    # OLD_LIST_FILE_IPv6_FILE : 古いIPv6アドレスリストファイル名
    # NEW_LIST_FILE_IPv6_FILE : 新しいIPv6アドレスリストファイル名
    # DELETE_LIST_IPv6_FILE   : 削除IPv6アドレスリストファイル名
    # ADD_LIST_IPv6_FILE      : 追加IPv6アドレスリストファイル名
    # PREFIX_LIST_ID_IPV4     : IPv4マネージドプレフィックスリストID
    # PREFIX_LIST_ID_IPV6     : IPv6マネージドプレフィックスリストID
    AWS_DEFAULT_REGION: "ap-northeast-1"
    OLD_LIST_IPV4_FILE: "list_ipv4_old"
    NEW_LIST_IPV4_FILE: "list_ipv4_new"
    DELETE_LIST_IPV4_FILE: "delete_ipv4_list"
    ADD_LIST_IPV4_FILE: "add_ipv4_list"
    OLD_LIST_IPV6_FILE: "list_ipv6_old"
    NEW_LIST_IPV6_FILE: "list_ipv6_new"
    DELETE_LIST_IPV6_FILE: "delete_ipv6_list"
    ADD_LIST_IPV6_FILE: "add_ipv6_list"
    # PREFIX_LIST_IDは外部変数で指定するためコメントアウト
    # PREFIX_LIST_ID_IPV4: "pl-xxxxxxxxxxxxxxxxx"
    # PREFIX_LIST_ID_IPV6: "pl-yyyyyyyyyyyyyyyyy"

phases:
  pre_build:
    on-failure: ABORT
    commands:
      # IPv4マネージドプレフィックスリストのエントリを取得
      # buildspec.yml内ではステップをまたぐと終了コードで判定できないため、エラーチェック用変数で判定
      - |
        aws ec2 get-managed-prefix-list-entries --prefix-list-id ${PREFIX_LIST_ID_IPV4} | jq -r '.Entries[].Cidr' | sort > ${OLD_LIST_IPV4_FILE}
        ERRCHK_IPV4=${PIPESTATUS[0]}
      # エラー処理
      - |
        if [ ${ERRCHK_IPV4} -ne 0 ]; then
          exit 1
        fi
      # IPv6マネージドプレフィックスリストのエントリを取得
      # buildspec.yml内ではステップをまたぐと終了コードで判定できないため、エラーチェック用変数で判定
      - |
        aws ec2 get-managed-prefix-list-entries --prefix-list-id ${PREFIX_LIST_ID_IPV6} | jq -r '.Entries[].Cidr' | sort > ${OLD_LIST_IPV6_FILE}
        ERRCHK_IPV6=${PIPESTATUS[0]}
      # エラー処理
      - |
        if [ ${ERRCHK_IPV6} -ne 0 ]; then
          exit 1
        fi
      # CloudflareのIPv4IPアドレスリストを取得
      - |
        curl -s --request GET --url https://api.cloudflare.com/client/v4/ips --header 'Content-Type: application/json' | jq -r '.result.ipv4_cidrs[]' | sort > ${NEW_LIST_IPV4_FILE}
        ERRCHK_IPV4=${PIPESTATUS[1]}
      # エラー処理
      - |
        if [ ${ERRCHK_IPV4} -ne 0 ]; then
          exit 1
        fi
      # CloudflareのIPv6IPアドレスリストを取得
      - |
        curl -s --request GET --url https://api.cloudflare.com/client/v4/ips --header 'Content-Type: application/json' | jq -r '.result.ipv6_cidrs[]' | sort > ${NEW_LIST_IPV6_FILE}
        ERRCHK_IPV6=${PIPESTATUS[1]}
      # エラー処理
      - |
        if [ ${ERRCHK_IPV6} -ne 0 ]; then
          exit 1
        fi
      # 比較用ファイル作成
      - diff ${OLD_LIST_IPV4_FILE} ${NEW_LIST_IPV4_FILE} | awk '/^</ {print $2}' > ${DELETE_LIST_IPV4_FILE}
      - diff ${OLD_LIST_IPV4_FILE} ${NEW_LIST_IPV4_FILE} | awk '/^>/ {print $2}' > ${ADD_LIST_IPV4_FILE}
      - diff ${OLD_LIST_IPV6_FILE} ${NEW_LIST_IPV6_FILE} | awk '/^</ {print $2}' > ${DELETE_LIST_IPV6_FILE}
      - diff ${OLD_LIST_IPV6_FILE} ${NEW_LIST_IPV6_FILE} | awk '/^>/ {print $2}' > ${ADD_LIST_IPV6_FILE}
  build:
    on-failure: ABORT
    commands:
      # IPv4マネージドプレフィックスリストの更新判定
      - |
        if [ ! -s ${DELETE_LIST_IPV4_FILE} ] && [ ! -s ${ADD_LIST_IPV4_FILE} ]; then
          # 何も実行しない
          :
        else
          # マネージドプレフィックスリストの現在のバージョン取得と削除&追加用コマンド作成
          LIST_VERSION_IPV4=$(aws ec2 describe-managed-prefix-lists --prefix-list-ids ${PREFIX_LIST_ID_IPV4} | jq -r '.PrefixLists[].Version')
          DELETE_LIST_IPV4_COMMAND="$(awk '{printf "Cidr=%s ", $1}' ${DELETE_LIST_IPV4_FILE})"
          ADD_LIST_IPV4_COMMAND="$(awk -v timestamp="$(date +'%Y-%m-%d')" '{printf "Cidr=%s,Description=Add_" timestamp "_Cloudflare ", $1}' ${ADD_LIST_IPV4_FILE})"
          # マネージドプレフィックスリストへの追加&削除がある場合
          if [ -s ${DELETE_LIST_IPV4_FILE} ] && [ -s ${ADD_LIST_IPV4_FILE} ]; then
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV4} \
              --remove-entries ${DELETE_LIST_IPV4_COMMAND} \
              --add-entries ${ADD_LIST_IPV4_COMMAND} \
              --current-version ${LIST_VERSION_IPV4} 2>&1 > /dev/null
            ERRCHK_IPV4=$(echo $?)
          # マネージドプレフィックスリストへの削除がある場合
          elif [ -s ${DELETE_LIST_IPV4_FILE} ] && [ ! -s ${ADD_LIST_IPV4_FILE} ]; then
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV4} \
              --remove-entries ${DELETE_LIST_IPV4_COMMAND} \
              --current-version ${LIST_VERSION_IPV4} 2>&1 > /dev/null
            ERRCHK_IPV4=$(echo $?)
          # マネージドプレフィックスリストへの追加がある場合
          else
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV4} \
              --add-entries ${ADD_LIST_IPV4_COMMAND} \
              --current-version ${LIST_VERSION_IPV4} 2>&1 > /dev/null
            ERRCHK_IPV4=$(echo $?)
          fi
        fi
      # IPv6マネージドプレフィックスリストの更新判定
      - |
        if [ ! -s ${DELETE_LIST_IPV6_FILE} ] && [ ! -s ${ADD_LIST_IPV6_FILE} ]; then
          # 何も実行しない
          :
        else
          # マネージドプレフィックスリストの現在のバージョン取得と削除&追加用コマンド作成
          LIST_VERSION_IPV6=$(aws ec2 describe-managed-prefix-lists --prefix-list-ids ${PREFIX_LIST_ID_IPV6} | jq -r '.PrefixLists[].Version')
          DELETE_LIST_IPV6_COMMAND="$(awk '{printf "Cidr=%s ", $1}' ${DELETE_LIST_IPV6_FILE})"
          ADD_LIST_IPV6_COMMAND="$(awk -v timestamp="$(date +'%Y-%m-%d')" '{printf "Cidr=%s,Description=Add_" timestamp "_Cloudflare ", $1}' ${ADD_LIST_IPV6_FILE})"
          # マネージドプレフィックスリストへの追加&削除がある場合
          if [ -s ${DELETE_LIST_IPV6_FILE} ] && [ -s ${ADD_LIST_IPV6_FILE} ]; then
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV6} \
              --remove-entries ${DELETE_LIST_IPV6_COMMAND} \
              --add-entries ${ADD_LIST_IPV6_COMMAND} \
              --current-version ${LIST_VERSION_IPV6} 2>&1 > /dev/null
            ERRCHK_IPV6=$(echo $?)
          # マネージドプレフィックスリストへの削除がある場合
          elif [ -s ${DELETE_LIST_IPV6_FILE} ] && [ ! -s ${ADD_LIST_IPV6_FILE} ]; then
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV6} \
              --remove-entries ${DELETE_LIST_IPV6_COMMAND} \
              --current-version ${LIST_VERSION_IPV6} 2>&1 > /dev/null
            ERRCHK_IPV6=$(echo $?)
          # マネージドプレフィックスリストへの追加がある場合
          else
            aws ec2 modify-managed-prefix-list \
              --prefix-list-id ${PREFIX_LIST_ID_IPV6} \
              --add-entries ${ADD_LIST_IPV6_COMMAND} \
              --current-version ${LIST_VERSION_IPV6} 2>&1 > /dev/null
            ERRCHK_IPV6=$(echo $?)
          fi
        fi
      # エラー処理
      - |
        if [ ${ERRCHK_IPV4} -ne 0 ] || [ ${ERRCHK_IPV6} -ne 0 ]; then
          exit 1
        fi
  post_build:
    on-failure: CONTINUE
    commands:
      # 作業用ファイル削除
      - rm -f ${OLD_LIST_IPV4_FILE} ${NEW_LIST_IPV4_FILE} ${DELETE_LIST_IPV4_FILE} ${ADD_LIST_IPV4_FILE}
      - rm -f ${OLD_LIST_IPV6_FILE} ${NEW_LIST_IPV6_FILE} ${DELETE_LIST_IPV6_FILE} ${ADD_LIST_IPV6_FILE}

CodeBuildのテスト

CodeBuildが完成したので、一度実行して動作するかを確認しておきます。

CodeBuildダッシュボードの「ビルドプロジェクト」から作成したCodeBuildのビルドプロジェクトを選択し、「ビルドを開始」で実行してみます。

問題なければ先程何もエントリの登録をしなかったIPv4とIPv6のプレフィックスリストに以下のようにCloudflareのIPリストが追加されているはずです。

CodeBuildで追加されたエントリについては説明欄に追加した日付が出るようにしているため、説明欄を見てCodeBuildで追加されたものなのか確認してもOKです。

Monosnap_20240602_103306.png

EventBridge Schedulerの設定

ここまでの設定で一通りの処理はできるようになりましたが、先程作成したCodeBuildを定期実行するため、EventBridge Schedulerで実行するようにします。

EventBridge Scheduler用IAMポリシーの作成

先程のCodeBuildのIAMポリシーと同じように作成します。

ポリシー名は「cloudflare-update-ip-eb-policy」として進めます。

EventBridge Scheduler用IAMポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EventBridgeSchedulerPolicy",
      "Effect": "Allow",
      "Action": "codebuild:StartBuild",
      "Resource": "*"
    }
  ]
}

EventBridge Scheduler用IAMロールの作成

IAMロールも同様に作成します。

ただし、EventBridge Schedulerはユースケースに登録されていないようなので、「カスタム信頼ポリシー」から作成していきます。

項目 設定
信頼されたエンティティタイプ カスタム信頼ポリシー
サービスまたはユースケース CodeBuild
ユースケース CodeBuild
許可ポリシー cloudflare-update-ip-eb-policy
許可の境界を設定 許可の境界なしでロールを作成
ロール名 cloudflare-update-ip-eb-role
説明 ※デフォルトのまま
タグ ※今回は無し

カスタム信頼ポリシーは以下で指定します。

EventBridge Schedulerカスタム信頼ポリシー
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "EventBridgeSchedulerAssumeRole",
			"Effect": "Allow",
			"Principal": {
				"Service": "scheduler.amazonaws.com"
			},
			"Action": "sts:AssumeRole"
		}
	]
}

EventBridge Schedulerの作成

EventBridgeダッシュボードの「Scheduler」→「スケジュール」の「スケジュールを作成」から作成します。

cron式で指定している時間はお好きな時間に指定してください。

項目 設定
スケジュール名 cloudflare-update-ip-schedule
説明 ※今回は無し
スケジュールグループ default
頻度 定期的なスケジュール
タイムゾーン (UTC+09:00) Asia/Tokyo
スケジュールの種類 cronベースのスケジュール
cron式 cron(0 1 * * ? *)
フレックスタイムウィンドウ オフ
時間枠 ※サマータイム設定は行わないためスキップ
ターゲットAPI テンプレート化されたターゲット
選択ターゲット CodeBuild StartBuild
CodeBuildプロジェクト ※先ほど作成したCodeBuildプロジェクトを指定
リクエストパラメータ {}
スケジュールを有効化 有効化
スケジュール完了後のアクション NONE
再試行ポリシー 再試行
イベントの最大経過時間 24hour(s) 0minute(s)
再試行回数 185times
デッドレターキュー(DLQ) なし
暗号化 チェックなし
アクセス許可 ※先ほど作成したEventBridge Scheduler用IAMロールを指定

EventBridge Schedulerのテスト

先ほど手動でCodeBuildを実行して確認しましたが、EventBridge Schedulerから実行して動作するかを確認してみます。

cron式で指定した時間に、EventBridge SchedulerからCodeBuildが実行され、成功していれば完了です。

Monosnap_20240602_113315.png

(おまけ)terraformとシェル

今回の手順をまとめたterraformコードをGitHubに置いたので手作業でやりたくない方はお使いください。
※terraform化にあたり、IAMポリシー部分はリソース指定して厳格化しています。

また、CodeBuildでやっていた更新作業をシェルにしました。

IPv4、IPv6プレフィックスリストIDをシェル内のPREFIX_LIST_ID_IPV4PREFIX_LIST_ID_IPV6に記載し、AWSにアクセス権限がある端末から以下のように実行すれば更新されます。

シェル実行方法
./cloudflare_update_ip.sh

また、更新があれば以下のように結果を確認できます。

更新があった場合の出力結果例
--------------------
削除リスト
--------------------
# IPv4
192.0.2.0/24
# IPv6
2001:db8::/32

--------------------
追加リスト
--------------------
# IPv4
103.31.4.0/22
104.16.0.0/13
# IPv6
2c0f:f248::/32

手軽にシェル実行で更新したい方はこちらをお使いください。

おわりに

今回はCodeBuildで処理を行うようにしましたが、Lambda等他の方法でも実装できるので、やりやすい方法で実装してもらえれば良いかなと思います。

なにかのお役に立てれば幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0