SSTエンジニアブログ

SSTのエンジニアによるWebセキュリティの技術を中心としたエンジニアブログです。

インフラコードの品質&セキュリティチェックをしてみよう!

はじめに

こんにちは、CISOの新井です。ここのところずっとリモートワークでの生活が続いているのですが、みなさんいかがお過ごしでしょうか?自分は出かけることが減った分、自宅で楽しめる幅を広げていけたらなと思っています。最近は、初めてチーズケーキなど作ってみましたが思いのほかうまく出来て家族には好評でした。

さてさて、話を本題に戻しまして、、今回はインフラコードの品質&セキュリティチェックについて紹介しようかと思います。ここ数年のクラウドの普及に伴って「Infrastructure as Code(以下、IaC)」として、インフラの構成をコードで表現・管理するのがだいぶ普及してきている状況かと思います。SSTではIaCとしてTerraform、CloudFormation、Ansibleといったあたりを使っています。コードで表現できるようになったことで、インフラ構成の問題をデプロイ前に事前チェックするようなことも可能となってきています。ということで、今回はCloudFormationで記述したコードの品質&セキュリティチェックをしてくれる「cfn-python-lint」と「cfn-nag」の紹介をさせてください。

  • cfn-python-lint

    • https://github.com/aws-cloudformation/cfn-python-lint
      • AWSの各種リソースの仕様に沿ってCloudFormationのyaml/jsonファイルが記述されているかをチェックしてくれます。アプリケーションコードの品質チェックではよく利用されているlint系ツールのCloudFormation版といったところです。
  • cfn-nag

    • https://github.com/stelligent/cfn_nag
      • CloudFormationのyaml/jsonファイルの記述内容にセキュリティ上の問題がないかをチェックしてくれます。例えば、セキュリティグループやIAMの権限を過剰に広く設定していないかなどを検出してくれます。

インストール

まずはcfn-python-lintからインストールしてみましょう。cfn-python-lintの実行にはpython 2.7もしくは3.4以上が必要です。執筆時は3.8.3で動作確認しています。pipでインストールします。

$ pip install cfn-lint
$ pip list | grep cfn-lint
cfn-lint           0.40.0

続いて、cfn-nagです。cfn-nagの実行にはRuby 2.5以上が必要です。執筆時は2.7.2で動作確認しています。gemでインストールします。

$ gem install cfn-nag
$ gem list cfn-nag

*** LOCAL GEMS ***

cfn-nag (0.6.12)

コマンド実行

さっそく実行してみましょう。以下のようにチェック対象となるCloudFormationファイルを指定してコマンドを実行すればOKです。

$ cfn-lint sample.cf.yml
$ cfn_nag sample.cf.yml

また、以下のコマンドでチェック項目の一覧を確認できます。

$ cfn-lint -l
E1001: Basic CloudFormation Template Configuration
Making sure the basic CloudFormation template components are properly configured
E1002: Template size limit
Check the size of the template is less than the upper limit
E1003: Template description limit
Check if the size of the template description is less than the upper limit

・・・
$ cfn_nag_rules
WARNING VIOLATIONS:
W1 Specifying credentials in the template itself is probably not the safest thing
W2 Security Groups found with cidr open to world on ingress.  This should never be true on instance.  ・・・略・・・
W5 Security Groups found with cidr open to world on egress

・・・

他にもオプションがいろいろとありますので、以下でヘルプを参照してみてください。

$ cfn_nag -h
$ cfn-lint -h

Visual Studio Codeのプラグイン

コマンドとしてのインストールが完了していれば、Visual Studio Code(以下、VS Code)の拡張もインストールすることで、VS Code内でチェック結果を確認できるようになります。

ということで、以下の拡張をインストールしましょう。

cfn-lintとcfn_nagのコマンドへパスが通っていれば拡張をインストールするだけですぐにでもVS Code内でチェックが可能となります。

実行 & 検出結果

VS CodeでCloudFormationファイルを開くとチェック結果がパネルの「問題」に表示されます。試しに手元で作成していたCloudFormationファイルを開いてみた結果が以下となります。

注) :VS CodeにJapanese Language Pack for VS Codeの拡張をインストールしているため、メニューの表記が英語ではなく日本語となっています。

f:id:micara:20201109201637p:plain
パネルに表示された検出結果一覧

上記の各検出項目については後ほど詳細に見ていきましょう。

また、CloudFormationのコードを表示しているエディター領域では問題となる箇所が以下のように波線で視覚的に分かりやすく表示されています。波線のところにマウスポインタを持ってくれば検出内容がポップアップで表示されます。問題の検出箇所が視覚的に分かるので改修作業の効率が上がりますね。

f:id:micara:20201109201909g:plain
ポップアップで検出結果を表示

それでは、具体的に検出されている各項目を見ていきましょう。

まずは1つ目の該当箇所です。下記は、IAMロールの定義を行っている箇所になります。

f:id:micara:20201109202008p:plain
IAMロールの定義

ここでは、cfn_nagで以下の問題が検出されています。

  • cfn_nag
    • (F38) IAM role should not allow * resource with PassRole action on its permissions policy
    • (W11) IAM role should not allow * resource on its permissions policy

cfn_nagでは2段階の検出レベルがあります。Fで始まるものはFailureで、Wで始まるものはWarningとなっています。Failureで指摘されている問題のほうが深刻度が高い指摘となるようです。

1つめの検出項目である「(F38) IAM role ~」は、Failureレベルの問題として任意のリソースをPassRoleできる権限が設定されていることが指摘されています。また、2つめの「(W11) IAM role ~」はWarningレベルの問題としてIAMロールのポリシー部分で指定するリソースに「*」を指定していることが指摘されているようです。いずれもコード中の32行目にあるResourceで「*」を指定している箇所の指摘となっています。 たしかに、任意のリソースをPassRoleできてしまうと、想定よりもさらに大きな権限を渡せてしまう可能性が出てきます。単に任意のリソースを扱える権限が与えられた状態よりも問題としてはより大きなものになりそうですね。ここは、リソース部分に「*」を記述して手抜きをするのではなく利用を想定している具体的なロールを記述しないとですね。

続いて、次の検出箇所に行きましょう。

下記はRDSの定義を行っている箇所になります。

f:id:micara:20201109202116p:plain
RDSの定義

ここでは、cfn-lintとcfn_nagでそれぞれ以下の問題が検出されています。

  • cf-lint
    • W2501 Password shouldn't be hardcoded for Resources/alertdb/Properties/MasterUserPassword
    • E3012 Property Resources/alertdb/Properties/BackupRetentionPeriod should be of type Integer
  • cfn_nag
    • (F23) RDS instance master user password must not be a plaintext string or a Ref to a Parameter with a Default value. Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager/ssm-secure value.
    • (F24) RDS instance master username must not be a plaintext string or a Ref to a Parameter with a Default value. Can be Ref to a NoEcho Parameter without a Default, or a dynamic reference to a secretsmanager value.

cf-lintはErrorとWarningの2段階のレベルで問題を検出するようです。Eで始まる項目がErrorで、Wで始まる項目がWarningです。Errorで検出されている項目のほうが深刻度が高い指摘となるようです。

で、、ここではRDSのユーザ名・パスワードをコードの115~116行目あたりに直接書いちゃっているのが検出されてます。手抜きでCloudFormationファイルを記述しているのがバレバレですね (^_^;。CloudFormation内に直接記述せずにパラメータ化するか、Secrets Managerに保管してそこから動的に値を取得するように改修をするのがよさそうです。Secrets Managerなどの暗号化されたデータの保管場所からCloudFormationのPropertiesに値を動的に格納する方法について以下で解説されていますので参考まで(手抜きw)。

、、ってなんか怒られそうなので、、例としてSecrets Managerをシンプルに利用する方式で対応してみましょう。まずはSecrets Managerにユーザ名・パスワードを保存します。AWS CLIを利用すると以下のような具合になります。

$ aws secretsmanager create-secret --name rds_auth01 --secret-string '{"username":"ユーザ名","password":"パスワード"}'

上記のようにコマンドラインでユーザ名・パスワードを渡したくないようであれば「--cli-auto-prompt」引数をつけてコマンドを実行することで値をインタラクティブに渡すこともできます(AWS CLI バージョン2以降)。

以下のような具合でインタラクティブに値を渡すことができます。

f:id:micara:20201111160002g:plain
インタラクティブなコマンド実行

「ユーザ名」・「パスワード」には具体的な値を設定してコマンド実行してください。上記のような設定をした場合、CloudFormationのほうでは以下のように定義します。

・・・

MasterUsername: '{{resolve:secretsmanager:rds_auth01:SecretString:username}}'
MasterUserPassword: '{{resolve:secretsmanager:rds_auth01:SecretString:password}}'

・・・

これでCloudFormationのyamlファイルからユーザ名・パスワードの具体的な値の記述を無くすことができました。安心してGitにコミットできますね。

また、100行目にある「BackupRetentionPeriod」の値に文字型の値を指定していることが検出されています。仕様ではここは数値型の値を指定する必要があるようです。ですので「'7'」を「7」に変更すればOKですね。こういったちょっとしたミスが早い段階で見つかるのも助かります。

(補足:RDSの定義をしている97行目のAllocatedStorageはRDSに割り当てるディスクサイズを定義する箇所ですが、こちらは数値型ではなく文字型になるようです。)

まとめ

デプロイ前にいろいろと問題が検出されることで、効率よく開発がすすめられそうです。 なお、どちらのツールも頻繁に新しいバージョンがリリースされているようです。定期的にアップデートしながら利用していきましょう。

$ pip install -U cfn-lint
$ gem update cfn-nag

以上、IaCのコード品質を高めていくきっかけに少しでもなれば幸いです。