この記事は、リンクバルアドベントカレンダー2022の1日目の記事です。
はじめに
SigV4署名を付与してIAM認証のAPI GatewayにHTTPリクエストをするRubyとGoのサンプルコードを紹介します!
AWSのAPIを使うには、SigV4署名が必要です。AWS CLIやAWS SDKは、SigV4署名をよしなにやってくれるので、APIを使う側がSigV4署名を意識することはありません。
しかし、IAM認証をしたAPI GatewayのAPIをリクエストするときなど、単純なHTTPリクエストの際には、自前で署名を付与する必要があります(AWSのAPIには、SigV4署名を付けてAPI Gatewayをリクエストしてくれる便利なものは存在しません)。
署名の付与は自前ですが、署名をするためのライブラリは用意されているので、そちらを使えばすんなり署名ができます。
Ruby
ドキュメント
サンプルコード
重要な部分はコメントで説明しています。
require 'aws-sdk-core'
require 'net/http'
require 'uri'
require 'json'
require 'yaml'
# SigV4署名を行うオブジェクトを作成
signer = Aws::Sigv4::Signer.new(
service: 'execute-api',# API GatewayのAPIを呼ぶためのserviceの指定
region: 'ap-northeast-1',
credentials_provider: Aws::Credentials.new('xxx', 'xxxxx'), # 認証情報の取得。詳細は後述。
)
# API Gatewayのエンドポイント
api_url = 'https://xxx.execute-api.ap-northeast-1.amazonaws.com/xxx/xxx'
body_data = {
hoge: "xxxx",
foo: "xxx"
}
req_body = body_data.to_json
# SigV4署名を行う。HTTP Method, URL, bodyを引数に渡す
# signature.headers が、SigV4署名されたhttp headerです
signature = signer.sign_request(
http_method: 'POST',
url: api_url,
body: req_body,
)
signature.headers['Content-Type'] = 'application/json'
uri = URI.parse(api_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# 署名されたheaderを、HTTPリクエストのheaderに追加
response = http.post(uri.path, req_body, signature.headers)
credentials_providerについて
credentials_providerは、以下の5種類があります。
漏洩のリスクやキーの管理など、主にセキュリティ的に InstanceProfileCredentials
か ECSCredentials
の利用が望ましいです。極力アクセスキーを利用するのは避けましょう。
- Aws::Credentials
- ACCESS_KEY_ID, SECRET_ACCESS_KEY, SESSION_TOKEN(任意) を利用
- コード例:
Aws::Credentials.new('xxxx', 'xxxxxxxxx')
- 第三引数がSESSION_TOKENで、指定しなければnilになります
- ドキュメント https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Credentials.html
- Aws::SharedCredentials
- SharedCredentials、つまり
~/.aws/credentials
を利用 - コード例:
Aws::SharedCredentials.new
- ドキュメント https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/SharedCredentials.html
- SharedCredentials、つまり
- Aws::InstanceProfileCredentials
- EC2にアタッチされているIAMロールを利用
- EC2上で動作するプログラムでしか、利用できません
- コード例:
Aws::InstanceProfileCredentials.new
- ドキュメント https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/InstanceProfileCredentials.html
- EC2にアタッチされているIAMロールを利用
- Aws::AssumeRoleCredentials AssumeRole
- AssumeRoleを利用
- コード例:
Aws::AssumeRoleCredentials.new(client: Aws::STS::Client.new(...), role_arn: "linked::account::arn", role_session_name: "session-name" )
- ドキュメント https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/AssumeRoleCredentials.html
- Aws::ECSCredentials
- ECSのタスク定義のIAMロールを利用
- コード例:
Aws::ECSCredentials.new(retries: 3)
- ドキュメント https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/ECSCredentials.html
Go
ドキュメント
サンプルコード
注意:SDKがv1のコードです
package main
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
)
type RequestStruct struct {
Hoge string `json:"hoge"`
Foo string `json:"foo"`
}
func main() {
// API Gatewayのエンドポイントを指定
apiURL := "https://xxx.execute-api.ap-northeast-1.amazonaws.com/xxx"
data := RequestStruct {
Hoge: "xxx",
Foo: "yyy",
}
body, err := json.Marshal(data)
if err != nil {
fmt.Printf("error: %+v\n", err)
}
// *Reader型のreqBodyを作成
reqBody := strings.NewReader(string(body))
req, err := http.NewRequest(http.MethodPost, apiURL, reqBody)
if err != nil {
fmt.Printf("error: %+v\n\n", err)
}
req.Header.Set("Content-Type", "application/json")
// 認証情報を取得
sess := session.Must(session.NewSession())
creds := stscreds.NewCredentials(sess, "arn:aws:iam::xxxxxxxx:role/xxxxxxxxxxxx")
// SigV4署名を行うsignerを作成
signer := v4.NewSigner(creds)
// SigV4署名
signer.Sign(
req, // *http.Request
reqBody, // io.ReadSeeker
"execute-api", // AWSのService
"ap-northeast-1", // region
time.Now(), // 署名時刻
)
client := new(http.Client)
res, err := client.Do(req)
if err != nil {
fmt.Printf("error: %+v\n\n", err)
}
defer res.Body.Close()
}
SDKがv2だと書き方が変わるため、ドキュメントや以下のような記事をご覧ください。
Credentialsについて
Rubyと同じく方法がいくつかあるので、ドキュメントをご覧ください!(ぶん投げ)
クラスメソッドさんの記事がわかりやすいです。
とりあえずリクエストだけしたいとき
そんなときは curl
の "--aws-sigv4"
オプションを付ければ、可能らしいです。
また、awscurl
というSigV4署名を付けてcurlしてくれるツールもあります。profileなどの指定が、aws cliと同じ用にできるのでらくちんです。
おわりに
IAMの理解が浅いと、Credentialの取得やAPI Gatewayのリソースポリシーの書き方などでつまずきます。僕はつまずきました。よくわかっていない方は、まずはIAMを理解するのがおすすめです!
参考
API Gatewayのリクエストではありませんが、いろいろな言語の参考になるサンプルコードです。
Pythonのサンプルコード
Node.jsのサンプルコード
Java
もしSigV4署名を自前で実装したい場合のサンプルコード