この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
ご機嫌いかがでしょうか、豊崎です。
前回に引き続きAWSの各サービスで利用されているIPアドレスについての記事になります。
前回のブログはこちら
AWSではAWSのサービスで利用しているIPアドレスをwebで公開しています。 https://ip-ranges.amazonaws.com/ip-ranges.json
AWSの各サービスのIPアドレスは不定期で追加されることがあり、上記URLの内容は更新されていきます。 今回は内容の差分を取得したかったので、S3+Lambda+SNSで更新差分を通知する仕組みを作ってみました。
こんな感じです。
ip-ranges.jsonファイルの更新があったタイミングでLambdaが通知を受け取り、内容の差分を確認してユーザにメール通知する想定です。 AmazonIpSpaceChanged トピックが北バージニア(us-east-1)から通知されるので、今回の作業は全て北バージニアで行なっています。
それでは早速作っていきましょう。
S3
まずはip-ranges.jsonを保存する用のS3バケットを作成します。バケット名は「demo-ipranges」としました。
特別な設定はしていませんが、テスト用にいくつかのファイルを配置しておきました。
SNS
トピックを作成し、差分内容を送信したいメールアドレスでサブスクライブしておきましょう。ここでは「demo-iprange」というトピックを作成し自分のメールアドレスを通知先としました。
Lambda
LambdaにはIAMRoleを割り当てています。S3、SNSの操作権限などが必要なためです。今回のLambda用のIAMRoleにアタッチしたポリシーは以下です。
- AWSLambdaFullAccess
- AmazonS3FullAccess
- AmazonSNSFullAccess
検証のため、大きめに権限を割り当てています。
今回のLambda関数ではjsonファイルの内容を比較して、増えたIPレンジと減ったIPレンジを出力するようにしています。減ったIPレンジは不要かもしれないと思ったのですが、使用しているIPレンジの変更やIPレンジ追加に伴うネットワークアドレスの統合表記(/24から/23など)などの可能性を考えて一応差分を確認しています。
設定したLambdaファンクションは以下です。
言語:python3.6
import urllib.request
import json
import boto3
import os
import sys
s3 = boto3.resource('s3')
s3client = boto3.client('s3')
sns = boto3.client('sns', region_name='us-east-1')
def lambda_handler(event, context):
## Webで現在公開されているAWSサービスのIPレンジのJSONを取得 iprangesに格納
url = 'https://ip-ranges.amazonaws.com/ip-ranges.json'
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
ipranges = json.load(res)
##ip-ranges.jsonをS3に保存(日付をつける)
bucket_name = os.environ['BUCKET_NAME']
json_key = 'ipranges-' + ipranges['createDate'] + '.json'
obj = s3.Object(bucket_name,json_key)
obj.put(Body = json.dumps(ipranges))
## 比較対象がない場合は処理を終了
s3objlist = []
s3obj=s3client.list_objects(Bucket=bucket_name)
if len(s3obj['Contents']) == 1:
return 'nothing old ipranges'
## 一つ前のip-ranges.jsonを読み込む
for i in s3obj['Contents']:
s3objlist.append(i['Key'])
s3objlist.sort(reverse=True)
res = s3client.get_object(Bucket=bucket_name, Key=s3objlist[1])
oldipranges = json.loads(res['Body'].read())
## 差分(増えた内容)を比較
diff_v4ipinc = []
diff_v6ipinc = []
for new in ipranges['prefixes']:
eq_flag = 0
for old in oldipranges['prefixes']:
if new['region'] == old['region']:
if new['service'] == old['service']:
if new['ip_prefix'] == old['ip_prefix']:
eq_flag = 1
if eq_flag == 0:
diff_v4ipinc.append(new)
for new in ipranges['ipv6_prefixes']:
eq_flag = 0
for old in oldipranges['ipv6_prefixes']:
if new['region'] == old['region']:
if new['service'] == old['service']:
if new['ipv6_prefix'] == old['ipv6_prefix']:
eq_flag = 1
if eq_flag == 0:
diff_v6ipinc.append(new)
## 差分(減った内容)を比較
diff_v4ipdec = []
diff_v6ipdec = []
for old in oldipranges['prefixes']:
eq_flag = 0
for new in ipranges['prefixes']:
if new['region'] == old['region']:
if new['service'] == old['service']:
if new['ip_prefix'] == old['ip_prefix']:
eq_flag = 1
if eq_flag == 0:
diff_v4ipdec.append(old)
for old in oldipranges['ipv6_prefixes']:
eq_flag = 0
for new in ipranges['ipv6_prefixes']:
if new['region'] == old['region']:
if new['service'] == old['service']:
if new['ipv6_prefix'] == old['ipv6_prefix']:
eq_flag = 1
if eq_flag == 0:
diff_v6ipdec.append(old)
### 差分を整形しSNS Publish
snsmsg = ipranges['createDate'] +'.json と '+ s3objlist[1] +'の比較\n\n'
snsmsg = snsmsg + '---増えた内容(' + ipranges['createDate'] + '.jsonにだけ記載されている)は以下---\n\n'
for msg in diff_v4ipinc:
snsmsg = snsmsg + 'region : ' + msg['region'] + ' / service : ' + msg['service'] + ' / ip_prefix : ' + msg['ip_prefix'] + '\n'
for msg in diff_v6ipinc:
snsmsg = snsmsg + 'region : ' + msg['region'] + ' / service : ' + msg['service'] + ' / ipv6_prefix : ' + msg['ipv6_prefix'] + '\n'
snsmsg = snsmsg + '\n---減った内容(' + s3objlist[1] + 'にだけ記載されている)は以下---\n\n'
for msg in diff_v4ipdec:
snsmsg = snsmsg + 'region : ' + msg['region'] + ' / service : ' + msg['service'] + ' / ip_prefix : ' + msg['ip_prefix'] + '\n'
for msg in diff_v6ipdec:
snsmsg = snsmsg + 'region : ' + msg['region'] + ' / service : ' + msg['service'] + ' / ipv6_prefix : ' + msg['ipv6_prefix'] + '\n'
sns_responce = sns.publish(
TopicArn = os.environ['TOPIC_ARN'],
Message = snsmsg,
Subject = 'diff-ipranges'
)
return 'Compleate!'
ちなみにS3バケット名とSNSのTOPIC_ARNは環境変数に登録して、タイムアウトも3分にしています。
事前にS3に配置しておいたjsonファイルの中身を弄って差分(増減)がちゃんと通知されるかを確認してみました。
Lambdaを実行して届いた通知がこちら。ちゃんと届いていますね。
<2019/02/04 追記>
実際の更新で正しく通知されていることを確認しました。
さいごに
意外とIPの差分を確認したいという要望が出てくることがありましたので、作ってみました。
テストでは成功しましたが、実際の変更での通知がAmazonIpSpaceChanged トピックからまだ来てないので、もうしばらく様子見してみます。 何か問題があれば修正するかもしれません。誰かの参考になれば幸いです。