API Gateway(HTTP API) で IP アドレス制限する

# HTTP API ではリソースポリシーが使えない
REST API で IP アドレス制限するには、通常、リソースポリシーを使います。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-resource-policies-examples.html#apigateway-resource-policies-source-ip-address-example

でも、後発の HTTP API では、リソースポリシーが未実装です。
AWS WAF も未対応なので、そっちで IP アドレス制限することもできません。

# Lambda Authorizer でできる
昔、REST API にリソースポリシーがなかった頃、IP アドレスで制限するには、Lambda Authorizer を利用していました。
その方法を試してみたところ、HTTP API でも利用できました。

1. HTTP API の Lambda Authorizer のレスポンスモードを `IAM　ポリシーにする`
![スクリーンショット 2023-12-14 17.13.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651869/27f980cd-45dd-c726-eb2a-317bc6dbf391.png)
2. Lambda Authorizer が返す IAM ポリシーの Condition に `IpAddress` と `aws:SourceIp` を追加する

```JavaScript
exports.handler = async function (event, context) {
    const response = {
        principalId: "hoge",
        policyDocument: {
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Action: "execute-api:Invoke",
                    Resource: event.routeArn,
                    Condition: {
                        IpAddress: {
                             "aws:SourceIp": ["192.168.0.1/32"]
                        }
                    }
                }
            ]
        }
    }
    return response;
};
```
この例では、`192.168.0.1` からのリクエストのみ許可されます。

制限したい IP アドレスが 1 つだけなら、Lambda Authorizer のロジックで判定してもあまり変わらないですが、IP アドレスが複数だったり、CIDR のホスト部が `32` より小さい場合、ロジックがややこしくなります。

その点、この方法だと AWS 側が判定してくれるので楽です。

# 他の条件も試してみる
これができるなら、他の [条件キー](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html) も使えるのではと、やってみました。

## [aws:CurrentTime](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-currenttime) ✅
```JavaScript
exports.handler = async function (event, context) {
    const response = {
        principalId: "hoge",
        policyDocument: {
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Action: "execute-api:Invoke",
                    Resource: event.routeArn,
                    Condition: {
                        DateGreaterThan: {
                            "aws:CurrentTime": "2023-12-05T18:10:00+09:00"
                        }
                    }
                }
            ]
        }
    }
    return response;
};
```

`2023/12/05 18:10` より前では Forbidden でしたが、その日時を過ぎるとアクセスできました。
`2023/12/25T00:00:00+09:00` に設定して、日本時間でクリスマスになったら公開するとか使い所がありそう。

逆に `DateLessThan` を使うと、その日時まで公開することもできますね。
期間限定のサイトとかに使えそう。

## [aws:Referer](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-referer) ❌

```JavaScript
exports.handler = async function (event, context) {
    const response = {
        principalId: "hoge",
        policyDocument: {
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Action: "execute-api:Invoke",
                    Resource: event.routeArn,
                    Condition: {
                        StringLike: {
                            "aws:Referer": ["http://example.com/*"]
                        }
                    }
                }
            ]
        }
    }
    return response;
};
```

エラーは起きないですが、無視されるようで、使えず。

一方で、Rest API のリソースポリシーでは使えました。

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": [
                "execute-api:/*"
            ],
            "Condition": {
                "StringLike": {
                   "aws:Referer": ["http://example.com/*"]
                }
            }
        }
    ]
}
```

## [aws:UserAgent](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_condition-keys.html#condition-keys-useragent) ❌

```JavaScript
exports.handler = async function (event, context) {
    const response = {
        principalId: "hoge",
        policyDocument: {
            Version: "2012-10-17",
            Statement: [
                {
                    Effect: "Allow",
                    Action: "execute-api:Invoke",
                    Resource: event.routeArn,
                    Condition: {
                        StringLike: {
                            "aws:UserAgent": ["Mozilla/*"]
                        }
                    }
                }
            ]
        }
    }
    return response;
};
```

こっちもダメ。
でも Rest API のリソースポリシーは OK。

```JSON
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": [
                "execute-api:/*"
            ],
            "Condition": {
                "StringLike": {
                   "aws:UserAgent": ["Mozilla/*"]
                }
            }
        }
    ]
}
```

# まとめ
AWS のグローバル条件キーはたくさんありますが、HTTP API では IP アドレス制限と期間限定以外に使えそうなものはあまりなさそうでした。残念。

結局何が言いたいかというと、HTTP API でもリソースポリシーと WAF を早く実装してください 🙏  > AWS