APIGatewayで定義したメソッドに対しIAMによる認証ができる。
IAMでの認証ってどういうことかというと、AWS SDKの内部で行っている署名をもって定義したメソッドのエンドポイントにリクエストを送ると他のサービスと同様にIAMユーザーで認証が行える。
つまりCognitoから振り出されたIAMなんかでAPIGatewayを使うことが出来る。そのIAMで直接dynamodbもSNSも叩けるしAPIGateway使うケースあるのかな…? rubyでリクエストつくろうとしたら躓いたのでメモを残しておく
IAMロールを作成
APIを叩く際、サービス名はapigatewayではなくexecute-apiである。利用するIAMのroleでexecute-api:Invoke
の権限が必要になる
{
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": "arn:aws:execute-api:*:*:*"
}
]
}
メソッドを作成
対象のメソッドのMethod RequestでAuthrization type:AWS_IAMを指定。(✔を押さないと適用されない。APIGatewayのGUIはどうにも独特すぎて分かりにくい)
外から叩くのでDeploy APIで適当なステージにデプロイしておく
署名したリクエストを作成
署名を作るの面倒くさいのでGEMを利用する。rubygemsで探すとaws4とaws4_signerの2つが見つかるがどちらもそのまま使おうとするとハマる。特にaws4はホスト名からサービス名を取り出して利用するのだが、APIGatewayのエンドポイントになるホスト名は他のサービスとルールが違うため正しい署名を作ることが出来ない。のでaws4_signerを使う。
require "aws_signer"
signer = Aws4Signer.new(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_ACCESS_SECRET_KEY"], 'us-east-1', 'execute-api')
# GETの場合
uri = URI("https://xxxxxx.execute-api.us-east-1.amazonaws.com/xxxxx/resource")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
request = Net::HTTP::Get.new(uri,
'Accept-Encoding'=>"identity" #identity or gzip
)
signer.sign_http_request(request)
response = http.request(req)
puts response.body
end
Net::HTTP::Get
でリクエストを作る場合、デフォルトでリクエストヘッダに accept-encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
が設定される。
が、Accept-Encodingにgzip
かidentity
の値が入っていると署名が一致せず認証エラーになる。S3などはデフォルトの値でも通ったのでAPIGatewayだけの仕様?情報求む。
リクエスト時のIAMでLambdaを実行
通常、APIGatewayのメソッドに紐付けたLambdaFunctionはLambda側で設定されたロールで実行されるが、Integration RequestのInvoke with caller credentials
にチェックを入れると、リクエスト時のIAMでLambdaを実行することができる。(lambdaの実行権限を持ってないと503になる)
ただしこの設定をするとコンソール画面からのテストが利用できなくなる
未来を感じる
IAMで認証できるしCognitoと外部OAuthプロバイダでがしがしユーザ発行できるのでログインが必要なAPIもこれでいけるかもしれないが死ぬほど面倒くさそう。
現状設定がやたら面倒くさいしコンソール使いにくいし動的にヘッダ吐きだせないしlambdaもデバッグしんどいしログ見難いし辛いことはいっぱいあるけどAPIGatewayでEC2にサヨナラを告げられるかもしれないので期待してる。