AWS

WAF Web ACLをAppSyncに適用する

スポンサーリンク
WAF Web ACLをAppSyncに適用する AWS
スポンサーリンク
スポンサーリンク

WAF Web ACLをAppSyncに適用する

AWS WAFは以下の4種類のリソースに適用することができます。

  • ALB
  • CloudFront
  • API Gateway
  • AppSync

今回はAppSyncに適用する方法を確認します。

なおWAFをALBに適用する方法については、以下のページをご確認ください。

WAFをCloudFrontに適用する方法については、以下のページをご確認ください。

WAFをAPI Gatewayに適用する方法については、以下のページをご確認ください。

構築する環境

AWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版 | 佐々木 拓郎, 林 晋一郎, 金澤 圭 | 産業研究 | Kindleストア | Amazon
Amazonで佐々木 拓郎, 林 晋一郎, 金澤 圭のAWS認定資格試験テキスト AWS認定ソリューションアーキテクト - アソシエイト 改訂第2版。アマゾンならポイント還元本が多数。一度購入いただいた電子書籍は、KindleおよびFire端末、スマートフォンやタブレットなど、様々な端末でもお楽しみいただけます。
Diagram of apply WAF Web ACL to AppSync

WAF Web ACLを作成します。
地理制限を行います。
具体的にはルールグループを自作し、その中に日本(JP)からのアクセスをブロックするルールを設定します。
作成したWeb ACLをAppSyncに適用します。

AppSync内でスキーマを定義し、以下の操作が実現できるよう設定を行います。

  • 扱うデータは2つのフィールド(keyとdatetime)を持つ。
  • 保存されている全データを取得するクエリ。

今回の構成では、AppSyncにデータソースは作成しません。
ですから上記の動作を実現するために、リゾルバに擬似的な仮データを定義します。

AppSyncによるGraphQL APIを実行するクライアントとして、Lambda関数を作成します。
Function URLを有効化し、URLクエリパラメータで実行する操作を指定できるようにします。
関数のランタイム環境はPython3.8とします。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

awstut-fa/055 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

本ページは、AppSyncにWAFを適用する方法を中心に取り上げます。

WAFで地理制限する方法については、以下のページをご確認ください。

データソースがないタイプのAppSyncを構築する方法については、以下のページをご確認ください。

Web ACL

Resources: WebACL: Type: AWS::WAFv2::WebACL Properties: DefaultAction: Allow: {} Name: !Sub "${Prefix}-WebACL" Rules: - Name: !Sub "${Prefix}-WebACL-GeoRestriction" OverrideAction: None: {} Priority: 0 Statement: RuleGroupReferenceStatement: Arn: !GetAtt RuleGroup.Arn VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Sub "${Prefix}-WebACL-GeoRestriction" SampledRequestsEnabled: true Scope: REGIONAL VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Ref Prefix SampledRequestsEnabled: true
Code language: YAML (yaml)

Scopeプロパティがポイントです。
Web ACLをCloudFrontに適用する場合は「CLOUDFRONT」、それ以外のリソースの場合は「REGIONAL」となります。
今回はAppSyncですから後者となります。

Resources: WebACLAssociation: Type: AWS::WAFv2::WebACLAssociation Properties: ResourceArn: !Ref ApiArn WebACLArn: !GetAtt WebACL.Arn
Code language: YAML (yaml)

AWS::WAFv2::WebACLAssociationリソースを作成することで、AppSyncにWAFを適用することができます。
ResourceArnにAppSync APIのARNを指定します。

(参考)AppSync

Resources: GraphQLApi: Type: AWS::AppSync::GraphQLApi Properties: AuthenticationType: API_KEY Name: !Sub "${Prefix}-GraphQLApi" DataSource: Type: AWS::AppSync::DataSource Properties: ApiId: !GetAtt GraphQLApi.ApiId Name: DataSource Type: NONE GraphQLSchema: Type: AWS::AppSync::GraphQLSchema Properties: ApiId: !GetAtt GraphQLApi.ApiId Definition: | schema { query: Query } type Query { listSampleDatas: [SampleData] } type SampleData { key: String! datetime: String } ListSampleDatasResolver: Type: AWS::AppSync::Resolver DependsOn: - GraphQLSchema Properties: ApiId: !GetAtt GraphQLApi.ApiId DataSourceName: !GetAtt DataSource.Name FieldName: listSampleDatas Kind: UNIT RequestMappingTemplate: | { "version": "2018-05-29", } ResponseMappingTemplate: | [ { "key": "hoge", "datetime": "2022-05-15 12:34:56.7890" }, { "key": "foo", "datetime": "2022-05-16 12:34:56.7890" }, { "key": "bar", "datetime": "2022-05-17 12:34:56.7890" } ] TypeName: Query
Code language: YAML (yaml)

データソースなしのAppSyncを作成します。
データソースを用意しないため、リゾルバ内のレスポンスマッピングにデータを定義することで、クライアントからのクエリに応答できるように設定します。

(参考)GraphQLクライアントLambda関数

import json import os import time from gql import Client, gql from gql.transport.aiohttp import AIOHTTPTransport api_key = os.environ['API_KEY'] graphql_url = os.environ['GRAPHQL_URL'] transport = AIOHTTPTransport( url=graphql_url, headers={ 'x-api-key': api_key }) client = Client(transport=transport, fetch_schema_from_transport=True) def lambda_handler(event, context): document = gql( """ query ListSampleDatas { listSampleDatas { key datetime } } """ ) result = client.execute(document) return { 'statusCode': 200, 'body': json.dumps(result, indent=2) }
Code language: Python (python)

GraphQLクエリを実行するLambda関数です。
今回はPython用GraphQLクライアントライブラリとして、GQLを使用します。

GitHub - graphql-python/gql: A GraphQL client in Python
A GraphQL client in Python. Contribute to graphql-python/gql development by creating an account on GitHub.

GQLを使って、スキーマで定義したクエリを実行します。
実行結果をクライアントに返します。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

Lambda関数用のデプロイパッケージを用意する

Lambda関数を作成する場合、3つの方法があります。
今回はデプロイパッケージをS3バケットにアップロードする方法を選択します。
詳細につきましては、以下のページをご確認ください。

Lambdaレイヤー用のデプロイパッケージを用意する

先述のGQLをLambdaレイヤーとして用意します。
Lambdaレイヤーに関する詳細は、以下のページをご確認ください。

なおLambdaレイヤー用パッケージを作成ためのコマンドは以下となります。

$ sudo pip3 install --pre gql[all] -t python $ zip -r layer.zip python
Code language: Bash (bash)

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • AppSync API:fa-055-GraphQLApi
  • GraphQLクライアントLambda関数のFunction URL:https://ygsu4o3c4grz5sjsdxquyqyngi0rxvbw.lambda-url.ap-northeast-1.on.aws/

AWS Management Consoleからもリソースを確認します。
まずAppSyncを確認します。

Detail of AppSync 1.
Detail of AppSync 2.

AppSync APIが作成され、WAFが適用されています。
つまりAPI URLにアクセスしようとすると、その前にWAFで検査が行われるという挙動になります。

ちなみに作成されたAppSync APIの詳細は以下の通りです。

Schema and Resolvers of AppSync.
Detail of Resolvers.

CloudFormationテンプレートで定義した通りにAPIが作成されています。
このAPIにはデータソースが存在しないため、listSamapleDatasクエリを実行すると、レスポンスマッピングに定義された値が返されるという挙動になります。

次にWAFを確認します。

WAF Web ACL is applied to AppSync.

WAF側からも、適用する対象がAppSyncであることが確認できます。

動作確認

準備が整いましたので、GraphQLクライアントLambda関数のFunction URLにアクセスします。

Access to AppSync was blocked by WAF Web ACL.

エラーが返ってきました。
日本リージョンに作成したLambda関数からAppSyncにアクセスしたため、その前面に配置されているWAFによってブロックされたということです。

確認のために、地理制限のアクションを変更します。

WAF rule changed from block to count.

ルールのアクションをブロックからカウント、つまりルールは生かしつつ、ルールに該当するトラフィック数を数えるだけに変更しました。

改めてLambda関数にアクセスします。

AppSync was accessible through the WAF Web ACL.

AppSyncの実行結果が返ってきました。
ルールのアクションがブロックからカウントだけになり、アクセスが可能になったためです。

まとめ

WAF ACLをAppSyncに適用する方法を確認しました。

タイトルとURLをコピーしました