Lambda オーソライザーを設定したAPI Gatewayのリソースポリシーに注意

# はじめに
AWSでサーバーレスwebアプリケーションを作りました．
アーキテクチャはこんな感じ
![serverless-architecture.png](https://qiita-image-store.s3.amazonaws.com/0/156802/477c2a3c-1eaa-7d2d-14ee-11cb7e9521cb.png)
認可処理は，cookieに設定したJWTにより検証を行っています．

# 表題の件
図にあるように，API GatewayへのアクセスはリソースポリシーによるIP制限をかけていました．
こんな感じで

```:リソースポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:<region>:<account-id>:<api-id>/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx"
                    ]
                }
            }
        }
    ]
}
```
いわゆるホワイトリストでIP制限をかけています． 特定のIPリソースからのアクセスのみ許可するという風に．

そして今回設定したAPI Gateway のリソースは大きく分けて以下の2種類です．
1. Lambdaオーソライザーを設定しないリソース(loginページ等)
2. Lambdaオーソライザーを設定したリソース(プロキシリソース)
![スクリーンショット 2018-10-16 16.44.54.png](https://qiita-image-store.s3.amazonaws.com/0/156802/963385e6-74ea-1b1c-679a-41cf110289d2.png)

ここで，上記1. のケースではリソースポリシーによる意図したIP制限が行われましたが，**2. のケースでは意図した挙動をしてくれませんでした．**


# 明示的/暗黙的な許可/拒否の話
Lambdaオーソライザーを設定した場合にリソースポリシーが適用されない原因は，ポリシーの評価方法が異なるためです．

デフォルトではAPI GatewayへのアクセスポリシーはDenyから始まります．先に示したリソースポリシーでは，特定のIPリソースからのアクセスだと判断されればAllowとするというものなので，結果的にポリシーはAllowとなります．(明示的な許可)

一方，Lambdaオーソライザーを設定した場合は，リソースポリシーとLambdaオーソライザーから返却されるポリシーを合わせて評価されます．
それぞれのポリシーの評価結果による最終的な動作は，以下のようになります．

| Lambdaオーソライザーポリシー | API Gateway リソースポリシー | 結果として生じる動作 |
|:--|:--|:--|
| 許可 | 許可 | 許可 |
| 許可 | 許可も拒否もしない | 許可 |
| 許可 | 拒否 | 明示的な拒否 |
| 許可も拒否もしない | 許可 | 許可 |
| 許可も拒否もしない | 許可も拒否もしない | 暗黙的な拒否 |
| 許可も拒否もしない | 拒否 | 明示的な拒否 |
| 拒否 | 許可 | 明示的な拒否 |
| 拒否 | 許可も拒否もしない | 明示的な拒否 |
| 拒否 | 拒否 | 明示的な拒否 |
> [API Gateway リソースポリシーと IAM ポリシーの間のやり取り](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-resource-policies-iam-policies-interaction.html)

ホワイトリスト以外のIPリソースからのアクセスの場合，(先に記したリソースポリシーでは，特定のIPリソースからのアクセスには明示的な許可がされていますが，)**明示的な拒否が設定されていない**ため，上記の表での**許可も拒否もしない**に当たります．
つまり，正しいJWTを設定したアクセスが行われた場合，Lambdaオーソライザーポリシーは許可なので，アクセス元のIPリソースが何であっても，結果として**Allow**となっていた，ということになります．


> [ポリシーの評価論理](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html#policy-eval-denyallow)
> [明示的な拒否と暗黙的な拒否の違い](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html#AccessPolicyLanguage_Interplay)

# 事象のまとめ
- リソースポリシー単体とLambdaオーソライザーを組み合わせた場合のポリシー評価方法は異なる．
- 明示的な拒否がされていない場合，いずれかのポリシーが許可の場合，結果的にAllowとなる．
- 今回のケースでは，明示的な拒否がされていなかった．

# 解決策
リソースポリシーに**明示的な拒否**を加えることで，意図した動作をさせることが可能です．

```:リソースポリシー
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:<region>:<account-id>:<api-id>/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": [
                "arn:aws:execute-api:<region>:<account-id>:<api-id>/*/",
                "arn:aws:execute-api:<region>:<account-id>:<api-id>/*/login",
                "arn:aws:execute-api:<region>:<account-id>:<api-id>/*/initiate-auth",
                "arn:aws:execute-api:<region>:<account-id>:<api-id>/*/confirm-forgot-password"
            ],
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx",
                        "xxx.xx.xx.xxx"
                    ]
                }
            }
        }
    ]
}

```

- 明示的な拒否では，特定のIPリソースからのアクセスは拒否
- Lambdaオーソライザーを設定しないAPIリソースに対しては,明示的な許可を設定

これで，特定のIPリソースからのアクセスのみに制限することができました．