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アクションが対象のユーザに許可されている必要があります。
両方の設定が必要です。