AWS Lambda

AWSのIPリストの変更をプレフィックスリストに反映するLambdaを作ってみた

はじめに

こんにちは、omkです。

突然ですがAWSはサービスごとにIPの範囲を出してくれるので非常にありがたいですよね。
https://ip-ranges.amazonaws.com/ip-ranges.json

私が若い頃はCloudFrontのプレフィックスリストがまだ無くてWAFで独自のヘッダーを付けて制限しろみたいな時代でした。
そのためこのリストからCloudFrontのIP一覧を取得してセキュリティグループに設定していたときはその数が多くあまりにも辛くて涙が出そうでした。

閑話休題、プレフィックスリストが公開されていないサービスのIPも独自のプレフィックスリストに入れて定期的に更新できるようにしていきます。

やってみた

とりあえずCodeBuildのIPを取ってみます。
以下のPythonプログラムを作成しました。

import json
import boto3
from urllib import request
import sys
import ipaddress

url = "https://ip-ranges.amazonaws.com/ip-ranges.json"
prefix_list_id = "pl-hogehogefugafuga"
service_name = "CODEBUILD"

client = boto3.client('ec2')

def lambda_handler(event, context):
    # IP一覧の取得
    try:
        response = request.urlopen(url)
    except Exception as e:
        print(e)
        print("対象URLへのリクエストに失敗しました")
        sys.exit(1)

    # 対象IPの取得
    try:    
        body = json.loads(response.read().decode("utf-8"))
        prefixes = body['prefixes']
    except Exception as e:
        print(e)
        print("レスポンスのパースに失敗しました")
        sys.exit(1)

    # 設定対象IPの精査(IPv4のみを取得)
    new_entry =  list()

    for prefix in prefixes:
        if prefix['service'] == service_name:
            ip = prefix["ip_prefix"]
            adress = ipaddress.ip_network(ip)
            if type(adress) == ipaddress.IPv4Network:
                new_entry.append({'Cidr':ip})

    # 現行のプレフィックスリストを取得
    try:    
        response = client.describe_managed_prefix_lists(
            PrefixListIds = [prefix_list_id]
        )

        current_version = response['PrefixLists'][0]['Version']

    except Exception as e:
        print(e)
        print("プレフィックスリストの取得に失敗しました")
        sys.exit(1)

    # 現在のプレフィックスリストのエントリー情報を取得
    try:    
        response = client.get_managed_prefix_list_entries(
            PrefixListId = prefix_list_id
        )

        now_entry = response['Entries']

    except Exception as e:
        print(e)
        print("プレフィックスリストのエントリー情報の取得に失敗しました")
        sys.exit(1)

    print("現在のプレフィックスリストに設定されたIPの一覧")
    print(now_entry)

    # 追加対象IPの設定
    add_entry = list(filter(lambda x: x not in now_entry,new_entry))

    # 削除対象IPの設定 
    remove_entry = list(filter(lambda x: x not in new_entry,now_entry))

    # 現行のプレフィックスリストとの比較
    if len(add_entry) > 0  or len(remove_entry) > 0:

        # 差分あり
        try:
            response = client.modify_managed_prefix_list(
                PrefixListId = prefix_list_id,
                CurrentVersion = current_version,
                AddEntries = add_entry,
                RemoveEntries = remove_entry
            )
            print("IPに変更があったためプレフィックスリストを更新しました。")

        except Exception as e:
            print(e)
            print("プレフィックスリストの更新に失敗しました")
            sys.exit(1)

    return {
        'statusCode': 200
    }

実行します。
プレフィックスリストにIPが追加されました。

やったね。
あとはEventBridgeで定期的に実行すればOKです。

解説

コメントの通りですがざっくりと以下のような流れになります。

  1. URLにアクセスして最新のリストを取ります。
  2. CodeBuildのIPだけを取得してマネージドプレフィックスのエントリーのフォーマットに合わせます。
  3. 現在のマネージドプレフィックスリストのエントリーを取得します。
  4. 両者の差分から追加すべきIPと削除すべきIPを割り出します。
  5. マネージドプレフィックスリストを更新します。

使用するライブラリがすべてLambdaにプリインストールされている点がこだわりです。

おわりに

AWSのIPに限らず色々なIPのリストに対応できるソリューションですね。
以上、最後までお付き合いありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA