$30 off During Our Annual Pro Sale. View Details »

DockertestとLocalStackを使って 外部サービスに依存した多要素認証の 動作確...

DockertestとLocalStackを使って 外部サービスに依存した多要素認証の 動作確認・テストをした話 / A story about using Dockertest and LocalStack to check and test the operation of multi-factor authentication that depends on external services

2024/06/08:
Go Conference 2024
https://gocon.jp/2024/

DockertestとLocalStackを使って 外部サービスに依存した多要素認証の 動作確認・テストをした話

西田 智朗
ソフトウェアエンジニア

株式会社カミナシ

June 11, 2024
Tweet

More Decks by 株式会社カミナシ

Other Decks in Technology

Transcript

  1. 自己紹介 ②本文を書くときに、強調する文字は太字にして、色をつける。 • 基本的な強調(中性的・ポジティブな意味)はカミナシブルーで表現する。 ※注釈などに利用する小さい文字は文字サイズを相対的に小さく調整し、グレイ色をつける。 ① 基本的な配色は、下記の青系のカラーパレットと白・黒・グレーとする。 ② 特別な強調をしたいときは指定の黄色か赤を使用する。 カミナシブルー

    サブカラー ベースカラー 文字色など 黄(濃) 黄(淡) ※本スライド最終ページに使用例記載 赤(濃) 赤(淡) ①書体は M PLUSを使用。 【文字】 【色】 ▪略歴 2020年にソフトウェアエンジニアのキャリアを開始。決済システムの開 発などを経験し、2023年2月にカミナシに入社。 ▪カミナシでの仕事 認証認可システムの設計・開発。 OAuth2.0 や OpenID Connect などの標準仕様に準拠した IdP の開発を 進めています。 ▪ひとこと 緊張しました! 株式会社カミナシ 認証認可ユニット ソフトウェアエンジニア 西田 智朗(にしだ ともろう)
  2. ⑤ユーザーが受け取ったワンタイムパスワードを入力することで認 証が完了 ②発行したワンタイムパスワードを AWS DynamoDB に保存 課題 メールを使った多要素認証 ①パスワード認証完了後、二要素認証の設定がオンになっている ユーザーに対してワンタイムパスワードを発行

    ③DynamoDB Streams をイベントソースに AWS Lambda 関数を 起動 ④Lambda から外部のメール配信サービスにリクエストして、ユー ザーにワンタイムパスワードを送信 ここの動作確認がしたい!
  3. DynamoDBEvent を受け取る Lambda ハンドラーは以下のような シグネチャになる。テスト関数上で想定される DynamoDBEvent を自前で初期化して実行する。 検討した方法② Event を自前で初期化して

    Lambda ハンドラー関数を実行する func handlerFunc(event events.DynamoDBEvent) error ここで渡されるイベントを 自前で定義する 【メリット】 ・localstackやらDockertestやらの仕組みを整える必要がない ・テスト自体の実行が早い 【デメリット】 ・テストの実装・修正をするためには、どのようなイベントが発行  されるかを知っている必要がある
  4. Dockertest について https://github.com/ory/dockertest When developing applications, it is often necessary

    to use services that talk to a database system. Unit Testing these services can be cumbersome because mocking database/DBAL is strenuous. Making slight changes to the schema implies rewriting at least some, if not all of the mocks. The same goes for API changes in the DBAL. To avoid this, it is smarter to test these specific services against a real database that is destroyed after testing. Dockertest は Go で書いたプログラムから簡単に Docker コンテナを立ち上げることができるライブラリで、 上記ではデータベースのコンテナについて言及しているが、実際は今回のようにテスト用のLocalStackコンテナを立て たり、自前で定義したDockerfileを元にコンテナを立てることもできる。 DB との通信が発生するサービスを作るときにテスト用のモック DB を作った り管理するのって大変ですよね? Dockertest を使うと任意のデータベースの一時的なコンテナをテスト用に 立てることができるため、 DB 通信部分のテストを書くのが楽になります!
  5. Dockertest について postgres, err := pool.RunWithOptions(&dockertest.RunOptions{ Repository: "postgres", Tag: "11",

    Env: []string{ "POSTGRES_USER=test", "POSTGRES_PASSWORD=test", "listen_addresses = '*'", }, }, func(config *docker.HostConfig) { // set AutoRemove to true so that stopped container goes away by itself config.AutoRemove = true config.RestartPolicy = docker.RestartPolicy{ Name: "no", } }) テストを実行する前にこのように記述するだけで一時的なPostgreSQLのコンテナを起動することができる。
  6. LocalStack について https://github.com/localstack/localstack LocalStack is a cloud service emulator that

    runs in a single container on your laptop or in your CI environment. With LocalStack, you can run your AWS applications or Lambdas entirely on your local machine without connecting to a remote cloud provider! Whether you are testing complex CDK applications or Terraform configurations, or just beginning to learn about AWS services, LocalStack helps speed up and simplify your testing and development workflow. LocalStackは、あなたのラップトップやCI環境上で単一のコンテナで動作す るクラウドサービス(AWS)エミュレータです。LocalStackを使えば、 Terraform構築のテストやAWSのサービスを使った複雑なアプリケーション のテストがローカルマシン上で実行できます。 AWS CLI/SDK, Terraform などの適用先を LocalStack コンテナにすることで、 LocalStack 上にリソースを用意するこ とができる。
  7. DockertestでLocalStackコンテナを起動するサンプル func UpLocalStackWithDockerTest() (func(), string) { pool, err := dockertest.NewPool("")

    pool.MaxWait = 5 * time.Second if err != nil { log.Panicf("Could not connect to docker: %v", err) } err = pool.Client.Ping() if err != nil { log.Panicf("Could not connect to docker: %v", err) } runOptions := &dockertest.RunOptions{ Repository: "localstack/localstack", Tag: "latest", Name: "localstack-for-email-otp-test", Env: []string{ "SERVICES=sns,sqs,dynamodb,lambda", "DEFAULT_REGION=ap-northeast-1", "DEBUG=1", }, // ホストマシンのdocker daemonを使ってLambdaコンテナを立てるため // ホストマシンのdocker daemonとソケット通信できるようにする Mounts: []string{"/var/run/docker.sock:/var/run/docker.sock"}, } resource, err := pool.RunWithOptions(runOptions, func(config *docker.HostConfig) { config.AutoRemove = true config.RestartPolicy = docker.RestartPolicy{ Name: "no", } }, ) if err != nil { log.Panicf("Could not start resource: %v", err) } // コンテナ側の4566ポートに対応するホスト側のホストとポートを取得 // ここで取得したホストとポートを使ってLocalStackのAPIにアクセスする hostAndPort := resource.GetHostPort("4566/tcp") healthcheckFunc := func() error { resp, err := http.Get(fmt.Sprintf("http://%s/health", hostAndPort)) if err != nil { return err } if resp.StatusCode != http.StatusOK { return fmt.Errorf("status code is not 200: %d", resp.StatusCode) } return nil } if err := pool.Retry(healthcheckFunc); err != nil { pool.Purge(resource) log.Panicf("Could not success healthcheck function: %v", err) } close := func() { if err := pool.Purge(resource); err != nil { log.Panicf("Could not purge resource: %v", err) } } return close, hostAndPort }
  8. ②メール配信サービスは「ワンタイムパスワードの受け取り」と「受け取ったワンタイムパスワードの返却」ができるだ  けのモックサーバーを建てる テストの方針と流れ 【方針】 ①アプリケーションのテストコードを実行すると LocalStack 上に必要な環境が構築され、  ワンタイムパスワードの発行からメール配信リクエスト用のLambda関数まで動くようにする ②テストパッケージの初期化時にterraform-exec(https://github.com/hashicorp/terraform-exec)を使ってDynamoDB  やLambda関数の構築を行う

    【流れ】 ①テストパッケージの初期化時にDockertestを使って「LocalStackコンテナ」と「メール配信サービスのモックサー   バーコンテナ」を立ち上げる ③テスト上でワンタイムパスワードの発行、DynamoDBへの保存を行う処理を実行する ④テスト上で「メール配信サービスのモックサーバーコンテナ」の「受け取ったワンタイムパスワードの返却」エンドポ  イントを叩き、取得したコードと生成したコードを比較する
  9. まとめ ・Dockertest に LocalStack を組み合わせると、  go test だけで AWS をエミュレートしたテストが実行できる

    ・Go でテストを書く際は Dockertest を使うと、テスト用の一時的なコンテナを  簡単に立ち上げられる Go の機能だけでテスト関数を簡単に実行できるように工夫したことで、テストの手軽さが向上し、結果的に手戻りを 減らすことができた。 また、今回紹介したテストの仕組みを作る前は漠然と「ちょっと大変そうかも」と思っていたが、元から Dockertest や LocalStack を利用していたこともあり実際は3~4時間ぐらいで実装することができた。 最初に数時間投資するだけで自分のような AWS 初心者でも快適に Lambda のコードを触れるようになった。 【感想】 【ポイント】