4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【サーバーレス初心者向け】Serverless Framework + SwaggerでWeb APIを作る!第2回 WAF適用編(全3回)

Last updated at Posted at 2020-08-12

※2020/12/6追記 DependsOnを使ってCloudFromationテンプレートを書き換えました。

はじめに

こんにちは!
本記事は先日公開した【サーバーレス初心者向け】Serverless Framework + SwaggerでWeb APIを作る!第1回(全3回)の続きとなります。

前回はSwaggerで記載したAPI定義を取り込んだAPI Gatewayと簡単なメッセージを返すだけのLambda関数を実装し、Serverless Frameworkを使ってAWSへデプロイするところまで行いました。

第2回ではAWS WAFを適用し、特定のIPからしかAPI Gatewayにアクセスできないようにします。これはAPI Gatewayはパブリックに公開されてしまうので、なにもアクセス制限をしないとURLさえ知っていればアクセスできてしまうためです。IP制限をかけることで安心して開発ができるようになります。

前回、第三回の記事はこちら

今回作るもの(再掲)

今回は以下のアーキテクト図のようなWeb APIバックエンドを作っていきます。
API GatewayでクライアントからのAPIリクエストを受信し、該当するLambda関数を呼び出し、必要に応じてDynamoDBからのデータ読み出しおよび書き込みを行います。さらに、WAFを適用することでセキュアにします。

slsTestAppArchitect.png

APIとしては、ID・名前・身長・体重・年齢の情報を持つPersonモデルを登録・取得・更新・削除するAPIを作りたいと思います。

  • GET dev/slsTestApp/v1/api/person/{personId}
  • POST dev/slsTestApp/v1/api/person
  • PUT dev/slsTestApp/v1/api/person/{personId}
  • DELETE dev/slsTestApp/v1/api/person/{personId}

第1回目の記事では、具体的なAPIロジックは実装せず、簡単なメッセージを返すだけのLambda関数をバックエンドとするAPI Gatewayを作りました。第2回となる本記事ではWAFを適用していきます。

AWS WAFについて

AWS WAFはCloudFront、API Gateway、ALBなどへのアクセス制限をかけることができるマネージドサービスです。
2019年11月にアップデートが行われており、CloudFromationのテンプレートのリソース名ではAWS::WAFv2と表現されます(古いバージョンはAWS::WAFAWS::WAFRegional)。

GlobalなWAFとRegionalなWAF

AWS WAFには大きく分けて二種類あり、一つはリージョンを持たないグローバルなAWSサービスに紐づけるGlobalなWAFと、もう一つはリージョンを持つAWSサービスに紐づけるRegionalなWAFです。
簡単にいうと、

  • GlobalなWAF → CloudFront
  • RegionalなWAF → API Gateway、ALBほか

という関係になります。それぞれ旧バージョンのWAFではAWS::WAFAWS::WAFRegionalで表現されます。
新バージョンのWAFv2ではGlobalかRegionalかでリソースは分かれず、Scopeプロパティでどちらにするかを指定する方式に変わりました。

ここまで大雑把に新旧のWAFについて説明してきましたが、本記事では新バージョンのWAFv2を使うこととします。
以降、WAFと記載してもそれはWAFv2を指すこととします。

WAFの構成要素

今回はIP制限を掛けるため、以下の三つが大きな構成要素となります。

  • IPセット
  • WebACL
  • Association

各要素について、簡単な説明とテンプレート例を示します。

IPセット

対象のIPを配列形式で指定します。
ここで作成するのはIPのリストのみです。WebACLのアクセスルールでこのIPリストにあるもののみ通信を許可するか(ホワイトリスト形式)、反対にこのIPリストにあるもののみ通信を拒否するか(ブラックリスト形式)選ぶことができます。ほとんどの場合は前者のホワイトリスト形式だと思います。

テンプレート例は以下の通りです。
IPは皆さんの自宅のIPなどを指定してください。

waf.yml(IPセット部分抜粋)
Resources:
  SlsTestAppIPSet:
    Type: AWS::WAFv2::IPSet
    Properties:
      Addresses:
        - ${self:provider.environment.ALLOWEDIP}
      Description: IP set for slsTestApp Access.
      IPAddressVersion: IPV4
      Name: SlsTestAppAPIAllowedIPSet
      Scope: REGIONAL
プロパティ名 説明
Addresses CIDER表記のIPアドレスを配列形式で指定します。上記コード例ではserverless.ymlの環境変数に指定したものを記載する方式にしています
Description IPセットの説明を記載します(任意)
IPAddressVersion IPV4またはIPV6を指定します
Name IPセットの名前を指定します(任意)
Scope CLOUDFRONTもしくはREGIONALを指定します。今回はリージョンごとのサービスであるAPI Gatewayに適用するため、上記コード例ではREGIONALを指定します

WebACL

WAFの具体的なルールを設定します。
テンプレート例は以下の通りです。IPセット構築後に作成したいので、DependsOnでIPセットのリソース名を指定しています。

waf.yml(WebACl部分抜粋)
Resources:
  SlsTestAppWebACL:
    Type: AWS::WAFv2::WebACL
    Properties:
      DefaultAction:
        BLOCK: {}
      Description: WebACL for slsTestApp Access.
      Name: slsTestAppAPIWebACL
      Rules:
        - Action:
            ALLOW: {}
          Priority: 0
          Name: SlsTestAppAPIAccessRule
          VisibilityConfig:
            CloudWatchMetricsEnabled: false
            MetricName: SlsTestAppRuleMetric
            SampledRequestsEnabled: false
          Statement:
            IPSetReferenceStatement:
              Arn:
                "Fn::GetAtt": [SlsTestAppIPSet, Arn]
      Scope: REGIONAL
      VisibilityConfig:
        CloudWatchMetricsEnabled: false
        MetricName: SlsTestAppWebACLMetric
        SampledRequestsEnabled: false
    DependsOn: SlsTestAppIPSet
プロパティ名 説明
DefaultAction WebACLのどのルールにも当てはまらない場合の動作を指定します。AllowもしくはBlockです。ルール外のリクエストは弾きたいので、上記コード例ではBlockを指定しています
Description IPセットの説明を記載します(任意)
Name IPセットの名前を指定します(任意)
Rules ルールの詳細をリスト形式で記載します。

Action→ルールに一致した場合の挙動を指定

Priority→ルールの優先度。最小値は0で、小さい順から優先的にルール判定が行われる

Statement→ルールの具体的な内容を指定します。今回はIP制限のため、IPSetReferenceStatementを指定し、先ほどのIPセットのArnを指定しています
Scope CLOUDFRONTもしくはREGIONALを指定します。今回はリージョンごとのサービスであるAPI Gatewayに適用するため、上記コード例ではREGIONALを指定します
VisibilityConfig CloudWatchメトリクスを送信するかどうか、メトリクス名、Webリクエストのサンプリングを行うかどうか指定します。今回はすべてfalseにしていますが、必要であればtrueに変えてください。

Association

WebACLをどのリソースに適用するかを定義します。
テンプレート例は以下の通りです。WeACL構築後に作成したいので、DependsOnでWebACLのリソース名を指定しています。

waf.yml(Association部分抜粋)
Resources:
  SlsTestAppWebACLAssociation:
    Type: "AWS::WAFv2::WebACLAssociation"
    Properties:
      ResourceArn: arn:aws:apigateway:${self:provider.region}::/restapis/${cf:slsTestApp-${self:provider.stage}.Id}/stages/${self:provider.stage}
      WebACLArn:
        "Fn::GetAtt": [SlsTestAppWebACL, Arn]
    DependsOn: SlsTestAppWebACL

ResourceArnにWAF(正確にはWebACL)を適用したいリソースのArn、 WebACLArnに適用するWebACLのArnを指定します。

API Gatewayは直接Arnを出力することができなさそうだったので、代わりにIDを出力するようAPIGatewayのテンプレートにOutputsを追加しそれを参照するようにしています。
ちなみにServerless Frameworkでは${cf:{スタック名}.{Outputsのキー名}}という書式を使うことで別スタックのOutputsで出力した値を読み込むことができます

api-gateway.yml
Resources:
  ApiGatewayRestApi:
    Type: "AWS::ApiGateway::RestApi"
    Properties:
      Body: ${file(./templates/swagger.yaml)}
…中略…
Outputs:
  Id:
    Value:
      Ref: ApiGatewayRestApi

WAFのデプロイ

serverless.ymlを作成し、sls deployコマンドを実行してください。serverless.ymlの中身はresourcesでテンプレートを読み込むくらいの内容になると思います。

動作確認

キャリアの電波に接続したスマホなどでブラウザを開きAPIのURLを打ち込んでみてください。
{message:Forbidden}などのメッセージが表示されていれば成功です。
APIの実行方法はこちらを参照してください。

おわりに

今回は先日公開した【サーバーレス初心者向け】Serverless Framework + SwaggerでWeb APIを作る!第1回(全3回)で作成したAPI GatewayにAWS WAFを適用する方法を解説しました。
次回はバックエンドのロジックを作りこんでいきたいと思います。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?