Terraform連載2024を の6本目です。
導入
インフラエンジニアとして働いているTIGの原木です。
cfn-guardを使用してTerraformをポリシーチェックしようとした話をします。
cfn-guardとは?
cfn-guardのcfnとはAWSのCloudFormation(AWSのIaCソリューションのこと)の略称です。
このツールはCloudFormationを使ってAWSのリソースをデプロイするときにその内容をチェックするポリシーチェックツールとしてよく使われています。
しかし、cfn-guardはその名前に反して、CloudFormationに限らず、JSON/YAMLファイルに対する汎用的なポリシーチェックツールとしても使用できます。
READMEの記載にも、次の通り説明があります。
Guard offers a policy-as-code domain-specific language (DSL) to write rules and validate JSON- and YAML-formatted data such as CloudFormation Templates, K8s configurations, and Terraform JSON plans/configurations against those rules.
Guardは、CloudFormationテンプレート、K8sコンフィグレーション、TerraformのJSONプラン/コンフィグレーションなどのJSONやYAMLフォーマットのデータに対して、ルールを記述し検証するためのPolicy as Codeなドメイン固有言語(DSL)を提供します。
AWS Certified Securityの勉強をしていて本ツールの名前を知り、READMEを見て、自分は興味を持ちました。
cfn-guardでTerraformをチェックしようとしたモチベーション
Terraformのポリシーチェックとしては、過去にFuture技術ブログで紹介したtflintやterraform validator1、tfsec2等すでに様々なツールがあります。
その中でなぜあえて、cfn-guardをTerraform planをチェックしようとしたのか?
それはcfn-guardに読み込ませるルール表となるCFn Guard DSLとAWSマネージドサービスの力を借りてインフラをデプロイする前から後まで一貫したポリシーチェックができるのではないか。と考えたためです。
一度CFn Guard DSL(ポリシールール)を書くことで二度おいしいメリットがあると考えました。
- Terraformのコーディング中に、cfn-guardによりユニットテストを動かす感覚でポリシーチェックを随時できるようになります
- Terraformを使ってAWSインフラを構築後、意図しない形でリソースが変更されてもAWS ConfigによりトリガーされたCFn Guardルールのスキャンによってインフラのドリフトを検知できるようになります
CloudFormation Guard で Policy as Code! 実際どうよ? / Policy as Code with CloudFormation Guardのスライドをお借りすると次のようなイメージです。
このような 青写真 を描きました。
このブログでは、cfn-guardを検証し…そして、思ってたのと違った!!という話をしたいと思います。
なにはともあれ実践してみよう
一番基礎的な使い方として、S3ファイルをデプロイするterraformのファイルをチェックする方法について書きたいと思います。
前提として、cfn-guardはHCLファイル(要は.tfファイル)を直接チェックできず、JSON/YAMLファイルを読み込ませないといけないので、 terraform plan
の実行結果からJSONファイルを作成する必要があります。
下記のようにS3バケットを作成するHCLファイルがあったとしましょう。
# provider等は省略します |
このファイルがまだ未作成の場合、次のようにコマンドを実行することで
作成後に想定されるリソース構成をJSONファイルで出力できます。
terraform plan -out tfplan.bin |
tfplan.json
のファイル構造を分解して中身を見てみましょう。
※そのままだと見づらいのでJSONファイルをサブセットであるYAMLファイルに変換して表示します。
※YAMLファイルでも素のJSONファイルでもcfn-guardは動かすことができます。
format_version: "1.2" |
上記リソースをチェックするためのリソースポリシーを書いてみます。
よくあるリソースポリシーとして
- S3のバケット名が特定の命名規則にしたがっていること
- リソースに特定の環境を示すタグが入っていること
をチェックしたいと思います。
let aws_s3_bucket_resources = planned_values.root_module.resources[type == "aws_s3_bucket"] |
このルールに従っているか実際に cfn-guard
を動かし、チェックしてみましょう。
cfn-guard validate -r s3_template_example.guard -d infrastructure/tfplan1.json -o yaml |
エラーになりました。contextを確認すると、 context: ' %aws_s3_bucket_resources[*].values.bucket EQUALS "/^test-.*/"'
とあるようにバケットの命名規則がルールに従っていないことがわかります。
そこでバケット名を修正し、再度 cfn-guard
にかけてみます。
修正したs3.tf及びyamlファイルは割愛 |
今度は通りました。
序の口ではありますが、cfn-guardを使ったチェック方法について、雰囲気は掴めたのではないかと思います。しかし、ここから先、cfn-guardを深掘りするうちにギャップが広がっていくことに気づきました。
ここが思ってたのと違ってたよという話
当初の自分の妄想では、cfn-guardとは、AWSのリソースAPIにアクセスしていい感じにチェックするツールなのかなとふわっと思ってました。ですが、実践例にあるように実態はどうでしょうか?
カンの良い方はすぐに気づかれたかもしれません。
CloudFormationを例に先ほどと同じことをしてみましょう。
AWSTemplateFormatVersion: 2010-09-09 |
というファイルに対してS3のバケット名の命名規則をチェックします。
let buckets = Resources.*[ Type == 'AWS::S3::Bucket' ] |
別物やんけ。
その通りです。なぜなら、CloudFormationとTerraform planではファイルの構造が全然異なりますので。
cfn-guardの実態は、CFn Guard DSLに基づきJSONやYAMLなどの構造型データを検査する、ある意味シンプルな構文解析ツールです。
したがって、ポリシーファイルについてCloudFormation向けはCloudFormation向け、Terraform plan向けはTerraform plan向けに書く必要があります。そして後者のTerraform plan向けのポリシーファイルは、当然AWS Config上で動きません。
ここに当初の構想はからくも崩れたのでした。
ECSのTask Definitionのようにインラインで文字列化したJSONファイルをいい感じにパースする方法が見つからなかった、tagチェックでtagsとtags_allを別々にチェックする必要があった、そもそも構文エラー時説明してるようで何も説明してくれないエラーログ等、細かいことを言い出すときりがない不満があり、最終的に自分はおとなしくtflintに戻りました。
最後に
Terraformユーザーには、cfn-guardの扱いは少々難しいところがあるという話でした。
ポリシールール等の設定について最近はChatGPT先生に下書きをお願いすることが多いのですが、彼女に自由に書かせたら、明らかにAWS CloudFormationテンプレート向けのguardファイルをTerraformと言い張ったのは悲しかったです。
WHY?と聞いたら次の通り開き直った回答が返ってきました。
しかし、AWS CDKを使ってCloufFormationのテンプレートファイルを生成し、AWSリソースのデプロイを行っているユーザーにとって強力なポリシーチェックツールなのは間違いありません。
CFn Guard Rules Registryには、ルールの実装例が多数掲載されております。
Amazon Web Services’ Well-Architected Framework Reliability Pillar等、インフラエンジニアが非機能要件を考える時のベストプラクティスを実装したポリシーファイル等もあり、痒い所に手が届く例となっています。
以上、参考になれば幸いです。