見出し画像

Chalice Authorizer で IP 制限を設定するときの注意点

Solution Architect の t_maru です。
今回は Python で AWS 上にサーバーレスなアプリケーションを構築するためのフレームワークである Chalice で構築した API で、IP アドレス制限を設定する際に考えるべきポイントについて取り上げたいと思います。

Chalice とは

AWS 上にサーバーレスなシステムを構築できるツールは SAM や Serverless など既にいくつかのツールが存在していますが、今回紹介する Chalice は Python を使って簡単に API Gateway + Lambda などのサーバーレスな API を作ることができます。

このフレームワークの良いところは、関数にデコレータをつけるだけであとはフレームワーク側が判断を行って必要な API Gateway (REST API) の設定や Lambda の作成を行ってくれるところだと思います。また、Lambda の起動をスケージュール、CloudWatch, S3, SNS, SQS, DynamoDB など Lambda のイベントソースとしてよく使うものが一通り簡単に設定できることも良い点かと思います。

基本的には AWS の設定はフレームワーク側が自動的に行なってくれるため、それほど AWS の知識がなくても使えるので、さっと作って試したい場合やバックエンドにそれほど明るくないフロントエンドエンジニアだけでプロジェクトを回している場合などにも使い勝手が良いのではないかなと思います。

注意点としては、作り方によってはデプロイのパッケージサイズが大きくなるため Lambda にデプロイできない事態が発生すること、このあと紹介する Authorizer が複数の API で同じものを使い回せない点があります。

Authorizer

フロントエンド向けの API の場合は特にですが、各 API に対して利用できるユーザーや操作を制限する認可の仕組みが必要になってくると思います。Chalice の場合は IAM, Cognito に対応していることはもちろん Custom Authorizer と言って、認可の仕組みを自前でコントロールするための仕組みも用意されています。

各種 Authorizer の記述方法については下記、公式のドキュメントを参照してください。
https://aws.github.io/chalice/topics/authorizers.html

IP アドレス制限をつける

企業向けのサービスの場合などによく出てくるパターンとして、アクセス元の IP アドレスを制限したいケースがあります。この場合は Chalice の config ファイル (.chalice/config.json) の `api_gateway_policy_file` という項目で、リソースポリシーを設定したファイルパスを指定する形で実現することができるのですが、このポリシーファイルを記述する際にはちょっとした注意が必要です。

今回は Custom Authorizer の場合を考えてみますが、各 Authorizer 毎の権限評価の詳細については、下記の公式ドキュメントを参照してください。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-authorization-flow.html

Custom Authorizer で API を call できる/できないを制御するとして、リソースポリシーにより IP アドレス制限をかけることを考えた場合、 `特定 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": ["192.0.2.0/24"]
        }
      }
    }
  ]
}

このポリシーを設定した場合問題となる挙動は、`Custom Authorizer でアクセスを拒否したのに指定した IP レンジからのリクエストはそのまま通過してしまう` というものです。

これは先程挙げた公式のドキュメントを見てもらえれば実装前にわかることではあるのですが、Lambda の Authorizer とリソースポリシーを同時に使用した場合は最終的にリソースポリシーと Custom Authorizer が合わせて評価され、その API に対する認可を与える形となります。

Chalice で簡単に Custom Authorizer を使う方法としては Built-in Authorizer を使う方法があります。下記に公式ドキュメントに記載のある Authorizer の例を転記します。

from chalice import Chalice, AuthResponse

app = Chalice(app_name='demoauth1')


@app.authorizer()
def demo_auth(auth_request):
    token = auth_request.token
    # This is just for demo purposes as shown in the API Gateway docs.
    # Normally you'd call an oauth provider, validate the
    # jwt token, etc.
    # In this example, the token is treated as the status for demo
    # purposes.
    if token == 'allow':
        return AuthResponse(routes=['/'], principal_id='user')
    else:
        # By specifying an empty list of routes,
        # we're saying this user is not authorized
        # for any URLs, which will result in an
        # Unauthorized response.
        return AuthResponse(routes=[], principal_id='user')


@app.route('/', authorizer=demo_auth)
def index():
    return {'context': app.current_request.context}

https://aws.github.io/chalice/topics/authorizers.html#built-in-authorizers 

Built-in Authorizer では、アクセスを許可する API path に対して `Allow`、許可しない場合は何も指定しないという形で書くことになるので、先程のリソースポリシーと合わせて評価した場合、指定された IP レンジからのアクセスであれば明示的に許可されており、それを拒否する明示的な宣言は書かれていないことになるため、先程書いたように `Authorizer ではアクセスを拒否したのに、指定している IP レンジからのリクエストだった場合はそのまま許可されてしまう` という状況が生じることになります。

この問題はリソースポリシーの表現を変更することで解決することができます。Built-in Authorizer と共に使用して期待通りの動作をさせるためのリソースポリシーは下記のようになります。

{
  "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": ["192.0.2.0/24"]
        }
      }
    }
  ]
}

前述のポリシーと変わった点は下記の 2 点です。

  • Effect が Deny になった

  • Condition で NotIpAddress での IP レンジ指定になった

文章で表すと `特定アドレスからの通信を許可する` という考え方から `特定アドレス以外からの通信は拒否する` という考え方に変更したことになります。

こうすることで、先程と同様に Authorizer とこのリソースポリシーをあわせて評価した場合には、特定の IP レンジ以外からの通信はリソースポリシーにより拒否され、特定範囲内からの通信に対してはすべてを許可する明示的な宣言はないため、Authorizer でアクセスが許可されなければ暗黙的な Deny がはたらき、リクエストが拒否されるようになります。

まとめ

今回は Chalice を使った場合の Authorizer と IP アドレス制限を設定する際の注意点について説明しました。
Chalice が題材となってはいますが、今回説明したリソースポリシーの書き方については Lambda Authorizer と API Gateway のリソースポリシーを併用した場合も同様ですので参考にしていただければ幸いです。


この記事が気に入ったらサポートをしてみませんか?