Terraformで予防的ガードレールを実装したい!「Terraform-Compliance」のご紹介
7月7日はクラスメソッドの創立記念日です。今日は次々にブログ記事があがってくることでしょう!
また、わたくしゴトですが 18 期からコンサルティング部の部長を務めさせていただくことになりました。「あいつ、部長になったらブログ書かなくなったよな」と後ろ指さされないように、ブログの書ける部長として頑張っていきたいと思います。
ここから
AWS が一般的に利用されるようになった昨今最近では単に AWS を使いたいという相談よりも、どうやって AWS 環境をうまく運用するべきか?というご相談が多くなっているように感じます。よくある相談の 1 つが「ガバナンス」です。
ある程度ユーザーに権限をもたせ自由にリソースを作成できるようにした結果、
- 「パブリックアクセスで S3 を公開してました、、」
- 「パブリック(0.0.0.0)で許可するセキュリティグループが設定されていました、、」
- 「ログが設定されておらず何が事故ったのか何もわからん、、」
よくある話ですね。
一般的に AWS のベストプラクティスに則れば AWS Control Tower、AWS Config、AWS SecurityHub、AWS Organizations(SCP)といったサービスを使い、検出および予防的ガードレールを提案する形になるかと思います。
IaC における予防ガードレール
ガードレールを検討される規模の場合、AWS コンソールだけでなく IaC などの構成ツールを利用している環境も少なくないと思いますが、最近では IaC 向けにも予防的ガードレールの仕組みが提供されています。
CloudFormation をお使いの場合は AWS 純正のツールとして AWS CloudFormation Guard が提供されています。デプロイ前に指定したルールに準拠していることをチェックし、非準拠なリソースのデプロイを未然に防ぎます。
Terraform ユーザ向け
しかしながら、私のような Terraform 派はどうすれば良いのでしょうか?
というのが、今回の趣旨です。
前置きが長くなりましたが、Terraform の場合は選択肢として 2 つです。(私が知っているのは、、)
Sentinel は Terraform 同様に Hashicorp 社が提供していますが、Terraform Cloud(Team & Governance プラン以上)や Terraform Enterprise 向けのプロダクトですので有償版になります。また、Terraform 1.0 のリリースと合わせて Terraform Cloud Run Checks が発表されていますが、こちらはまだベータ版の扱いです。
一方、terraform-compliance はオープンソースとして提供されています。無償で利用可能ということもあり、以前より気になっているツールでした。
本記事では terraform-compliance をインストール、サンプルの実行あたりまで検証して、利用方法等をシェアいたします。
検証環境
今回はとりあえず terraform-compliance の使い方を試してみることが目的ですので、ローカル PC で検証しています。実環境では CI/CD パイプラインに組み込む必要があるかと思いますが、今回はそこまで検証していませんのであしからず。
検証環境のローカル PC 環境は以下のとおりです。
$ terraform -version Terraform v1.0.1 on darwin_amd64 $ docker -v Docker version 20.10.6, build 370c289 $ sw_vers ProductName: Mac OS X ProductVersion: 10.15.7 BuildVersion: 19H524
今回は執筆時点の最新バージョン Terraform v1.0.1 を使用していますが、terraform-compliance は Terraform v0.12 以上でサポートされています。
インストール
それではインストールしていきましょう。terraform-compliance のインストール方法は以下の 2 とおりです。
今回はローカル環境をあまり汚したくないという意図もあり、Docker 版で検証します。イメージは Docker Hub より入手します。function
コマンドでユーザ定義関数として登録しておくと呼び出しが楽になります。
$ terraform-compliance Unable to find image 'eerkunt/terraform-compliance:latest' locally latest: Pulling from eerkunt/terraform-compliance b4d181a07f80: Pull complete de8ecf497b75: Pull complete 69b92f9e5e70: Pull complete 1f2b8e2c8ad8: Pull complete ce65eeb74c3c: Pull complete 064992249895: Pull complete 82c71336f0ef: Pull complete 0df1d219a873: Pull complete Digest: sha256:868b2fdf03434ecbcad8bfdac066e9270f57fe51201ec41dfd6ee7069d787682 Status: Downloaded newer image for eerkunt/terraform-compliance:latest $ function terraform-compliance { docker run --rm -v $(pwd):/target -i -t eerkunt/terraform-compliance "$@"; } $ terraform-compliance --version terraform-compliance v1.3.21 initiated 1.3.21
使用方法
BDD (Behavior Driven Development)
terraform-compliance は BDD(ビヘイビア駆動開発)を処理するために radish を使用しています。
BDD については今回の terraform-compliance で初めて触ったので多くを語れませんが、テストコードを自然言語に近い形で記述できるためテストコードの可読性が高く、理解しやすいコンテキストを提供します。
BDD には 3 つのコンポーネントがあります。
- Feature
- Scenario/Scenario Outline
- Steps
Feature
Feature はいくつかの Scenario で構成される機能ファイルの全体像を記述します。
Feature: Security Groups should be used to protect services/instances In order to improve security As engineers We'll use AWS Security Groups as a Perimeter Defence
この機能は「サービス/インスタンスを保護する為にセキュリティグループを使用する必要」があり、「セキュリティを向上させるために、エンジニアとして、AWS 境界防御にセキュリティグループを使用する」ためのテストがここに書かれてるんだな、というのがすぐに理解できますね。
Senario
Senario では以下の BDD ディレクティブを使用した複数の Steps を含むテストを定義します。
- GIVEN
- 検索するリソースを指定。必須パラメータ。
- WHEN
- GIVEN で指定されたリソースのフィルタリング条件。必須ではない。
- THEN
- マッチング基準を定義。シナリオが失敗するか成功するかの判断をするステップ。必須ではない。
WEHN
またはTHEN
に対してAND
で始まる追加の拡張ステップを含めることも可能です。
Scenario: Ensure all resources have tags Given I have resource that supports tags defined Then it must contain tags And its value must not be null
このシナリオは「すべてのリソースでタグを持っていることを確認」するためのもので
- Given(検索するリソース) : 持っているリソースで、タグがサポートされれているものすべて
- Then(マッチング基準) : タグが含まれていること
- AND(Thenの拡張ステップ) : かつ、値が null でないこと
各ディレクティブで記述可能な書式が数パターンあるので、詳細はドキュメントを参照ください。
実行方法
terraform-compliance は以下のように実行して使用します。
$ terraform-compliance -p plan.out -f /path/to/feature/files/
-p plan.out
はterraform plan
の結果をファイル出力したものです。terraform plan
のファイル出力は以下のコマンドで実行できます。
$ terraform plan -out=plan.out
-f /hogehoge/
は機能ファイルが配置されているディレクトリを指定します。-f git:https://github.com/user/repo
のように Git リポジトリを指定することも可能です。ディレクトリ、またはリポジトリ内のすべての機能ファイルが非再帰的に処理されます。
コンプライアンスチェック
それでは実行してみましょう。以下のとおり、ただ単に S3 バケットを作成するだけのテンプレートを準備しました。
resource "aws_s3_bucket" "test-bucket" { bucket = "cm-marumo-tf-compliance-test" acl = "private" }
次に terraform-compliance の機能ファイルを作成します。こちらは公開リポジトリから S3.feature を拝借しました。内容としては
- 「SSEが設定されていること」
- 「ログ設定されていること」
- 「バージョニングが有効化されていること」
が準拠項目となっています。
Feature: S3 related general feature Implemented - Data must be encrypted at rest (what if it's suppose to be public?, maybe check if it's suppose to be public before? What if it's mistakenly set as public?) - Data stored in S3 has versioning enabled Questionable checks (only checks if one pass) - S3 must have access logging enabled Scenario: Data must be encrypted at rest Given I have aws_s3_bucket defined Then it must have server_side_encryption_configuration # check if at least one s3 has logging enabled, because logging will require another s3 @noskip_at_line_20 Scenario: S3 must have access logging enabled Given I have aws_s3_bucket defined When it has logging Scenario: Data stored in S3 has versioning enabled Given I have aws_s3_bucket defined Then it must have versioning Then it must have enabled And its value must be true
では、terraform plan
の結果をファイル出力して、terraform-compliance
を実行してみます。
$ terraform plan -out=plan.out $ terraform-compliance -p plan.out -f tf-compliance/ terraform-compliance v1.3.21 initiated . Converting terraform plan file. ERROR: Failed to convert terraform plan file to JSON format via terraform. Here is the error : None ╷ │ Error: error configuring S3 Backend: no valid credential sources for S3 Backend found. │ │ Please see https://www.terraform.io/docs/language/settings/backends/s3.html │ for more information about providing credentials. │ │ Error: NoCredentialProviders: no valid providers in chain. Deprecated. │ For verbose messaging see aws.Config.CredentialsChainVerboseErrors │
・・・。なぜか plan.out
の変換に失敗していますし、クレデンシャルのエラーが出ていますね、、。
terraform-compliance の中にも terraform が含まれているのですが、使用したバージョンがv1.0.1
と新しすぎたので変換できなかったので、state を読みに行こうとしてるのでしょうか??
ちょっと原因が特定できていませんが、plan.out
の JSON 変換に失敗しているのであれば、terraform show -json
でJSON 形式に変換したうえで渡してあげれば問題ありません。
$ terraform show -json plan.out > plan.out.json $ terraform-compliance -p plan.out.json -f tf-compliance/ terraform-compliance v1.3.21 initiated ? Features : /target/tf-compliance/ ? Plan File : /target/plan.out.json ? Running tests. ? Feature: S3 related general feature # /target/tf-compliance/S3.feature Implemented - Data must be encrypted at rest (what if it's suppose to be public?, maybe check if it's suppose to be public before? What if it's mistakenly set as public?) - Data stored in S3 has versioning enabled Questionable checks (only checks if one pass) - S3 must have access logging enabled Scenario: Data must be encrypted at rest Given I have aws_s3_bucket defined Failure: aws_s3_bucket.test-bucket (aws_s3_bucket) does not have server_side_encryption_configuration property. Then it must have server_side_encryption_configuration Failure: @noskip_at_line_20 Scenario: S3 must have access logging enabled Given I have aws_s3_bucket defined Failure: Can not find any logging property for aws_s3_bucket resource in terraform plan. When it has logging Failure: Scenario: Data stored in S3 has versioning enabled Given I have aws_s3_bucket defined Failure: aws_s3_bucket.test-bucket (aws_s3_bucket) does not have versioning property. Then it must have versioning Failure: Then it must have enabled And its value must be true 1 features (0 passed, 1 failed) 3 scenarios (0 passed, 3 failed) 8 steps (3 passed, 3 failed, 2 skipped) Run 1625605475 finished within a moment
おぉ!正しく非準拠なテンプレートであることを検出していますね。
修正して再実行
では、コンプライアンス非準拠として指摘された箇所を修正して、以下のとおり S3 テンプレートを更新します。
resource "aws_s3_bucket" "test-bucket" { bucket = "cm-marumo-tf-compliance-test" acl = "private" logging { target_bucket = "cm-marumo-testlog" } server_side_encryption_configuration { rule { apply_server_side_encryption_by_default { sse_algorithm = "aws:kms" } } } versioning { enabled = true } }
再度、plan.out
を出力して JSON 変換したファイルを、terraform-compliance に渡します。
$ terraform plan -out=plan.out $ terraform show -json plan.out > plan.out.json $ terraform-compliance -p plan.out.json -f tf-compliance/ terraform-compliance v1.3.21 initiated ? Features : /target/tf-compliance/ ? Plan File : /target/plan.out.json ? Running tests. ? Feature: S3 related general feature # /target/tf-compliance/S3.feature Implemented - Data must be encrypted at rest (what if it's suppose to be public?, maybe check if it's suppose to be public before? What if it's mistakenly set as public?) - Data stored in S3 has versioning enabled Questionable checks (only checks if one pass) - S3 must have access logging enabled Scenario: Data must be encrypted at rest Given I have aws_s3_bucket defined Then it must have server_side_encryption_configuration @noskip_at_line_20 Scenario: S3 must have access logging enabled Given I have aws_s3_bucket defined When it has logging Scenario: Data stored in S3 has versioning enabled Given I have aws_s3_bucket defined Then it must have versioning Then it must have enabled And its value must be true 1 features (1 passed) 3 scenarios (3 passed) 8 steps (8 passed) Run 1625607902 finished within a moment
すべて PASS することが出来たので、コンプライアンスに準拠したリソース定義であることが確認できましたので、あとはterraform apply
でデプロイするだけです。
今回の検証はココまでです!
実環境での利用
今回はとりあえず terraform-compliance を使ってみたく、ローカル環境で検証していますが実環境においては IaC が CI/CD パイプラインで運用されていなければ意味がありません。(ローカルの terraform からデプロイされたらコンプライアンスチェックとおりませんので、、)
近いうちに AWS CodePipeline に terraform-compliance に組み込んでパイプラインのなかでコンプライアンスチェックを行い、問題なければデプロイが実行されるような CI/CD 環境を作ってみたいと思います。
まとめ
- AWS の利用が広がるにつれて、ガバナンスを利かせた運用が課題になりつつある
- IaC として Terraform を利用している場合、terraform-compliance も 1 つの選択肢
- terraform-compliance は BDD 構文を使用したガードレール定義であるためテストコードの可読性が高い
- Git リポジトリから機能ファイルを読み込めるので、リポジトリで管理しておくとマルチアカウントでも同じガバナンスを利かせることが容易
- コンプライアンスチェックのツールは CI/CD パイプラインに組み込み、デプロイは必ずパイプラインから実行しなければ意味がありません
以上!大阪オフィスの丸毛(@marumo1981)でした!