この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
API GatewayとLambdaで作るAPIがあります。本記事では、このAPIにIAM認証を適用してみました。 これらの仕組みをAWS SAMで作成します。
おすすめの方
- AWS SAMでAPI GatewayのIAM認証をしたい方
- AWS SAMでAPI(API Gateway)を作りたい方
- AWS SAMでOpen APIを使いたい方
AWS SAMでAPIを作成する
sam init
sam init \
--runtime python3.9 \
--name Api-IAM-Sample \
--app-template hello-world \
--package-type Zip
OpenAPI定義
GET /hello
を作成します。
api.yaml
openapi: 3.0.1
info:
title: sample api
version: 1.0.0
paths:
/hello:
get:
tags:
- hello
responses:
200:
$ref: "#/components/responses/200"
403:
$ref: "#/components/responses/403"
500:
$ref: "#/components/responses/500"
security:
- awsSigv4: []
x-amazon-apigateway-integration:
type: aws_proxy
uri:
'Fn::Sub': >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${HelloWorldFunction.Arn}/invocations
httpMethod: POST
responses:
default:
statusCode: 200
passthroughBehavior: when_no_templates
contentHandling: CONVERT_TO_TEXT
components:
securitySchemes:
awsSigv4:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: awsSigv4
responses:
200:
description: Success
403:
description: User or client is not authorized.
500:
description: Internal Server Error
SAMテンプレート
次のリソースを定義しています。
- IAMユーザ
- IAMユーザ用のIAMポリシー
- API Gateway
- Lambda
- Lambda用のロググループ
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Api-IAM-Sample
Parameters:
StageName:
Type: String
Default: v1
Resources:
# IAM認証で使うIAMユーザ(アクセスキーを使う)
IamUser:
Type: AWS::IAM::User
Properties:
UserName: !Sub api-gateway-user
IamUserPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Sub api-gateway-user-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: execute-api:Invoke
Resource: !Sub arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${HelloWorldApi}/${StageName}/GET/hello
Users:
- !Ref IamUser
HelloWorldApi:
Type: AWS::Serverless::Api
Properties:
StageName: !Ref StageName
Auth:
DefaultAuthorizer: AWS_IAM
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location: s3://cm-fujii.genki-deploy/Api-IAM-Sample-Stack/api.yaml
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.9
Timeout: 3
Architectures:
- x86_64
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
RestApiId: !Ref HelloWorldApi
HelloWorldFunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${HelloWorldFunction}
Outputs:
HelloWorldApi:
Value: !Sub "https://${HelloWorldApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}/hello"
Lambdaコード
app.py
import json
def lambda_handler(event, context):
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world",
}),
}
デプロイ
aws s3 cp \
api.yaml \
s3://cm-fujii.genki-deploy/Api-IAM-Sample-Stack/api.yaml
sam build
sam package \
--output-template-file packaged.yaml \
--s3-bucket cm-fujii.genki-deploy
sam deploy \
--template-file packaged.yaml \
--stack-name Api-IAM-Sample-Stack \
--s3-bucket cm-fujii.genki-deploy \
--capabilities CAPABILITY_NAMED_IAM \
--no-fail-on-empty-changeset
エンドポイント取得
$ aws cloudformation describe-stacks \
--stack-name Api-IAM-Sample-Stack \
--query 'Stacks[].Outputs'
[
[
{
"OutputKey": "HelloWorldApi",
"OutputValue": "https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello"
}
]
]
APIにアクセスしてみる(失敗する)
SigV4署名ヘッダが無いので、アクセスに失敗します。
$ curl https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello
{"message":"Missing Authentication Token"}
SigV4署名ヘッダを使って、APIにアクセスする
アクセスキーを取得する
下記コマンドでアクセスキーを取得します。マネジメントコンソールから取得してもOKです。
aws iam create-access-key \
--user-name api-gateway-user
APIアクセス用のスクリプトを作成する
Postmanを使えば簡単に動作確認できますが、今回は、Pythonを使ってアクセスしてみます。
次のライブラリを利用します。
- Requests: HTTP for Humans™ — Requests 2.27.1 documentation
- tedder/requests-aws4auth: Amazon Web Services version 4 authentication for the Python Requests module
pip install requests
pip install requests-aws4auth
スクリプト本体は次のコードです。さきほど取得したAWSアクセスキーを利用します。
sample.py
import requests
from requests_aws4auth import AWS4Auth
API_ENDPOINT = 'https://xxx.execute-api.ap-northeast-1.amazonaws.com/v1/hello'
auth = AWS4Auth(
'your access key',
'your secret key',
'ap-northeast-1',
'execute-api',
)
response = requests.get(API_ENDPOINT, auth=auth)
print(response.status_code)
print(response.json())
APIにアクセスする
成功しました!
$ python sample.py
200
{'message': 'hello world'}
参考
- AWS::Serverless::Api - AWS Serverless Application Model
- ApiAuth - AWS Serverless Application Model
- API を呼び出すためのアクセスの制御 - Amazon API Gateway
- 署名バージョン 4 を使用した AWS リクエストへの署名 - AWS 全般のリファレンス
- API Gateway REST API の IAM 認証を有効にする
- AWS API Gateway | Postman Learning Center
- tedder/requests-aws4auth: Amazon Web Services version 4 authentication for the Python Requests module
- x amazon-apigateway-authtype プロパティ - Amazon API Gateway
- [OpenAPI] AWS SAMでLambdaオーソライザーを「適用するLambda」と「適用しないAPI」を作ってみた | DevelopersIO