IAM認証付きのLambda-HTTPSエンドポイントにPythonとSigV4でアクセス

LambdaにHTTPSエンドポイントが生やせるようになったので、API GatewayみたいにIAM認証ヘッダ付きのHTTPリクエストでアクセス試してみます。

AWSでは認証付きのAPIを叩くときは署名バージョン 4 (SigV4) の認証情報をつけるのが一般的で、LambdaのHTTPSエンドポイントの場合も同様です。

lambda作成

HTTPSエンドポイントがあるlamdbaを今回はマネジメントコンソールから作ります。

テンプレートは「一から作成」を選び、適当な名前をつけます。詳細設定のところに関数URLを有効化とあるのでチェックをつけ、認証タイプにAWS_IAMを選びます。

CORSも設定できるみたいで良いですね。

作成するとコンソールでURLが確認できます。ドメイン名がlambda-url.<region名>.on.awsと面白いですね。

コードはデフォルトのステータスコード200返すやつです。

関数URLをとりあえずクリックしてみると弾かれます。認証ついてないので正常動作ですね。

認証情報付きでリクエスト

Pythonでsigv4署名つけてリクエストしてみます。9~13行目をご自身のものに変えてください。

import sys, os, base64, datetime, hashlib, hmac 
import requests # pip install requests

method = 'GET'
service = 'lambda'
request_parameters = ''

# ここを自分のものに変える
region = 'ap-northeast-1'
host = 'bg7t4rkf5t2lsbmqnhv3cbai3q0zyylv.lambda-url.ap-northeast-1.on.aws'
endpoint = 'https://bg7t4rkf5t2lsbmqnhv3cbai3q0zyylv.lambda-url.ap-northeast-1.on.aws/'
access_key = 'AKIsample'
secret_key = 'yoursecretkey'
# ここを自分のものに変える おわり

if access_key is None or secret_key is None:
    print('No access key is available.')
    sys.exit()


def sign(key, msg):
    return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()

def getSignatureKey(key, dateStamp, regionName, serviceName):
    kDate = sign(('AWS4' + key).encode('utf-8'), dateStamp)
    kRegion = sign(kDate, regionName)
    kService = sign(kRegion, serviceName)
    kSigning = sign(kService, 'aws4_request')
    return kSigning

# Create a date for headers and the credential string
t = datetime.datetime.utcnow()
amzdate = t.strftime('%Y%m%dT%H%M%SZ')
datestamp = t.strftime('%Y%m%d') # Date w/o time, used in credential scope


canonical_uri = '/' 
canonical_querystring = request_parameters
canonical_headers = 'host:' + host + '\n' + 'x-amz-date:' + amzdate + '\n'
signed_headers = 'host;x-amz-date'
payload_hash = hashlib.sha256(('').encode('utf-8')).hexdigest()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = datestamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' +  amzdate + '\n' +  credential_scope + '\n' +  hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
signing_key = getSignatureKey(secret_key, datestamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' +  'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = {'x-amz-date':amzdate, 'Authorization':authorization_header}
request_url = endpoint + '?' + canonical_querystring

print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print(f'Request URL = {request_url}\nHeaders = {headers}')
r = requests.get(request_url, headers=headers)

print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
print(r.text)

上記のコードを保存しローカルから実行してみると、HTTPリクエストが許可されてlambdaが実行され、結果が返ってきました。簡易APIという感じで良いですね。今後いろいろと活躍するかもしれません。

IAM設定

うえでさらっとアクセス試しましたが、IAM認証を有効にした場合、事前にIAMの権限設定が必要です。具体的には使うアクセスキーのユーザにしかるべき権限をつけます。

実行しようとしているLambda同じAWSアカウント内なのか、別AWSアカウントなのかで必要となる権限が異なります。

同じAWSアカウントのユーザがアクセスする場合

アイデンティティベースポリシーでlambda:InvokeFunctionUrlアクションが対象のlambdaに許可されているか、

リソースベースポリシーでlambda:InvokeFunctionUrlアクションが対象のユーザに許可されている必要があります。

どちらかがあればOKです。

異なるAWSアカウントのユーザがアクセスする場合

アイデンティティベースポリシーでlambda:InvokeFunctionUrlアクションが対象のlambdaに許可されており、

リソースベースポリシーでlambda:InvokeFunctionUrlアクションが対象のユーザに許可されている必要があります。

両方の設定が必要です。

コメントする

メールアドレスが公開されることはありません。