

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# コンテキスト対応評価を実行するための句の記述
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard 句は階層データに対して評価されます。ガード評価エンジンは、シンプルな点線表記を使用して、指定された階層データに従って受信データに対するクエリを解決します。多くの場合、データのマップまたはコレクションに対して評価するには、複数の句が必要です。Guard は、このような句を記述するのに便利な構文を提供します。エンジンはコンテキストを認識し、評価に関連する対応するデータを使用します。

以下は、コンテキスト対応評価を適用できるコンテナを使用した Kubernetes Pod 設定の例です。

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    - name: app
      image: 'images.my-company.example/app:v4'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.5
    - name: log-aggregator
      image: 'images.my-company.example/log-aggregator:v6'
      resources:
        requests:
          memory: 64Mi
          cpu: 0.25
        limits:
          memory: 128Mi
          cpu: 0.75
```

Guard 句を作成して、このデータを評価できます。ルールファイルを評価する場合、コンテキストは入力ドキュメント全体です。以下は、Pod で指定されたコンテナの制限の適用を検証する句の例です。

```
#
# At this level, the root document is available for evaluation
#

#
# Our rule only evaluates for apiVersion == v1 and K8s kind is Pod
#
rule ensure_container_limits_are_enforced
    when apiVersion == 'v1'
        kind == 'Pod' 
{
    spec.containers[*] {
        resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

## 評価`context`における理解
<a name="context"></a>

ルールブロックレベルでは、受信コンテキストは完全なドキュメントです。`when` 条件の評価は、 属性`apiVersion`と `kind` 属性があるこの受信ルートコンテキストに対して行われます。前の例では、これらの条件は に評価されます`true`。

ここで、前の例`spec.containers[*]`に示す の階層をトラバースします。階層のトラバースごとに、コンテキスト値はそれに応じて変わります。`spec` ブロックのトラバーサルが完了すると、次の例に示すようにコンテキストが変更されます。

```
containers:
  - name: app
    image: 'images.my-company.example/app:v4'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.5
  - name: log-aggregator
    image: 'images.my-company.example/log-aggregator:v6'
    resources:
      requests:
        memory: 64Mi
        cpu: 0.25
      limits:
        memory: 128Mi
        cpu: 0.75
```

`containers` 属性をトラバースした後、コンテキストを次の例に示します。

```
- name: app
  image: 'images.my-company.example/app:v4'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.5
- name: log-aggregator
  image: 'images.my-company.example/log-aggregator:v6'
  resources:
    requests:
      memory: 64Mi
      cpu: 0.25
    limits:
      memory: 128Mi
      cpu: 0.75
```

## ループについて
<a name="loops"></a>

式を使用して`[*]`、 `containers` 属性の配列に含まれるすべての値のループを定義できます。ブロックは、 内の各要素について評価されます`containers`。前述のルールスニペットの例では、 ブロックに含まれる句は、コンテナ定義に対して検証されるチェックを定義します。内部に含まれる句のブロックは、コンテナ定義ごとに 1 回、2 回評価されます。

```
{
    spec.containers[*] {
       ...
    }
}
```

反復ごとに、コンテキスト値は対応するインデックスの値です。

**注記**  
サポートされているインデックスアクセス形式は、 `[<integer>]`または のみです`[*]`。現在、Guard は のような範囲をサポートしていません`[2..4]`。

## 配列
<a name="arrays"></a>

多くの場合、配列が受け入れられる場所では、単一の値も受け入れられます。たとえば、コンテナが 1 つしかない場合、配列を削除して次の入力を受け入れることができます。

```
apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
    name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: 0.25
      limits:
        memory: "128Mi"
        cpu: 0.5
```

属性が配列を受け入れることができる場合は、ルールが配列形式を使用していることを確認します。前の例では、 `containers[*]`ではなく を使用します`containers`。Guard は、単一値フォームのみを検出したときにデータをトラバースするときに正しく評価します。

**注記**  
属性が配列を受け入れるときにルール句へのアクセスを表現するときは、常に配列形式を使用してください。単一の値を使用する場合でも、ガードは正しく評価されます。

## `spec.containers[*]` の代わりに フォームを使用する `spec.containers`
<a name="containers"></a>

ガードクエリは解決された値のコレクションを返します。フォーム を使用する場合`spec.containers`、クエリの解決された値には、クエリ内の要素ではなく`containers`、 によって参照される配列が含まれます。フォームを使用する場合は`spec.containers[*]`、含まれる個々の要素を参照します。配列に含まれる各要素を評価する場合は、必ず `[*]`フォームを使用してください。

## `this` を使用して現在のコンテキスト値を参照する
<a name="this"></a>

ガードルールを作成するときは、 を使用してコンテキスト値を参照できます`this`。多くの場合、 `this`はコンテキストの値にバインドされているため、暗黙的です。たとえば、、`this.apiVersion``this.kind`、 `this.spec`はルートまたはドキュメントにバインドされます。対照的に、 `this.resources`は `/spec/containers/0/`や など`containers`、 の各値にバインドされます`/spec/containers/1`。同様に、 `this.cpu`と は制限、特に `/spec/containers/0/resources/limits`と に`this.memory`マッピングされます`/spec/containers/1/resources/limits`。

次の例では、Kubernetes Pod 設定の前述のルールが`this`明示的に を使用するように書き換えられています。

```
rule ensure_container_limits_are_enforced
    when this.apiVersion == 'v1'
         this.kind == 'Pod' 
{
    this.spec.containers[*] {
        this.resources.limits {
            #
            # Ensure that cpu attribute is set
            #
            this.cpu exists
            <<
                Id: K8S_REC_18
                Description: CPU limit must be set for the container
            >> 

            #
            # Ensure that memory attribute is set
            #
            this.memory exists
            <<
                Id: K8S_REC_22
                Description: Memory limit must be set for the container
            >>
        }
    }
}
```

を`this`明示的に使用する必要はありません。ただし、次の例に示すように、この`this`リファレンスはスカラーを使用する場合に便利です。

```
InputParameters.TcpBlockedPorts[*] {
    this in r[0, 65535) 
    <<
        result: NON_COMPLIANT
        message: TcpBlockedPort not in range (0, 65535)
    >>
}
```

前の例では、 `this` は各ポート番号を参照するために使用されます。

## 暗黙的な の使用による潜在的なエラー `this`
<a name="common-errors"></a>

ルールと句を作成する場合、暗黙的な`this`コンテキスト値から要素を参照するときによくある間違いがあります。たとえば、評価する次の入力データムを考えてみます (合格する必要があります）。

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 110]
configuration:
  ipPermissions:
  - fromPort: 172
    ipProtocol: tcp
    ipv6Ranges: []
    prefixListIds: []
    toPort: 172
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: "0.0.0.0/0"   
  - fromPort: 89
    ipProtocol: tcp
    ipv6Ranges:
      - cidrIpv6: "::/0"
    prefixListIds: []
    toPort: 109
    userIdGroupPairs: []
    ipv4Ranges:
      - cidrIp: 10.2.0.0/24
```

上記のテンプレートに対してテストすると、次のルールは暗黙的な を活用することを誤って想定するため、エラーが発生します`this`。

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1' # this here refers to each ipPermission instance
            InputParameters.TcpBlockedPorts[*] {
                fromPort > this or 
                toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

この例を実行するには、前述のルールファイルを という名前で保存`any_ip_ingress_check.guard`し、データを というファイル名で保存します`ip_ingress.yaml`。次に、これらのファイルを使用して次の`validate`コマンドを実行します。

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

次の出力では、エンジンは値 `InputParameters.TcpBlockedPorts[*]`のプロパティを取得しようとする試みが`/configuration/ipPermissions/0``/configuration/ipPermissions/1`失敗したことを示します。

```
Clause #2     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/0, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]

Clause #3     FAIL(Block[Location[file:any_ip_ingress_check.guard, line:17, column:13]])

              Attempting to retrieve array index or key from map at Path = /configuration/ipPermissions/1, Type was not an array/object map, Remaining Query = InputParameters.TcpBlockedPorts[*]
```

この結果を理解するには、`this`明示的に参照された を使用してルールを書き換えます。

```
rule check_ip_procotol_and_port_range_validity
{
    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = this.configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            this.InputParameters.TcpBlockedPorts[*] {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }                
        }
    }
}
```

`this.InputParameters` は、変数 に含まれる各値を参照します`any_ip_permissions`。変数に割り当てられたクエリは、一致する`configuration.ipPermissions`値を選択します。エラーは、このコンテキスト`InputParamaters`で を取得しようと`InputParameters`したが、ルートコンテキストにあったことを示します。

内部ブロックは、次の例に示すように、範囲外の変数も参照します。

```
{
    this.ipProtocol != '-1' # this here refers to each ipPermission instance
    this.InputParameter.TcpBlockedPorts[*] { # ERROR referencing InputParameter off /configuration/ipPermissions[*]
        this.fromPort > this or # ERROR: implicit this refers to values inside /InputParameter/TcpBlockedPorts[*]
        this.toPort   < this 
        <<
            result: NON_COMPLIANT
            message: Blocked TCP port was allowed in range
        >>
    }
}
```

`this` は の各ポート値を参照しますが`[21, 22, 110]`、 `fromPort`および も参照します`toPort`。どちらも外部ブロックスコープに属します。

### の暗黙的な使用によるエラーの解決 `this`
<a name="common-errors-resolution"></a>

変数を使用して、値を明示的に割り当て、参照します。まず、 `InputParameter.TcpBlockedPorts`は入力 (ルート) コンテキストの一部です。次の例に示すように、内部ブロック`InputParameter.TcpBlockedPorts`の外に移動し、明示的に割り当てます。

```
rule check_ip_procotol_and_port_range_validity
{
     let ports = InputParameters.TcpBlockedPorts[*]
    # ... cut off for illustrating change
}
```

次に、この変数を明示的に参照します。

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110]. 
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[ 
        some ipv4Ranges[*].cidrIp == "0.0.0.0/0" or
        some ipv6Ranges[*].cidrIpv6 == "::/0"

        ipProtocol != 'udp' ]
    
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            this.ipProtocol != '-1' # this here refers to each ipPermission instance
            %ports {
                this.fromPort > this or 
                this.toPort   < this 
                <<
                    result: NON_COMPLIANT
                    message: Blocked TCP port was allowed in range
                >>
            }
        }
    }        
}
```

内の内部`this`参照に対しても同じ操作を行います`%ports`。

ただし、内部のループの参照`ports`が正しくないため、すべてのエラーはまだ修正されていません。次の例は、誤った参照の削除を示しています。

```
rule check_ip_procotol_and_port_range_validity
{
    #
    # Important: Assigning InputParameters.TcpBlockedPorts results in an ERROR. 
    # We need to extract each port inside the array. The difference is the query
    # InputParameters.TcpBlockedPorts returns [[21, 20, 110]] whereas the query 
    # InputParameters.TcpBlockedPorts[*] returns [21, 20, 110].
    #
    let ports = InputParameters.TcpBlockedPorts[*]

    # 
    # select all ipPermission instances that can be reached by ANY IP address
    # IPv4 or IPv6 and not UDP
    #
    let any_ip_permissions = configuration.ipPermissions[
        #
        # if either ipv4 or ipv6 that allows access from any address
        #
        some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or
        some ipv6Ranges[*].cidrIpv6 == '::/0'

        #
        # the ipProtocol is not UDP
        #
        ipProtocol != 'udp' ]
        
    when %any_ip_permissions !empty
    {
        %any_ip_permissions {
            ipProtocol != '-1'
            <<
              result: NON_COMPLIANT
              check_id: HUB_ID_2334
              message: Any IP Protocol is allowed
            >>

            when fromPort exists 
                 toPort exists 
            {
                let each_any_ip_perm = this
                %ports {
                    this < %each_any_ip_perm.fromPort or
                    this > %each_any_ip_perm.toPort
                    <<
                        result: NON_COMPLIANT
                        check_id: HUB_ID_2340
                        message: Blocked TCP port was allowed in range
                    >>
                }
            }
        }       
    }   
}
```

次に、 `validate` コマンドを再度実行します。今回は合格です。

```
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
```

以下は、 `validate` コマンドの出力です。

```
ip_ingress.yaml Status = PASS
PASS rules
check_ip_procotol_and_port_range_validity    PASS
```

このアプローチに障害がないかテストするために、次の例ではペイロードの変更を使用します。

```
resourceType: 'AWS::EC2::SecurityGroup'
InputParameters:
  TcpBlockedPorts: [21, 22, 90, 110]
configuration:
  ipPermissions:
    - fromPort: 172
      ipProtocol: tcp
      ipv6Ranges: []
      prefixListIds: []
      toPort: 172
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: "0.0.0.0/0"   
    - fromPort: 89
      ipProtocol: tcp
      ipv6Ranges:
        - cidrIpv6: "::/0"
      prefixListIds: []
      toPort: 109
      userIdGroupPairs: []
      ipv4Ranges:
        - cidrIp: 10.2.0.0/24
```

90 は、任意の IPv6 アドレスが許可されている 89～109 の範囲内です。コマンドを再度実行`validate`した後の出力を次に示します。

```
Clause #3           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:43, column:21], Check: _  LESS THAN %each_any_ip_perm.fromPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/fromPort"), 89)) failed
                    (DEFAULT: NO_MESSAGE)
Clause #4           FAIL(Clause(Location[file:any_ip_ingress_check.guard, line:44, column:21], Check: _  GREATER THAN %each_any_ip_perm.toPort))
                    Comparing Int((Path("/InputParameters/TcpBlockedPorts/2"), 90)) with Int((Path("/configuration/ipPermissions/1/toPort"), 109)) failed

                                            result: NON_COMPLIANT
                                            check_id: HUB_ID_2340
                                            message: Blocked TCP port was allowed in range
```