【AWS】LambdaにHTTPSエンドポイントが実装されたので使ってみた【Golang】

どもども、すっかり暖かくなってそろそろ衣替えを考え始まる今日この頃です。


ほんじつは今週発表された激アツアップデートである"AWS Lambda Function URLs"を使用してみたいとおもいます。


Lambdaの呼び出しがより柔軟になること間違いなしなので要チェックです!今回はcurlでの呼び出しとエンドポイントにリクエストを送るコードをGolangで書いてみようと思います。

AWS Lambda Function URLsとは

AWS Lambda Function URLs は記事のタイトルにもある通り、AWS LambdaのHTTPSエンドポイントです。


これまで、外部からLambdaを呼び出す際にはAPI Gatewayを使用してエンドポイントを実装する必要がありました。


しかし、AWS Lambda Function URLsが実装されたことにより作成したAWS LambdaにURLが付与されエンドポイントにして呼び出せるようになりました!これは非常に革命的です!


Webフック的に使用したい、実装中のLambdaを試験的に呼び出したいなどユースケースは色々考えられそうです。


またパブリックなエンドポイントだけでなくIAM認証を必要とするエンドポイントも作成可能なのでセキュリティ面でも配慮ができそうです。こちらも本記事で紹介したいと思います。


それでは実際にLambdaを作成し、エンドポイントから呼び出してみましょう。

実際に使用してみる

それでは準備から進めていきましょう。


まずはテスト用のLambdaを作成しましょう。普段通りGUIから作成しますが、詳細設定から関数URLを有効しておきましょう。最初は一旦認証はNONEで試してみます。

すると作成したLambdaにエンドポイントとなるURLが付与されていることがわかります。

では、実際に作成したLambdaのエンドポイントにリクエストを投げてみましょう。

$ curl <エンドポイントURL>
Hello from Lambda!

無事、デフォルトで作成されるLambda関数のレスポンスが返ってきました!AWS CLIも必要ありません。す、すごい…。


コードから呼び出してみましょう。今回はGolangで実装しています。とは言っても、エンドポイントにGETリクエストを投げるだけのシンプルなものです。

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {

	url := "<エンドポイントURL>/"
	response, err := http.Get(url)
	if err != nil {
		log.Fatal(err)
	}

	defer response.Body.Close()

	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(string(body))
}

実行すると、同じようにLambda関数の実行結果が返ってきました!

$ go run main.go 
Hello from Lambda!

これで非常に簡単にLamdaが呼び出せるようになったことがお分かり頂けたと思います。


しかし、このままではURLを知っている人は誰でもこのLambdaを実行できてしまうのでセキュリティ上よろしくありません。


そこでAWS Lambda Function URLsの呼び出しにIAM認証を要求することで、よりセキュアに呼び出せるようにしてみたいと思います。

IAM認証を使用する

次に設定を変更し、エンポイントからの呼び出しにIAM認証を使用します。lambda:InvokeFunctionUrl権限を持つIAMプロファイルがないとLambdaを呼び出すことはできなくなります。

設定は先ほどURLを確認したページの右上"編集"から変更できます。認証タイプを変更しましょう!

これで、普通のGETリクエストでは呼び出せなくなっていることがわかります。

$ curl <エンドポイントURL>
{"Message":"Forbidden"}

アクセスするためにはAWS Signature Version 4 (SigV4)を使用して、HTTPリクエストに対して署名する必要があります。今回はawscurlを使用してこれを実現します。

インストールはpipコマンドで行うことができます。Macの場合はbrewを使用したインストールも可能です。

$ pip install awscurl

では、awscurlを使用してLambdaをエンドポイントから呼び出してみましょう。

$ awscurl --service lambda --region ap-northeast-1 <エンドポイントURL> --profile <プロファイル>
Hello from Lambda!

無事、Lambdaが呼び出されました!


次に、コードからLambdaを呼び出してみましょう。もちろん、AWS Signature Version 4 (SigV4)を使用して、HTTPリクエストに対して署名する必要があります。先程のコードを実行してもエラーが返ってきます。

$ go run main.go
{"Message":"Forbidden"}

下記パッケージを利用してHTTPSリクエストに署名していきましょう。

github.com/aws/aws-sdk-go/aws/signer/v4

リクエスト部分の実装は先ほどとほとんど変わりません(今回はClientを利用していますが)。大きな変更点としてHTTPリクエストに対して署名する処理が追加されています。

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/aws/aws-sdk-go/aws/credentials"
	v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
)

func main() {
 // 今回は直接変数に入れていますが、実運用の際はENVファイルやプロファイルから読み出しましょう
	access_key := "<アクセスキー>"
	secret_acces_key := "<シークレットアクセスキー>"
	client := &http.Client{}

 // 認証に使用するIAMプロファイルのクレデンシャル情報を用意しておきます
	credentials := credentials.NewStaticCredentials(access_key, secret_acces_key, "")
 // クレデンシャルを使用したSignerを用意しておきます
	signer := v4.NewSigner(credentials)

	url := "<エンドポイントURL>"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		fmt.Print(err)
	}

 // リクエストに対してSignerを使用して署名します
	signer.Sign(req, nil, "lambda", "ap-northeast-1", time.Now())
	res, _ := client.Do(req)

	res_body, _ := ioutil.ReadAll(res.Body)
	fmt.Println(string(res_body))
}

実行してみると、再びLambdaを呼び出すことができました!

$ go run main.go 
Hello from Lambda!

これでセキュアなHTTPSエンドポイントの実装完了です!

さいごに

今回はAWS Lambda Function URLsを利用したLambdaの呼び出しを試してみました。


API Gatewayとの使い分けが肝になってくると思います。大きな分岐点はカスタムドメインの必要性かな、と個人的に考えています。


もちろんリクエストにパラメータを追加できますし、CORSを有効化することもできるので機能として申し分ないと思います。


サーバレスアーキテクチャを考える上で影響力の大きいアップデートだと思いますので、是非ガンガン使って知見を貯めておきたいところです。


最後まで読んでいただき、ありがとうございました!

本日のオススメ

AWS,Golang

Posted by CY