LoginSignup
0

AWSのSigV4署名を付与してIAM認証のAPI GatewayにHTTPリクエストを送るRubyとGoのサンプルコード

Last updated at Posted at 2022-11-30

この記事は、リンクバルアドベントカレンダー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種類があります。
漏洩のリスクやキーの管理など、主にセキュリティ的に InstanceProfileCredentialsECSCredentials の利用が望ましいです。極力アクセスキーを利用するのは避けましょう。

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署名を自前で実装したい場合のサンプルコード

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0