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関数の使い道・どんな用途で使えるか
- WebAPIとしての利用
- API GateWayを使用せずに直接Lambdaを呼び出す
- Lambda関数URLの認証タイプでAWS_IAMを使い、CloudFront経由にアクセスを絞る
- CloudFrontではWAFを使えるので、Lambda関数URLを安全に使うことができる
- 実装方法は本記事で記載しています。「CloudFront経由でLambda関数URLを使用する」をご参照ください
- Lambda関数URLの認証タイプでAWS_IAMを使い、CloudFront経由にアクセスを絞る
- API GateWayを使用せずに直接Lambdaを呼び出す
- 非同期処理としての利用(15分以内に終わる処理限定)
- Lambdaに非同期処理を実装しておく
- ECS/EC2などのWEBサーバで発生した非同期処理はLambdaで実行するようにしておく
- Lambda関数URLの認証タイプでAWS_IAMにしておき、WEBサーバ以外のリクエストは受ける気ないようにしておく
- ECS/EC2などのWEBサーバで発生した非同期処理はLambdaで実行するようにしておく
- Lambdaに非同期処理を実装しておく
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に設定することです。
認証タイプNONEで公開すると攻撃を受けた時にもアクセスを遮断することができず、常にフルオープンとなってしまい危険です。
ちなみに、画面上に以下の文言が表示されます。(上のキャプチャは少し古いので文言違いますが、最新ですと以下の文言となります)
「すべてのユーザーに公開されます」がポイントですね。しかもWAFも設定できないので、この使い方は危険と考えています。
認証タイプの NONE を選択すると、Lambda によって以下のリソースベースのポリシーが自動的に作成され、関数にアタッチされます。このポリシーにより、関数が関数 URL を持つすべてのユーザーに公開されます。後でポリシーを編集できます。認証された IAM ユーザーとロールへのアクセスを制限するには、認証タイプの AWS_IAM を選択します。
Lambda関数URLの安全な使い方について
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を設定できるため、攻撃を受けた際も通信を遮断することが可能です。
危険な使い方をしてはいけないのか
危険な使い方をしても良いと思いますが、リスクを知った上で認証タイプを設定しましょ!
- リスクを負い、認証タイプをNONEに設定する
- Lambda関数URLの良いところを生かすことができる
- リスクを負わず、認証タイプをAWS_IAMに設定する
- Lambda関数URLの良いところが若干失われるが安全に使用できる
次に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のアクセスキーを元に署名を作成し、それを認証情報としてリクエストに付加して送信します。
以下、公式ページと公式ページの引用です。
署名バージョン 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"
}
]
}
次に以下の設定でトリガーを追加します。
次に関数URLを有効にしたLambda関数にアクセス権限を追加します
アクセス権限を追加ボタンを押下し、以下の設定でアクセス権限を追加します。
- ステートメント ID:適切なもの
- プリンシパル:Lambda@EdgeのロールのARN
動作確認
ディストリビューションドメイン名でアクセスして、
“Hello from Lambda!”
が表示されればOKです。
まとめ
「CloudFront経由でLambda関数URLにアクセスできるようにする」方法で、Lambda関数URLの安全な使い方について紹介させていただきました。
初回の設定は少し面倒ですが、Lambda@Edgeは使い回しできますので、関数を複数公開する場合は徐々に楽になっていくかと思います。
CloudFrontへのWAFの設定は以下の記事をご参照ください