gokigenmaruのブログ

40から始めるクラウドエンジニア

APIGWのIPフィルター設定がうまくいかない場合がある

こんにちわ、ごきげんまるです。

前回のブログで、暗黙の拒否と明示的な拒否はIAMポリシー以外でも出てくると記載しましたが、今回はその関連でブログ書こうと思います。
これは自分が昔、とある要件でAPIGWにIPフィルターの設定をしたときにハマった話です。

APIGWのIPフィルター設定方法

APIGWでIPフィルターの設定はリソースポリシーで実施します。

f:id:gokigenmaru:20210525095539p:plain
APIGW(IPフィルター設定場所)

リソースポリシーの設定方法は以下のAWSサイトにあります。
docs.aws.amazon.com

フォーマットはIAMポリシーの時と同様にJSON形式で記載します。
慣れれば簡単です。

IPフィルターの書き方

で、皆さん、IPフィルターのイメージってどういう感じでしょう?
Firewallとかをやっている方だと、全部の通信をDenyしてから許可するIPを書く感じですかね。
実は上記のやり方だと通信は全てDenyされます。
なぜかというと、昨日のブログで書いた通り「許可と拒否が一緒に設定されている場合は拒否となる」ためです。
じゃあどうするかというと、パッと思いつくのは明示的な拒否をせずに暗黙の拒否を使うことです。
暗黙の拒否であれば、「記載したIPは許可して記載していないIPは拒否(暗黙の拒否)する」ことが出来るためです。
実際、どう設定するかというと、以下の感じです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:*:*:*/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "AA.AA.AA.AA/32",
                        "BB.BB.BB.BB/32"
                    ]
                }
            }
        }
    ]
}

これでIPフィルターの設定はこちらの想定通り、記載したIPからの通信は許可され、それ以外のIPからの通信は拒否されます。
簡単ですね!

IPフィルターを設定したのに通信が出来る

ただ、この記載方法、APIGWでIPフィルターとは別に認証系の設定(オーソライザー)をしていると、IPフィルターで許可しているIP以外からも通信が出来るようになってしまう場合があります。

なぜかというと、APIGWのアクセスフィルター方式は「掛け合わせ」でのフィルター方式の為です。
つまりこうです。

「通信前提」
IPフィルターで許可されていないIPからの通信、ヘッダでAPIキー(x-api-key)を設定

「設定内容」
・リソースポリシーでIPフィルター(暗黙の拒否)を実装
・オーソライザーでAPIキーによる制限

「APIGWの処理」
・IPフィルターで許可されてないIPからの通信 ←拒否
APIキーで認証 ←許可

「結果」
IPフィルターでは拒否対象だが、APIキーの認証が通っているので通信は許可


という残念な結果になってしまいます。
考え方を変えると、
・IPフィルターで許可した通信またはAPIキーがある通信であればAPIGWへの通信を許可する
という場合においてはこの書き方が有効です。

でも、特定のIPからの通信以外はAPIキーがあろうがなかろうが拒否したい場合はこの書き方ではダメです。
じゃあどうすればいいかと言うと、IPフィルターで「明示的な拒否」を書けばいいのです。

明示的な拒否の書き方でIPフィルターを実施する

オーソライザーの認証があろうがなかろうが特定のIP以外からの通信を拒否する場合、「許可と拒否が一緒に設定されている場合は拒否となる」ことを利用します。
先ほどは暗黙の拒否でIPフィルターを書きましたが、それだと他に認証の設定があると通ってしまいます。
「特定のIP以外は拒否」するのであれば、明示的な拒否の書き方が必要です。

以下が明示的な拒否の設定方法です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:*:*:*/*",
        },
        {
            "Effect": "Deny",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:*:*:*/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "AA.AA.AA.AA/32",
                        "BB.BB.BB.BB/32"
                    ]
                }
            }
        }
    ]
}

注意点は、ブログの上のほうでも書きましたが、すべてDenyをするとそのDenyが有効になってしまうことです。
なので、書き方としては
・EffectでDenyをすることで、このルールは明示的な拒否であることを宣言
・NotIpAddressとすることで、これ以降に記載したIPは通信が許可され、記載していないIPからの通信は全て明示的な拒否とする
となります。

このように設定をすれば、IPフィルターは全ての設定において優先され、IPフィルターを通った後にIPフィルター以外の認証が入るということになります。