📖

AWS - Lambda function URLs を IAM 署名付きでリクエストしてみる

2022/05/10に公開

少し前に、Lambdaで直接URL発行ができるようになったやつ。
使ってみた投稿は多々あるが、どれもIAM署名付きでリクエストしている事例がない。
あったとしても、awscurlとか使ってるので、そのままではアプリケーションに組み込めない。

なので、どうやってIAM署名付きでリクエストできるのか気になったので、試してみる。

AWS_IAM auth type

https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html

Auth typeは、AWS_IAMとNONEの2種類ある。
名前の通り、AWS_IAMを指定すると、IAMで認証できるようになる。
権限としては、Functionに対するlambda:InvokeFunctionUrlの呼び出し権限があれば良いとのこと。

Signature Version 4

IAM 署名付きでリクエストするには、Signature Version 4という方法で現在は署名を付与する必要があるらしい。
Lambdaのドキュメントを見た限りでは、AWS SDKを使っている場合は内部でいい感じにやってくれているが、Function URLsの場合は適当にツール使って頑張ってくれ、とのこと。

https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html

@aws-sdk/signature-v4

流石に自分で処理を組み立てるのは面倒くさいので、AWS SDKで署名を付与する方向で進めてみる。

JS用のSDKに@aws-sdk/signature-v4というパッケージがある。
で、テストコードを見ると、HTTPリクエスト内容に対して、署名を付与してそう。

それらを参考に、署名付きでHTTPリクエストするコードを書いてみると、こんな感じ。

request.js
const { SignatureV4 } = require('@aws-sdk/signature-v4');
const { defaultProvider } = require('@aws-sdk/credential-provider-node');
const { Sha256 } = require('@aws-crypto/sha256-js');
const { HttpRequest } = require('@aws-sdk/protocol-http');

(async () => {
  const signer = new SignatureV4({
    region: 'ap-northeast-1',
    service: 'lambda',
    sha256: Sha256,
    credentials: defaultProvider(),
  });

  const req = await signer.sign(
    new HttpRequest({
      method: 'POST',
      protocol: 'https:',
      path: '/',
      hostname: 'ny4p5y5glmfuyzmv6m2xz6rw640khtse.lambda-url.ap-northeast-1.on.aws',
      headers: {
        host: 'ny4p5y5glmfuyzmv6m2xz6rw640khtse.lambda-url.ap-northeast-1.on.aws',
      },
      body: JSON.stringify({ hello: 'world' }),
    })
  );
  console.log(req);

  const res = await fetch(`${req.protocol}${req.hostname}${req.path}`, {
    method: req.method,
    body: req.body,
    headers: req.headers,
  });
  console.log(await res.json());
})();

これで、リクエストしてみると、レスポンスが返ってきた。

$ node --no-warnings request.js
HttpRequest {
  method: 'POST',
  hostname: 'ny4p5y5glmfuyzmv6m2xz6rw640khtse.lambda-url.ap-northeast-1.on.aws',
  port: undefined,
  query: {},
  headers: {
    host: 'ny4p5y5glmfuyzmv6m2xz6rw640khtse.lambda-url.ap-northeast-1.on.aws',
    'x-amz-date': '20220509T145201Z',
    'x-amz-content-sha256': 'xxxxx',
    authorization: 'AWS4-HMAC-SHA256 Credential=xxxxx/20220509/ap-northeast-1/lambda/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=xxxxx'
  },
  body: '{"hello":"world"}',
  protocol: 'https:',
  path: '/'
}
"Hello from Lambda!"

そして、ヘッダーを外してリクエストしてみると、意図したとおり認証が通らず、Forbiddenで返ってきた。

$ node --no-warnings request.js
...
{"Message":"Forbidden"}

これで、Lambda function URLs を IAM 署名付きでリクエストできたと思う。

まとめ

SDK使った署名付きリクエスト方法ぐらいは、公式ドキュメントに含めても良いような?

Discussion