Lambda関数URLの使い方-CloudFrontを使って安全にLambda関数URLを使いましょ!

Lambda関数URLの使い方!!

Lambda関数URLという便利機能が公開されましたが、

  • Lamdba関数URLって使う上でのリスクないの?
  • Lambda関数URLを安全に使うには?

など、リスクや安全な使い方に関する疑問がありますが、関連する記事はあまり見かけませんので、この記事で記載します。

結論としては、CloudFrontを使ってLambda関数URLを安全に使いましょ!ということです。実装方法が早く知りたい場合は「CloudFront経由でLambda関数URLを使用する」をご参照くださいー。

まず、Lambda関数URLとは?について記載していきます

スポンサーリンク

Lambda関数URLとは

Lambda関数をHTTPS経由で実行するには、Lambdaの前段にAPI Gatewayを設置する必要がありました。 

Lambda関数URLというものが公開され、この機能をONにするだけでLambda関数をHTTPS公開できるようになりました。

API Gatewayを使わずにLambda単体でAPI(HTTPS)公開できるようになったということです。

↓公式記事ですー↓

Lambda 関数 URL - AWS Lambda
関数 URL を使用して、Lambda 関数に専用の HTTP エンドポイントを追加します。

Lambda関数の使い道・どんな用途で使えるか

  • WebAPIとしての利用
    • API GateWayを使用せずに直接Lambdaを呼び出す
      • Lambda関数URLの認証タイプでAWS_IAMを使い、CloudFront経由にアクセスを絞る
  • 非同期処理としての利用(15分以内に終わる処理限定)
    • Lambdaに非同期処理を実装しておく
      • ECS/EC2などのWEBサーバで発生した非同期処理はLambdaで実行するようにしておく
        • Lambda関数URLの認証タイプでAWS_IAMにしておき、WEBサーバ以外のリクエストは受ける気ないようにしておく

Lambda関数URLを使うメリット

WebAPIとして使う場合のメリットを記載します。

まずタイムアウト面についてです。

API Gateway + Lambdaの構成の場合、タイムアウトの上限が29秒という制限がありました。

これがLambda関数URLではLambda側のタイムアウト上限の15分になります。

次に手軽さです。

API Gateway + Lambdaの構成の場合、API Gatewayの知識・ノウハウが必要でしたが、Lambda関数URLではボタン1つで機能をHTTPSで公開できます。

Lambda関数URLの使い方・設定について

Lambda関数URLの危険な使い方と安全な使い方について記載します。

安全な使い方については、CloudFront経由でLambda関数URLにアクセスできるようにしておき、攻撃された場合にはWAFを設定しようといった内容になります。

まず、Lambda関数URLの危険な使い方についてみてみます。

Lambda関数URLの危険な使い方について

Lambda関数URL設定画面で認証タイプをNONEに設定することです。

Lambda関数URL 認証タイプNONE

Lambda関数URL 認証タイプNONE

認証タイプNONEで公開すると攻撃を受けた時にもアクセスを遮断することができず、常にフルオープンとなってしまい危険です。

ちなみに、画面上に以下の文言が表示されます。(上のキャプチャは少し古いので文言違いますが、最新ですと以下の文言となります)

すべてのユーザーに公開されます」がポイントですね。しかもWAFも設定できないので、この使い方は危険と考えています。

認証タイプの NONE を選択すると、Lambda によって以下のリソースベースのポリシーが自動的に作成され、関数にアタッチされます。このポリシーにより、関数が関数 URL を持つすべてのユーザーに公開されます。後でポリシーを編集できます。認証された IAM ユーザーとロールへのアクセスを制限するには、認証タイプの AWS_IAM を選択します。

Lambda関数URLの安全な使い方について

Lambda関数URL設定画面で認証タイプをAWS_IAMに設定することです。

Lambda関数URL 認証タイプAWS_IAM

Lambda関数URL 認証タイプAWS_IAM

AWS_IAMに設定すると直接関数URLにアクセスできなくなります。

関数URLにアクセスできないままでは使えないので、CloudFront経由でアクセスできるようにします。

CloudFront経由にしただけではIAMの権限がついていないので、Lambda@Edgeを使ってLambda の IAM ロールで関数URLにアクセスするようにします。

以下のような流れでアクセスするイメージです。

CloudFront→Lambda@Edge(IAMロールを付与)→Lambda関数URL

これでCloudFront経由でしかアクセスできなくなります。

CloudFrontにはWAFを設定できるため、攻撃を受けた際も通信を遮断することが可能です。

危険な使い方をしてはいけないのか

危険な使い方をしても良いと思いますが、リスクを知った上で認証タイプを設定しましょ!

次にCloudFrontを使って関数URLを使用する実際の手順を記載します

CloudFront経由でLambda関数URLを使用する

まずはLambda関数URLを使用できるようにします

関数の作成ボタンから以下の設定で関数を作成します。

  • 関数名:適切なもの
  • ランタイム:Node.js 18.x
  • 詳細設定->関数URLの有効化にチェックを入れる
    • 認証タイプはAWS_IAMにチェックを入れる

ソースコードは以下のコードを設定し、デプロイします。

(40秒スリープしてからレスポンスを返すプログラムです)


const sleep = (m) => {
  return new Promise((resolve) => setTimeout(resolve, m));
};
export const handler = async(event) => {
    // TODO implement
    await sleep(40000);
    const response = {
        statusCode: 200,
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};


作成した関数のタイムアウト値は1分に変更しておきます

次にCloudFrontを作成します

ディストリビューションを作成ボタンを押下して以下の設定で作成します。

  • オリジンドメイン:Lambda関数URLを貼り付ける
  • 追加設定の「レスポンスタイムアウト」と「キープアライブタイムアウト」を60に設定
  • 上記以外はデフォルトのままとします

次にLambda@edgeを作成します

関数の作成ボタンから以下の設定で関数を作成します。

  • 関数名:適切なもの
  • ランタイム:Python 3.9
  • 上記以外はデフォルトのままとします

ソースコードは以下の通りです。

Sigv4を使って署名してあげます。



import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
import urllib
import base64

session = boto3.Session()
CREDENTIALS = session.get_credentials()
creds = CREDENTIALS.get_frozen_credentials()

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']
    url = f"https://{headers['host'][0]['value']}{request['uri']}"
    method = request['method']
    data = base64.b64decode(request['body']['data'])
    headers = {v[0]['key']:v[0]['value'] for k,v in headers.items()}
    headers.pop('X-Forwarded-For')
    url_segments = urllib.parse.urlparse(url).netloc.split('.')
    region = url_segments[2]
    req = AWSRequest(method=method, url=url, params=None, headers=headers, data=data)
    SigV4Auth(creds, 'lambda', region).add_auth(req)
    signed_headers=dict(req.headers.items())
    request['headers'] = { k.lower():[{'key':k,'value':v}] for k,v in signed_headers.items() }

    return request

Sigv4(AWS 署名バージョン 4)とは

Sigv4(AWS 署名バージョン 4)は、HTTPリクエストをAWSサービスに行う際の認証情報です。HTTPのホスト名やアクセスパス、クエリ、ヘッダーなどの情報とIAMのアクセスキーを元に署名を作成し、それを認証情報としてリクエストに付加して送信します。

以下、公式ページと公式ページの引用です。

AWS API リクエストの署名 - AWS 全般のリファレンス
AWS API リクエストに署名する方法はこちら。

署名バージョン 4 は、AWS 署名プロトコルです。AWS は、マルチリージョン API リクエストの署名をサポートする署名バージョン 4A という拡張機能もサポートしています。詳細については、GitHub で「sigv4a-signing-example」プロジェクトを参照してください。

次にLambdaのロールの信頼関係を以下のように編集します。


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com",
                    "edgelambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

次に以下の設定でトリガーを追加します。

CloudFrontトリガー設定

CloudFrontトリガー設定

次に関数URLを有効にしたLambda関数にアクセス権限を追加します

アクセス権限を追加ボタンを押下し、以下の設定でアクセス権限を追加します。

  • ステートメント ID:適切なもの
  • プリンシパル:Lambda@EdgeのロールのARN
Lambda関数アクセス権追加

Lambda関数アクセス権追加

 動作確認

ディストリビューションドメイン名でアクセスして、

“Hello from Lambda!”

が表示されればOKです。

まとめ

「CloudFront経由でLambda関数URLにアクセスできるようにする」方法で、Lambda関数URLの安全な使い方について紹介させていただきました。

初回の設定は少し面倒ですが、Lambda@Edgeは使い回しできますので、関数を複数公開する場合は徐々に楽になっていくかと思います。

CloudFrontへのWAFの設定は以下の記事をご参照ください

Lambda関数URLにCloudFrontとAWS WAFを使ってIP制限をかける方法
Lambda関数URLをIP制限をかけて公開する方法を記載します。 Lambda関数URLへのアクセスはCloudFront経由のみに絞る。 AWS WAFを使用してCloudFrontへのアクセスにIP制限をかける。 こんな感じのことをやっていきます。