Amazon API Gateway でカスタムドメインを使ってパスベースで使える使用量プラン(API キー)を制限してみた

2023.02.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

Amazon API Gateway の機能のひとつに API キーという機能があります。
API キーを使うことで API Gateway は利用者に API へのアクセスを許可することが出来ます。

API キーでは使用量プランを通して関連付けられている全ての API へアクセスすることが出来るため、対象 API のこのパス・リソースのみ許可するといった使い方は通常出来ません。
一方で API Gateway のカスタムドメイン機能ではパスベースで複数 API を割り当てることが出来ます。そのためカスタムドメインと API キーを組み合わせると対象 API キーでアクセス出来るパスを疑似的に制限することが出来ますので試してみました。

なお、本記事ではどういうシーンで API キーを使うべきかなどについては言及しません。
まず、AWS の公式ドキュメント上では「API キーを API の認証または承認に使用しないでください。」と明記されています。理由としては使用量プランで関連づけされている全ての API にアクセス出来るためとのこと。

とはいえ、使用量プランの機能(スロットリング、レート制限、使用量エクスポートなど)の利用を目的に API キーで利用を許可するシーンはよくあると思います。

適当な API 用意

API Gateway + Lambda な REST API を作成します。
Lambda 関数はデフォルトのものを使います。

index.mjs

export const handler = async(event) => {
    // TODO implement
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

Lambda プロキシ統合を有効化しています。

API キーを有効化せずに普通にアクセスすると以下のようになります。

% curl -i https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1
HTTP/2 200 
date: Sun, 05 Feb 2023 00:53:51 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: 1ff5ca2b-2d63-49d2-bc85-d1f8b26eaeeb
x-amz-apigw-id: f1yk4GFtNjMF0bw=
x-amzn-trace-id: Root=1-63defe1f-23b39dbe1f64d2d42ee31def;Sampled=0

"Hello from Lambda!"

良いですね。

API キー有効化と発行

次に、この API に対して API キーを有効化します。
メソッドごとに API Key Required の設定があるので有効化します。

API 側で出来るのは API キーの要求をするかどうかだけです。

次に API キーを発行します。
API 選択後のサイドメニューで表示されますが、 API キーや使用量プランは同一アカウント・リージョン内の API を横断して利用することが出来ます。
API キー自体はランダムあるいは任意の文字列で簡単に発行出来ます。また IAM ユーザーのシークレットと異なりマネジメントコンソールや AWS CLI から値の確認が可能です。

なお AWS アカウントあたり、各リージョンで作成出来る API キーの数は 10000 が上限で、ハードリミットのようです。

これで API キーを発行し、API 側ではキーを要求するようにしました。
クライアントからリクエスト送信時に API キーを使うためには、x-api-keyヘッダーにキーの値を設定する必要があります。
試しに使ってみましょう。

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1"
HTTP/2 403 
date: Sun, 05 Feb 2023 01:29:21 GMT
content-type: application/json
content-length: 23
x-amzn-requestid: 5a1b3d5a-c69e-4a9e-bc3c-3153297e386b
x-amzn-errortype: ForbiddenException
x-amz-apigw-id: f13x0GS3NjMFaJg=

{"message":"Forbidden"}

403 Forbidden となりました。

対象 API キーがどの API へアクセス出来るのかを使用量プランで設定する必要があります。

使用量プランを設定

使用量プランを使って指定した API キーがどの API のステージにアクセス出来るのかを指定出来ます。
また、API キーごとに使用量プランに設定したスロットリングやクォータ制限を設定することも出来ます。
余談ですが SaaS on AWS の文脈ではテナントの Tier をこの使用量プランを使って表現する場合があります。

使用量プランを作成し、API のステージを関連付け出来ます。
さらに、使用量プランに先程作成した API キーを追加します。
これによって対象の API キーはこの使用量に従って API へアクセスすることが出来るようになります。

AWS アカウントあたりのリージョン内で作成出来る使用量プランの上限は 300 となっています。
こちらは上限緩和が可能となっています。

なお、1 つの API キーに関連付けできる使用量プランの上限は 10 までとなっています。

API キーを使ってもう一度リクエストを送信してみましょう。

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge2"
HTTP/2 200 
date: Sun, 05 Feb 2023 02:14:45 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: b1026407-30be-4243-ae9b-0748abe333f2
x-amz-apigw-id: f1-bRFjutjMFmGw=
x-amzn-trace-id: Root=1-63df1114-55529a670c58f4164773d4ed;Sampled=0

"Hello from Lambda!"

今度はアクセスすることが出来ましたね。

カスタムドメインで同一ドメインに複数の API を割り当てる

使用量プランと API キーを使って、API へのアクセスを許可出来るようにしたのですが使用量プランで許可出来る最小単位は API ステージです。
対象 API ステージの全てのリソースにアクセス出来る状態なので、使用量プランごとにアクセス可能なリソースをコントロールすることは出来ません。

ただし、API Gateway のカスタムドメイン機能では複数の API ステージをパスベースでマッピング出来ます。
使用量プランが API ステージ単位で関連付けされるので、エンドユーザーから見るとカスタムドメインを経由する場合は API キーごとになアクセス可能なリソースを変更出来そうです。

ここでは 2 つの API ステージをカスタムドメインにマッピングしています。

Route 53 でカスタムドメインをターゲットにエイリアスレコードを作成します。

使用量プランごとにアクセス出来る API ステージを変更

ここでは使用量プランを 2 つ作成しました。
1 つは API 1 と API 2 にアクセス出来ます。もう 1 つは API 1 へのみアクセス出来ます。
そしてカスタムドメインで API 1 と API 2 を同一ドメインで別のパスにマッピングしました。

では、アクセスしてみましょう。
まずはどちらにもアクセス出来る使用量プランに関連づいている API キーです。

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://hoge0205api.tak1wa.com/api1/hoge1"
HTTP/2 200 
date: Sun, 05 Feb 2023 01:54:54 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: 28ac69e5-1156-46e9-a182-79868349f112
x-amz-apigw-id: f17hNHw6NjMF9mQ=
x-amzn-trace-id: Root=1-63df0c6d-6ebb1f3b135bd5315aa6e017;Sampled=0

"Hello from Lambda!"

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://hoge0205api.tak1wa.com/api2/hoge1"
HTTP/2 200 
date: Sun, 05 Feb 2023 01:58:39 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: 36439317-289a-4da5-a49c-240e90510e93
x-amz-apigw-id: f18EfF2ttjMFpIw=
x-amzn-trace-id: Root=1-63df0d4f-3ef421420875b40d27b33cf4;Sampled=0

"Hello from Lambda!"

どちらの API にもアクセス出来ました。

続いて、API1 のみ許可されている使用量プランの API キーを使ってみます。

% curl -i -H "x-api-key:hoge0205key2aaaaaaaaaa" "https://hoge0205api.tak1wa.com/api1/hoge1"
HTTP/2 200 
date: Sun, 05 Feb 2023 02:00:11 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: 7ca54bf2-aadc-4e8a-be0d-b603f26f38c2
x-amz-apigw-id: f18SwHmbNjMFVuQ=
x-amzn-trace-id: Root=1-63df0dab-7f58d923654bd41c4c152df8;Sampled=0

"Hello from Lambda!"

% curl -i -H "x-api-key:hoge0205key2aaaaaaaaaa" "https://hoge0205api.tak1wa.com/api2/hoge1"
HTTP/2 403 
date: Sun, 05 Feb 2023 02:00:14 GMT
content-type: application/json
content-length: 23
x-amzn-requestid: 30cc9f28-a9cc-4fe6-8450-ec1a9e6774c2
x-amzn-errortype: ForbiddenException
x-amz-apigw-id: f18TTEnoNjMFuQg=

{"message":"Forbidden"}

こちらは API 2 へのアクセス時は拒否されました。
良いですね!

さいごに

本日は Amazon API Gateway でカスタムドメインを使ってパスベースで使える使用量プラン(API キー)を制限してみました。

カスタムドメインを含むパスの設計と API リソースの分離が前提になってしまいますが、API キーごとにアクセス出来るパスを制限することは出来そうです。

冒頭紹介したように認証・認可に API キーを使うことは推奨されていないという前提は置きつつ、API キーを使わざるを得ないユースケースの場合はこういった方法も出来るということは覚えておいても良さそうですね。