Blogical

AWS/Salesforceを中心に様々な情報を配信していきます(/・ω・)/

【AWS Chalice】API Gatewayで特定のIPアドレスからのみ通信を許可する

こんにちは、ロジカル・アーツ株式会社の高橋です。

AWSでサーバレスなシステムを構築する上で、Amazon API Gatewayを介した通信はよくある手法です。

ただし、そのままAPI GatewayをデプロイしてしまうとAPIは全世界に向けて公開されることとなります。

社内限定で扱うような業務用アプリケーションの場合「特定のIPアドレスからのみ通信を許可したい」ということはよくあることだと思います。

今回はAWS Chaliceで作成したAPI Gatewayに対して「API Gatewayで特定のIPアドレスからのみ通信を許可する」という設定を行おうと思います。

AWS Chaliceについて

AWS Chaliceとは、Pythonで記述するサーバレスなアプリケーションを構築するためのフレームワークです。弊社でも入門記事を執筆しておりますので、公式サイトと併せてご覧ください。

前提条件

項目 バージョン
OS macOS Monterey (Apple M1)
Python 3.9.7
Chalice 1.26.5

なお、本記事ではChaliceの環境構築は割愛致します。まだインストールされていない方は、弊社の入門記事を参考にしていただければ幸いでございます。

blog.logical.co.jp

Chaliceでプロジェクトを作成

まず、任意のディレクトリ(今回は~/Developとします)に移動してプロジェクトを作成します。

$ cd ~/Develop
$ chalice new-project demo-api
# 以下のスケルトンが作成される
#     demo-api
#     ├── .chalice
#     │   └── config.json
#     ├── .gitignore
#     ├── app.py
#     └── requirements.txt

非常に簡単ですね! それでは、APIの中身の方を見ていきましょう。

app.pychaliceの本体となる部分です。@app.route(path)APIを定義しています。

from chalice import Chalice

app = Chalice(app_name='demo-api')


@app.route('/')
def index():
    return {'hello': 'world'}

# 以下略

今回はアクセスできるIPアドレスの制御という主題なので、APIの中身はこのまま使用します。

リソースポリシー用のファイルを作成

プロジェクトの設定やデプロイ情報に関するファイル群は.chaliceディレクトリに置かれます。.chaliceディレクトリ下にresource_policy.jsonを作成しましょう。 今回は例として11.22.33.44からのアクセスからのみ許可する設定*1とします。

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

解説

ポリシーの優先順位は「暗黙的な拒否<明示的な許可<明示的な拒否」となります。

そのため、Statement内の1つ目の要素で全てのユーザーに対して全てのAPIメソッドの呼び出しを許可しています。これは、リソースポリシーは明示的に許可を与えない限り権限を得られないため(=暗黙的な拒否)です。 ここでまず明示的にフルオープンの許可を与えることで、APIがどこからでもアクセスできる状態とします。(=明示的な許可

次に、2つ目の要素でNotIpAddress、つまり「特定のIPアドレス以外(ここでは11.22.33.44)」を拒否しています。(=明示的な拒否

リソースポリシーでは明示的な許可よりも明示的な拒否の方が強いため、拒否のStatementが優先されて処理されます。

このようにすることで、特定のIPアドレスからのみアクセスを許可することが可能となります。

特定のIPアドレスのみAllowとしない理由

前述の通りポリシーの優先順位は「暗黙的な拒否<明示的な許可<明示的な拒否」となります。

API Gatewayのリソースポリシーで「特定のIPアドレス以外は暗黙的な拒否」として処理してしまうと、他のレイヤーのポリシーで「特定のIPアドレス以外は明示的な許可」を記述している場合、アクセス可能になってしまいます。

従って、API Gatewayのリソースポリシーで「特定のIPアドレス以外は明示的な拒否」とする必要があります。

一度フルオープンにする理由

「特定のIPアドレス以外は明示的な拒否」というStatementを書いただけでは、特定のIPアドレスは暗黙的な拒否のままです。

特定のIPアドレスが許可されるためには「特定のIPアドレスに明示的な許可」が必要ですが、許可のStatementにもIPアドレスを書くとメンテナンスの負荷が増加し、インシデントの可能性が上がります。

拒否のStatementがある限り、許可のStatementでフルオープンにしていても必ず特定のIPアドレス以外は拒否されるので、フルオープンとしても影響はありません。

従って、許可のStatementではフルオープンとしておき、拒否のStatementホワイトリストを記述することでメンテナンス負荷を低減させることができます。

リソースポリシーのファイルを指定

Chaliceの設定は基本的に.chalice/config.jsonに記述します。デフォルトで記述されているconfig.jsonの中身を見てみましょう。

{
  "version": "2.0",
  "app_name": "demo-api",
  "stages": {
    "dev": {
      "api_gateway_stage": "api"
    }
  }
}
  • app_nameはデプロイ時にAPI Gatewayのリソース名となる文字列です。また、関連するリソース(Lambda、IAMロール、CloudWatch Logsグループなど)の名称の一部に使用されます。

  • stagesAPI Gatewayのリソース単位を表します。chaliceのバージョンは0.7.0以上の場合、devステージが自動で追加されています。こちらも、関連するリソースの名称の一部に使用されます。

  • api_gateway_stageAPI Gatewayにデプロイされるステージを表し、ベースパスに追加される名称です。

このdemo-apiリソースのdevステージに、リソースポリシーを適用させてみましょう。 api_gateway_policy_fileを追加することで、.chaliceディレクトリ内の指定したファイル名のJSONをリソースポリシーとして適用することができます*2

{
  "version": "2.0",
  "app_name": "demo-api",
  "stages": {
    "dev": {
      "api_gateway_stage": "api",
      "api_gateway_policy_file": "resource_policy.json"
    }
  }
}

これで設定は完了です。実際にデプロイしてみましょう。

デプロイ

Chaliceプロジェクトは非常に簡単です。プロジェクトディレクトリ(~/Develop/demo-api)直下で以下のコマンドを実行して下さい。

$ chalice deploy

Creating deployment package.
Creating IAM role: demo-api-dev
Creating lambda function: demo-api-dev
Creating Rest API
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:<AccountId>:function:demo-api-dev
  - Rest API URL: https://<ApiId>.execute-api.ap-northeast-1.amazonaws.com/api/

これでデプロイは完了です。 マネジメントコンソールから、APIGatewayが作成されていることを確認して下さい。

制限がかかっているかを確認

デプロイに成功するとAPIのURLが表示されるので、curlコマンドで実際にIPアドレスの制限がかかっているかを確認してみましょう。

許可されているIPアドレスからAPIを呼び出した場合

$ curl https://<ApiId>.execute-api.ap-northeast-1.amazonaws.com/api/

{"hello":"world"}

許可されているIPアドレス以外からAPIを呼び出した場合

$ curl https://<ApiId>.execute-api.ap-northeast-1.amazonaws.com/api/

{"Message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:ap-northeast-1:<AccountId>:<ApiId>/api/GET/ with an explicit deny"}

このように、IPアドレスによるAPI呼び出しの制限が可能となりました。

まとめ

今回は、API Gatewayに対してIPアドレスによる制限をかける手法を紹介しました。 ポリシーは自由度が高く高度なセキュリティを確保できる反面、注意して扱わないとセキュリティインシデントにつながる可能性がある非常に重要な要素です。

システムで構築する全てのリソースに対して、適切なセキュリティが確保されているかを確認するようにしましょう。