AWS Lambda で遊ぼう(第4話: APIを実行できるIPアドレスを制限しよう)

こんにちは、たーせるです。
本日も AWS Lambda と API Gateway の話をしようと思います。

今回のテーマは「AWS SAM で作った API に対して、簡単なアクセス制限を付けてみよう」という内容で、流れとしては第2話の続き*1という形になります。

お勉強しながらゆっくり進んでいるので、有益なコメントなど大歓迎です。

tercel-tech.hatenablog.com

ちなみにこのシリーズを順に読みたい場合は、Lambda カテゴリからどうぞ。

tercel-tech.hatenablog.com
さて前回は、AWS SAM を利用してサクっと REST API を作ったものの、全世界の端末から実行可能な状態となっていた。

もしも会社や学校でこれをやると、偉い人に注意されてしまうことがある。 たとえ機密情報を扱わない実験的な API であっても、身元の明らかではない端末からアクセスされる状態を看過できないというのは、至極もっともな了見でもある*2

【練習】 API を実行できる IP アドレスを制限しよう

多くの会社では、社内 LAN からインターネットへのアクセスはプロキシサーバを経由する仕掛けが構築されている(と思う)。

そして、自社の情報システム部のネットワーク管理者に問い合わせると、社内 LAN からインターネットへの出口となるグローバル IP アドレスの範囲を教えて貰えるケースがある。

つまり、この IP たい以外からのアクセスを全て遮断できれば、理論上はある程度じょうの明らかな端末からしAPI を実行できなくなり、さしあたり最低限の防衛線になるだろう。

template.yaml の修正

以下のように、API の実行を許可する IP 帯をCIDRサイダー形式で yaml に指定することで、簡単にアクセス制限を設定できる。

template.yaml (diff)

  Resources:
    HelloWorldFunction:
      Type: AWS::Serverless::Function
      Properties:
        CodeUri: hello_world/
        Handler: app.lambda_handler
        Runtime: python3.8
        Events:
          HelloWorld:
            Type: Api
            Properties:
              Path: /hello
              Method: get
+             Auth:
+               ResourcePolicy:
+                 IpRangeWhitelist:
+                   - mmm.mmm.mmm.mmm/32
+                   - nnn.nnn.nnn.nnn/32

実際に、template.yaml にリソースポリシーを追加してデプロイしてみよう。

$ sam deploy

f:id:tercel_s:20210508170724p:plain

設定した範囲外の IP アドレスから curlAPI のエンドポイント URL を叩くと、想定どおり拒否されることが確認できる。

{"Message":"User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:ap-northeast-1:********██:██████/Prod/GET/hello with an explicit deny"}

一方で、許可された IP アドレスからは普通にアプリケーションコードが実行され、正常系の応答が返ってくる。 おめでとう。

f:id:tercel_s:20210508162400p:plain

内容としては以上なのだが、この設定によって一体何が起きたかを念の為確認してみよう。

設定の確認

マネジメントコンソールにサインインして、Lambda のトリガになっている API Gateway を確認する。

f:id:tercel_s:20210508162949p:plain

API Gateway の右隣に表示されているアプリケーション名のリンク(下図赤枠)をクリックすると、API Gateway のマネジメントコンソールに飛べる。

f:id:tercel_s:20210508164337p:plain

左側のメニューから「リソースポリシー」をクリックすると、自動的に下図の緑枠の設定が追加されていることが確認できる。

f:id:tercel_s:20210508164442p:plain

これは、指定した IP アドレス範囲以外からの API 実行を拒否Denyするという意味である。

つまりどういうことだって?

template.yaml に書いたたった数行の宣言が、リソースポリシーに化けるんだよ。

Properties:
  # (略)
  Auth:
    ResourcePolicy:
      IpRangeWhitelist:
        - nnn.nnn.nnn.nnn/32

これ (↑) が、これ (↓) に自動変換される。

開発のコストパフォーマンスがめちゃくちゃ良い。

{
  "Version": "2012-10-17",
  "Statement": [
    (略),
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": "execute-api:Invoke",
      "Resource": "arn:aws:execute-api:ap-northeast-1:████████:████████/Prod/GET/hello",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": "nnn.nnn.nnn.nnn/32"
        }
      }
    }
  ]
}

手作業でこんな長ったらしい JSON を書く気にはならない。

ちなみにこの動画 (↓) の 2:50 あたりから、リソースポリシーを設定するシーンが見られるよ。

リソースポリシーの設定ってそんな面倒なの?

うん。

結局、動画でもポリシージェネレータというチートツールを使ったしね。 少なくとも人間が手書きするには優しくないよね。

設定ミスったら終わるし。

あぁなるほど。

たしかに慣れれば template.yaml を書く方が負荷は低いかもね。

まとめ

API Gateway は、基本的に*3デプロイした途端にすぐ API が公開されてしまう。

ひとたび無防備な状態で公開すると、アクセス制限の適用が間に合わずに不測のアクセスがアプリケーションコードへ到達するおそれがある。

そのため、実験段階の API であっても必要なセキュリティ設定はデプロイ前に実施し、できるだけ外部からの被弾リスクを下げることが大切である。

今日はだいぶ話が短いね。

このくらいがいいんだよ。

*1:3話は template.yaml と仲良くなる話なので、それはそれで別の話題。

*2:「お宅の会社のセキュリティ意識低過ぎませんか」というクレームが来る虞がある。

*3:プライベートなエンドポイントを除く。

Copyright (c) 2012 @tercel_s, @iTercel, @pi_cro_s.