前回の記事では、Webhook の受け口を Amazon API Gateway で作る方法をまとめました。
この方法で作った API は、URLを知っている人なら誰でも叩けてしまいます。Amazon API Gateway は IAM や API キーでの認証をサポートしていますが、一般的な Webhook は指定された URL に POST を送るだけなので使いづらいです。
しかし、Amazon API Gateway のリソースポリシーを使うと、API の呼び出しを許可する IP アドレス(つまり Webhook の送信元 IP アドレス)を限定することができます。何も無いよりはずっと良いので、今回はこの設定方法をまとめておきおます。
Amazon API Gateway のリソースポリシー対応
昔の Amazon API Gateway には送信元 IP アドレスの制限機能はなく、Amazon API Gateway Lambda オーソライザー(カスタムオーソライザー)で自前で実装する必要があったそうです(※僕は使ったことないのですが、ググると出てきます)。
しかし、2018年4月2日に Amazon API Gateway が API リソースポリシーをサポート開始し、これで IP アドレス制限ができるようになりました。
https://aws.amazon.com/jp/about-aws/whats-new/2018/04/amazon-api-gateway-supports-resource-policies/aws.amazon.com
以下の AWS のドキュメントに、リソースポリシーの例が掲載されています。
Webhook の場合は「送信元 IP アドレスを特定のサービスに限定する」ことができれば十分でしょう。例えば、Backlog というサービスでは以下のように Webhook を実行するサーバーの IP アドレス範囲を公開しています。
この Backlog の Webhook のみを受け取りたい場合、以下のようにリソースポリシーを書けば OK です(リージョンなどは伏せてます)。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Principal": "*", "Action": "execute-api:Invoke", "Resource": [ "arn:aws:execute-api:region:account-id:api-id/*" ], }, { "Effect": "Allow", "Principal": "*", "Action": "execute-api:Invoke", "Resource": [ "arn:aws:execute-api:region:account-id:api-id/*" ], "Condition": { "NotIpAddress": { "aws:SourceIp": [ "52.199.190.133/32", "54.238.59.48/32", "54.65.8.249/32", "52.192.161.184/32", "52.68.222.36/32" ] } } } ] }
Serverless Framework のリソースポリシー対応
Serverless Framework も、2018年4月7日リリースのバージョン1.28でリソースポリシーに対応しました。今では、serverless.yml 内でリソースポリシーを記述できます。
簡単な例が公式ドキュメントに載っています。
serverless.yml に以下のように書くと、先ほどの Backlog の例に挙げたリソースポリシーが自動設定されます。"103.79.14.0/24"
のような範囲指定も可能です。
provider: name: aws runtime: nodejs8.10 resourcePolicy: - Effect: Allow Principal: "*" Action: execute-api:Invoke Resource: - execute-api:/*/*/* Condition: IpAddress: aws:SourceIp: - "52.199.190.133" - "54.238.59.48" - "54.65.8.249" - "52.192.161.184" - "52.68.222.36"
設定結果は、以下のように AWS コンソールで確認できます。
範囲外の IP アドレスからのアクセスはどのように拒否されるか
前回の記事で作った hello 関数で試してみます。
hello 関数は、以下のように JSON でメッセージを受け取って、文字列を付け足して返す関数でした。
$ curl -H 'Content-Type:application/json' -d '{"message":"world"}' http://localhost:3000/hello {"message":"Hello world 2018"}
許可されていない IP アドレスから、この curl コマンドを実行すると、403 とエラーメッセージ(JSON)が返されます。
$ curl -v -H 'Content-Type:application/json' -d '{"message":"world"}' https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/hello * Trying ***.***.***.***... * TCP_NODELAY set * Connected to **********.execute-api.ap-northeast-1.amazonaws.com (***.***.***.***) port 443 (#0) * TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 * Server certificate: *.execute-api.ap-northeast-1.amazonaws.com * Server certificate: Amazon * Server certificate: Amazon Root CA 1 * Server certificate: Starfield Services Root Certificate Authority - G2 > POST /dev/hello HTTP/1.1 > Host: **********.execute-api.ap-northeast-1.amazonaws.com > User-Agent: curl/7.54.0 > Accept: */* > Content-Type:application/json > Content-Length: 19 > * upload completely sent off: 19 out of 19 bytes < HTTP/1.1 403 Forbidden < Content-Type: application/json < Content-Length: 165 < Connection: keep-alive < Date: Sun, 29 Jul 2018 02:08:33 GMT < x-amzn-RequestId: 4ba819ee-92d4-11e8-a7e3-7feb0ae86886 < x-amzn-ErrorType: AccessDeniedException < x-amz-apigw-id: KxIxOGMatjMFhWg= < X-Cache: Error from cloudfront < Via: 1.1 7b63dc372a4330b28d4dd1f11ec139a7.cloudfront.net (CloudFront) < X-Amz-Cf-Id: PPGEdw9vYkOwQejOGAztIO63Atb73po3rzaszbcRpJpXZEQbjVzPIg== < * Connection #0 to host **********.execute-api.ap-northeast-1.amazonaws.com left intact {"Message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:ap-northeast-1:********3987:**********/dev/POST/hello"}
一度設定したリソースポリシーの削除方法
ここはまだ自分でもよくわかっていない部分です。
一度設定したリソースポリシーを消すために、以下のように resourcePolicy
以下をコメントアウトして sls deploy
を実行しても、AWS コンソール上のリソースポリシーは削除されませんでした。
provider: name: aws runtime: nodejs8.10 # resourcePolicy: # - Effect: Allow # Principal: "*" # Action: execute-api:Invoke # Resource: # - execute-api:/*/*/* # Condition: # IpAddress: # aws:SourceIp: # - "52.199.190.133" # - "54.238.59.48" # - "54.65.8.249" # - "52.192.161.184" # - "52.68.222.36"
また、AWS コンソール上で直接、以下のようにリソースポリシーを削除しても、IP アドレス制限は有効なままでした。Amazon API Gateway の設定を変更するだけでは駄目? CloudFormation や CloudFront の設定も絡んでるんでしょうか?
結局、以下のように、すべての IP アドレスを許可するリソースポリシーを設定したあとで、sls deploy
を実行すると、IP アドレス制限を解除することができました。
resourcePolicy: - Effect: Allow Principal: "*" Action: execute-api:Invoke Resource: - execute-api:/*/*/*