無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Amazon API Gateway で API (Webhook) の呼び出し元 IP アドレスを制限する

f:id:muziyoshiz:20180630010936p:plain

前回の記事では、Webhook の受け口を Amazon API Gateway で作る方法をまとめました。

muziyoshiz.hatenablog.com

この方法で作った 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 のドキュメントに、リソースポリシーの例が掲載されています。

docs.aws.amazon.com

Webhook の場合は「送信元 IP アドレスを特定のサービスに限定する」ことができれば十分でしょう。例えば、Backlog というサービスでは以下のように Webhook を実行するサーバーの IP アドレス範囲を公開しています。

backlog.com

この 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 内でリソースポリシーを記述できます。

github.com

簡単な例が公式ドキュメントに載っています。

serverless.com

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 コンソールで確認できます。

f:id:muziyoshiz:20180729112632p:plain:w800

範囲外の 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 の設定も絡んでるんでしょうか?

f:id:muziyoshiz:20180729112647p:plain:w800

結局、以下のように、すべての IP アドレスを許可するリソースポリシーを設定したあとで、sls deploy を実行すると、IP アドレス制限を解除することができました。

  resourcePolicy:
    - Effect: Allow
      Principal: "*"
      Action: execute-api:Invoke
      Resource:
        - execute-api:/*/*/*