Lambdaでセキュリティグループを自動更新する(CloudFront用)

CloudFrontのIPがよく変更されるので、自動でセキュリティグループを変更してくれるバッチを作成しました。
CloudFrontのIPレンジは[AWSがjson形式で公開している](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/LocationsOfEdgeServers.html)ので、これをベースに作成します。
※CloudFrontはエッジロケーションとリージョナルエッジキャッシュの2つがあります。

#CloudFront+ALBのアクセス制御方法
たくさんあるので[こちら](https://qiita.com/bunty/items/418363dfc335de053478)にわかりやすくまとまってます。
よくあるパターン

1. ALBにWAFを入れてアクセス制御
2. CloudFrontにカスタムヘッダ付与+ALBのルールでアクセス制御
3. セキュリティグループで制限

今回は3つめのCloudFrontからのアクセスをセキュリティグループで制御してみます。

[こちら](https://github.com/ytatsuya-private/update-cloudfront-sg)に私が改修したプログラムがあるのでそれを使います。

#SecurityGroupによるアクセス制御方法
##実行環境
LambdaとSNSを使います。
SNSをトリガーとし、lambdaを動かし、実行結果をSNSのメールとして通知します。

##事前準備
###セキュリティグループの作成とタグ付け
以下のセキュリティグループを4つ作成して、下記のタグをつけます。
Name:cfnt_global_http, cfnt_global_https, cfnt_region_http, cfnt_region_https
AutoUpdate:true
Protocol:http or https
※ルールは空で大丈夫です


###IAMポリシー
Lambda用ロールを作成し、ポリシーをアタッチ

```:Policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "arn:aws:ec2:[region]:[account-id]:security-group/*"
        },
        {
            "Effect": "Allow",
            "Action": "ec2:DescribeSecurityGroups",
            "Resource": "*"
        },
        {
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Effect": "Allow",
            "Resource": "arn:aws:logs:*:*:*"
        },
        {
            "Effect": "Allow",
            "Action": "sns:Publish",
            "Resource": "*"
        }
    ]
}
```

##実行環境作成
###Lambda
実行環境：Python2.7
ソースはGithubで公開しています。
https://github.com/ytatsuya-private/update-cloudfront-sg
インスタンスは一番小さいものを選択。
タイムアウトは30秒程度あれば十分です。
作成したロールをアタッチ
GitHubから落としてきたプログラムのTOPIC_ARN定数にSNS完了通知用のARNを記載する。

###SNS
・完了通知用
通知用としてトピックを作成し、サブスクリプションで通知先アドレスを入力

・トリガー用
トリガーにSNSを選択し、下記ARNを指定して追加を実行する。
arn:aws:sns:us-east-1:806199016981:AmazonIpSpaceChanged

##テスト準備
lambdaで仮にAWSからの通知が来た場合を想定して実行してみましょう。

Lambda関数の「テスト」をクリックし、下記ポリシーを貼り付け、作成をクリック

```:テストコード
{
  "Records": [
    {
      "EventVersion": "1.0",
      "EventSubscriptionArn": "arn:aws:sns:EXAMPLE",
      "EventSource": "aws:sns",
      "Sns": {
        "SignatureVersion": "1",
        "Timestamp": "1970-01-01T00:00:00.000Z",
        "Signature": "EXAMPLE",
        "SigningCertUrl": "EXAMPLE",
        "MessageId": "00a95725138f2348131821c6987b5966",
        "Message": "{\"create-time\": \"yyyy-mm-ddThh:mm:ss+00:00\", \"synctoken\": \"0123456789\", \"md5\": \"00a95725138f2348131821c6987b5966\", \"url\": \"https://ip-ranges.amazonaws.com/ip-ranges.json\"}",
        "Type": "Notification",
        "UnsubscribeUrl": "EXAMPLE",
        "TopicArn": "arn:aws:sns:EXAMPLE",
        "Subject": "TestInvoke"
      }
    }
  ]
}
```

##テスト実行
このテストコードで初回実行した際に、MessageIDが異なります、という形でエラーが起きるので、テストコードのMessageIdとMessageにある文字を書き換えます。
上のコードでいう「00a95725138f2348131821c6987b5966」にあたります。

書き換え後、保存して再度テスト実行すると上手く動くようになります。

![update-sg01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475010/75e008bc-4cb1-f21c-7f3e-8419fefb19ab.png)

メール通知も来ています
![update-sg02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/475010/e0c8d703-4324-f9fd-c1ae-249893d80f64.png)


#注意点
トリガーとなるAmazonIpSpaceChangedですが、こちらはAWS全てのリソースのIPの変化を通知するので、プログラムを書き換えて使う方は注意してください。
またテスト時は設定するインバウンドルールを1つ以上削除し、ルールが更新される状態に変更してからテストを実施してください。セキュリティグループの更新が無い場合は、メールを通知しない仕組みにしているためです。


参考
https://github.com/aws-samples/aws-cloudfront-samples/tree/master/update_security_groups_lambda