諒2021.06.12この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。いなど重大なセキュリティ事案が発生するケースが実際に多々起きています。そこで、アクセスキー運用に関する安全向上の取組みをブログでご紹介する企画をはじめました。セキュリティ対策を事前に適用した上で適切にご利用ください。RDSのみなど)をしている方は対象外です本記事はAWS上にソフトウェアを構築する開発者(主にバックエンドエンジニア)や開発環境を提供するプラットフォーマーを想定読者としていますTypeScriptのサンプルコードが提示されます、使用経験がなくてもオブジェクト指向を理解していれば恐らく読めると思います「ユニットテストではAWSへのアクセスをする処理はモックに差し替える」「インテグレーションテストではDynamoDBへアクセスする処理はlocalstackをコンテナで立ち上げてフェイクとして利用する」これらの文章が理解できる方にとっては本記事は既知の内容ですはじめに本記事はソフトウェア開発におけるテストが主な関心ごとです。ローカル開発端末上でテストする際に、AWSへのアクセスが必要になり、そのためにアクセスキーをローカル開発端末に保持してしまっている方に対してAWSにアクセスしなくてもテストを行える開発方法を伝えることを目的としています。AWSにアクセスしなくてもテストが行えるならばアクセスキーを発行する必要はなく、発行されなければ漏洩もしません。対象はソフトウェア開発(テスト)のためにアクセスキーを保持しないことなので、AWSリソースの構築をローカル開発端末から行うのであればアクセスキーは保持しなければなりません。本記事は開発のためのサンドボックス環境としてのAWSアカウントに対するアクセスキーの保持を否定するものではありません。最初にテストの粒度に関する認識を揃えます。Sizesをそのまま採用しています。(解釈にズレはあるかもしれませんが。)ひとまず表だけでも良いので先に進む前に読んでください。名称に関しては下記の様に言い換えています。テストの粒度や名称に関しては皆さんそれぞれの拘りがあると思いますが、本記事では一旦この様にさせてください。systemsはNo/Discouragedとなっており、外部システムに対してアクセスしない/推奨されません。AWSへアクセスしません。よって、ユニットテストとインテグレーションテストはローカル開発端末で行える様に、AWSへのアクセスが必要なE2EテストはCI環境など専用の環境から行うことにします。DynamoDBに対してデータの書き込みを行う場合を想定してコードを書いてみます。一切テストのことを考えずに書いてみます。handler();})();handler関数はDynamoDBクライアントの生成からデータの書き込み(PutItem)まで一連の処理を行っています。これを実行する時は常にAWSアカウントへのアクセスが発生してしまいます。依存性の注入を行える様にする場合DynamoDBへのアクセスをRepositoryクラスとして切り出し、DynamoDBのクライアントと接続に必要なテーブル名をインスタンス生成時に与えるようにします。DynamoDBに対して行うアクション(ここではputItem)をインターフェイスとして定義し、Repositoryクラスはこれを実装します。通常時はDynamoDBRepositoryから生成したインスタンスをusecase関数に渡して処理を行います。テスト時にはもう1つのMockDynamoDBRepositoryクラスから生成したインスタンスを関数に渡します。このクラスは同様にIDynamoDBRepositoryを実装していますが、何も処理を行わないのでDynamoDBへのアクセスが発生しません。(インターフェイスの定義より引数が少なくても良いのはTypeScriptの仕様です。handler();})();依存性の注入はクラスではなく関数(クロージャー)でも行えます。handler();})();テストツールによる動的なモック動的型付け、漸進的型付け言語であればテストツールによっては動的にテスト対象の挙動を書き換える事によってAWSへアクセスする部分の処理を自由に操作する事ができます。src/aws-dynamodb-1.tsをベースにモックしやすい様にAWSへのアクセス部分をファイルに切り出し、propsを外部から受け取るように、例外を拾ったときにステータスコード500を返す様に変更します。500}));};jestというテストツールを使うことで、DynamoDBクライアントのsendメソッド実行時のレスポンスを自由に設定することができます。例外を発生させることで異常系のテストも行えます。});});フェイクを使ったインテグレーションテスト本記事ではフェイクとは実物と同様に動作する代替品のことを示します。RDSで動かすようなアーキテクチャにおいて、これのフェイクはローカル開発端末やCI環境上のDockerコンテナを使い実行されたMySQLです。今回のサンプルコードではデータストアにDynamoDBを使用しています。Localというツールがあるので、今回はこれを使用します。下記の様に定義することでDockerを使って簡単にローカル開発端末上で立ち上げることができます。-sharedDb'フェイクを使うことでAWSサービスまで含めてインテグレーションテストが可能になりますが、そもそもそれが何で必要なのかという話があります。私はSDKの型でフォローできないところがあるので、フェイクを使ったテストが必要と考えいます。下記はDynamoDBトランザクションを使ったユニーク制約を実現しているサンプルコードで、nameとmailAddressに対してユニーク制約をかけています。}や単にstring型です。これを正しく記述できているかをユニットテストで確認はできないし、かといって毎回E2Eテストをするにはデプロイに時間がかかり集中力を維持できません。いったシチュエーションでフェイクを使ったインテグレーションテストは役立ちます。};}テスト時にフェイクに繋がるようにDynamoDBクライアントを生成し、前処理/後処理としてテーブルの作成/削除を行います。});});実際のインテグレーションテストではコントローラー層などもう少し層が増えると思いますが、大きく変わりはないはずです。SDKはクライアント生成時にcredentialsオプションからアクセスキーを受け取り、これを使ってAWSリソースへのアクセス権限を受け取る事ができます。Runnerなどにソフトウェアをデプロイする際に、このように記述することはおすすめしません。インスタンスプロファイルやIAMロールを使いソフトウェアを認可することをおすすめします。SDKはインスタンスプロファイルやIAMロールを自動で受け取り使用します。あとがきローカル開発端末や、それ経由でGitリポジトリからのアクセスキー漏洩が結構あるという話を聞き、そもそもアクセスキーを持たないようにする方法を記事ににまとめてみました。