🎪

AWS Config Custom Policy Rules、完全に理解した

2022/06/28に公開

はじめに

こんなツイートが流れてきた。

https://twitter.com/benbridts/status/1527059764555010049

ここ1年くらいConfigRuleと向き合い続けている身からすると、叫んでしまうレベルのアップデートなんだけど日本語圏の盛り上がりはいまいち。ここは試しておきたいところ。

What’s Custom Policy Rules?

AWS Config Rule

ざっくり言うと、AWSリソースの設定値を記録するサービスであるAWS Config を使用して、リソースの設定内容を評価するのがAWS Config Ruleである。

ConfigRuleは次の種類があり、2-2が新しくできるようになった機能。

  1. Managed Rules
  1. Custom Rules
  • 利用者が実装するルールの総称。
    1. Custom Lambda Rules
    • AWS Lambdaで実装するConfig Rule。
    • 以前のCustom Rulesはこれだけだったので、Custom Rules=AWS Lambdaで実装するConfigRuleと同義であった。
    1. Custom Policy Rules ← New!
    • Guard(CloudFormation Guard)で実装するConfig Rule。
    • シンプルなDSLで記述できるのが良さそう。

Lambda Rules VS Policy Rules

Custom Rulesはもう少し深堀して比較してみる。

  1. Lambda Rules
  • Pros
    • 使い慣れたプログラミング言語で自由度の高いルールが記述できる。
    • ConfigRecorder管理外のリソース、プロパティもAPIコールで取得可能。
  • Cons
    • ConfigRuleとして動作させるための、独特のBoilerplate
    • 関連するリソース管理の重たさ(Lambda、IAMRole)
  1. Policy Rules
  • Pros

    • コアロジックのみを記載すれば動くので、シンプルで管理しやすい。
    • コード以外のインフラはすべてAWS管理のため、IAMなどの管理から解放される
  • Cons

    • 複雑なロジックが必要な場合には向かない。
    • ConfigRecorderで定義されたパラメータのみがスコープ
    • 2022/6時点でCDK(CloudFormation)非対応
      • Terraformは可
  • 一言でいうと今までのLambda Rules、ちょっとリッチ過ぎたよね。という中間的な意味合いで生まれたのがPolicy Rules。と捉えると腑に落ちる。

    • パラメータ一つ確認するのが目的の場合、今までのLambdaでの実装は少々重過ぎる。

Let’s Try!

1. 準備

https://docs.aws.amazon.com/config/latest/developerguide/evaluate-config_develop-rules_cfn-guard.html

DynamoDBのpointInTimeRecoveryStatus=Trueを確認するルールが公式に掲載されている。以下はサンプルの引用。

# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables
let status = ['ACTIVE']

rule tableisactive when
    resourceType == "AWS::DynamoDB::Table" {
    configuration.tableStatus == %status
}

rule checkcompliance when
    resourceType == "AWS::DynamoDB::Table"
    tableisactive {
        let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus
        %pitr == "ENABLED"
}

ほーん。Lambda Rulesに慣れた身からすると、シンプルでわかりやすい。
※Custom Lambda Rulesの記述が気になる方は、この辺りを参考。
https://github.com/awslabs/aws-config-rules

2. オリジナルのルール作成

  • とりあえずEBSを見るルールを作ってみよう。
    • EBSは最新のVolumeTypeを利用できているか?
      • 最新:io2, gp3, sc1, st1
  • 要はgp2(+α)で間違えて作ったEBSを検知して非準拠にするルール。見よう見まねで書いてみる。
let latest = ["io2", "gp3", "sc1", "st1"]

rule checkcompliance when
  resourceType == "AWS::EC2::Volume" {
  configuration.VolumeType IN %latest
}

3.検証

結論から言うと動かない。CloudWatch Logsへログが吐けるので設定を有効にして確認してみる。

ConfigRuleId: config-rule-gvfpun, ResourceType: AWS::EC2::Volume, ResourceId: vol-084fe6f654db5cf28 {"data_from":"DATA_STDIN[1]","rules_from":"RULES_STDIN[1]","not_compliant":{"checkcompliance":[{"rule":"checkcompliance","path":"/configuration","provided":null,"expected":null,"comparison":null,"message":"Attempting to retrieve array index or key from map at path = /configuration , Type was not an array/object map, Remaining Query = VolumeType"}]},"not_applicable":[],"compliant":[]}

……良くわからない。JSONを抜粋。

{"data_from":"DATA_STDIN[1]","rules_from":"RULES_STDIN[1]","not_compliant":{"checkcompliance":[{"rule":"checkcompliance","path":"/configuration","provided":null,"expected":null,"comparison":null,"message":"Attempting to retrieve array index or key from map at path = /configuration , Type was not an array/object map, Remaining Query = VolumeType"}]},"not_applicable":[],"compliant":[]}

エラーメッセージは
Attempting to retrieve array index or key from map at path = /configuration , Type was not an array/object map, Remaining Query = VolumeType

本業でAWS Config普通にやってるんだけど、正直何言ってるかわからない。マジ!?

解析

サンプルの解析

pathが違う、スキーマが~ということはパラメータの参照方法がおかしいのだろうか?
ConfigRecorderに格納される値の持ち方を理解しないとダメそう。正常に動くサンプルコードを元に、改めて確認する。

  • ConfigRecorder(AWS::DynamoDB::Table)のリソースの抜粋
{
  "configuration": {
    "tableStatus": "ACTIVE",
  },
  "supplementaryConfiguration": {
    "ContinuousBackupsDescription": {
      "pointInTimeRecoveryDescription": {
        "pointInTimeRecoveryStatus": "DISABLED"
      }
    },
  }
}
公式サンプル(再掲)
# This rule checks if point in time recovery (PITR) is enabled on active Amazon DynamoDB tables
let status = ['ACTIVE']

rule tableisactive when
    resourceType == "AWS::DynamoDB::Table" {
    configuration.tableStatus == %status
}

rule checkcompliance when
    resourceType == "AWS::DynamoDB::Table"
    tableisactive {
        let pitr = supplementaryConfiguration.ContinuousBackupsDescription.pointInTimeRecoveryDescription.pointInTimeRecoveryStatus
        %pitr == "ENABLED"
}

雰囲気はわかる。で、自分が作ったルールもそこまで間違っているように見えない。。。

作ったルール(EBS)の解析

続いては 該当の

  • ConfigRecorder(AWS::EC2::Volume)のリソースの抜粋
{
  "configuration": {
    "volumeType": "gp2",
  },
  "supplementaryConfiguration": {},
  "resourceTransitionStatus": "None"
}
  • 動かないコード
let latest = ["io2", "gp3", "sc1", "st1"]

rule checkcompliance when
  resourceType == "AWS::EC2::Volume" {
  configuration.VolumeType IN %latest
}
  • NG: configuration.VolumeType
  • OK: configuration.volumeType

ただのTypoでした。直したら無事動いて嬉しい!

Policy Rules作成のコツ

役に立ったこと

巷のサンプルもあまり多いわけではないので、基本的にこれを読むしかない。
Policy Rulesにおいて、Guardの文法は2.0以降でないといけない。ググってサンプル集めをする際は文法が古くないことを確認しよう。

今回の愚直な実環境デバッグはこれさえ見つけていれば不要だった。リソースを逐一作成→Config Recoderと睨めっこの回数が大幅に減りそう。
※issueやPR見ると、含まれていないPropatyもありそうなので、最終的に実環境で確認するしかないシチュエーションはありそうだが……いっそ公式で提供してほしいものである。

あまり役に立たなかったこと

  • Guardリポジトリのチュートリアル、サンプルを探す

具体例というかサンプルがなく、コピペから始めるのは筋が悪い。
……結構力技じゃない?という感は否めない。たくさん作ることになったら大変かもしれない。

  • supplementaryConfigurationで調べてみる

https://docs.aws.amazon.com/ja_jp/config/latest/APIReference/API_ConfigurationItem.html

supplementaryConfiguration
Configuration attributes that AWS Config returns for certain resource types to supplement the information returned for the configuration parameter.

Type: String to string map

Required: No

→ こういう返し方があるよ!そっか程度。申し訳ないがこの記述からは、大した情報が得られない。

まとめ

  • 機能を見つけても細かいところはなんもわからん!だったとこが大分改善されて良かった。
  • CDK(Cfn)対応されると嬉しいね。

とりあえずAWS Config×Terraformで回してる人は困らなそうなので、仮にもLambdaベースのCustomruleが既にある人も描き直しても良さそう。

Discussion