こんにちは、ロジカル・アーツ株式会社の高橋です。
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の環境構築は割愛致します。まだインストールされていない方は、弊社の入門記事を参考にしていただければ幸いでございます。
Chaliceでプロジェクトを作成
まず、任意のディレクトリ(今回は~/Develop
とします)に移動してプロジェクトを作成します。
$ cd ~/Develop $ chalice new-project demo-api # 以下のスケルトンが作成される # demo-api # ├── .chalice # │ └── config.json # ├── .gitignore # ├── app.py # └── requirements.txt
非常に簡単ですね! それでは、APIの中身の方を見ていきましょう。
app.py
はchaliceの本体となる部分です。@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グループなど)の名称の一部に使用されます。stages
はAPI Gatewayのリソース単位を表します。chaliceのバージョンは0.7.0以上の場合、dev
ステージが自動で追加されています。こちらも、関連するリソースの名称の一部に使用されます。api_gateway_stage
はAPI 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アドレスによる制限をかける手法を紹介しました。 ポリシーは自由度が高く高度なセキュリティを確保できる反面、注意して扱わないとセキュリティインシデントにつながる可能性がある非常に重要な要素です。
システムで構築する全てのリソースに対して、適切なセキュリティが確保されているかを確認するようにしましょう。