

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Klauseln schreiben, um kontextsensitive Bewertungen durchzuführen
<a name="context-aware-evaluations"></a>

AWS CloudFormation Guard Klauseln werden anhand hierarchischer Daten ausgewertet. Die Guard-Evaluierungs-Engine löst Abfragen anhand eingehender Daten, indem sie hierarchischen Daten wie angegeben folgt und dabei eine einfache Punktnotation verwendet. Häufig sind mehrere Klauseln erforderlich, um eine Auswertung anhand einer Datenkarte oder einer Sammlung durchzuführen. Guard bietet eine praktische Syntax zum Schreiben solcher Klauseln. Die Engine ist kontextsensitiv und verwendet die entsprechenden zugehörigen Daten für Auswertungen.

Im Folgenden finden Sie ein Beispiel für eine Kubernetes-Pod-Konfiguration mit Containern, auf die Sie kontextsensitive Evaluierungen anwenden können.

```
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
```

Sie können Guard-Klauseln verfassen, um diese Daten auszuwerten. Bei der Auswertung einer Regeldatei ist der Kontext das gesamte Eingabedokument. Im Folgenden finden Sie Beispielklauseln, die die Durchsetzung von Grenzwerten für in einem Pod angegebene Container validieren.

```
#
# 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
            >>
        }
    }
}
```

## Verständnis bei `context` Evaluierungen
<a name="context"></a>

Auf der Ebene der Regelblöcke ist der eingehende Kontext das vollständige Dokument. Die Auswertung der `when` Bedingung erfolgt anhand dieses eingehenden Stammkontextes, in dem sich die `kind` Attribute `apiVersion` und befinden. Im vorherigen Beispiel werden diese Bedingungen wie folgt ausgewertet`true`.

Gehen Sie nun durch die Hierarchie, `spec.containers[*]` wie im vorherigen Beispiel gezeigt. Bei jeder Durchquerung der Hierarchie ändert sich der Kontextwert entsprechend. Nachdem die Durchquerung des `spec` Blocks abgeschlossen ist, ändert sich der Kontext, wie im folgenden Beispiel gezeigt.

```
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
```

Nach dem Durchlaufen des `containers` Attributs wird der Kontext im folgenden Beispiel gezeigt.

```
- 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
```

## Schleifen verstehen
<a name="loops"></a>

Sie können den Ausdruck verwenden`[*]`, um eine Schleife für alle Werte zu definieren, die im Array für das `containers` Attribut enthalten sind. Der Block wird für jedes darin enthaltene Element ausgewertet`containers`. Im obigen Beispiel für einen Regelausschnitt definieren die im Block enthaltenen Klauseln Prüfungen, die anhand einer Containerdefinition validiert werden sollen. Der darin enthaltene Klauselblock wird zweimal ausgewertet, einmal für jede Containerdefinition.

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

Für jede Iteration ist der Kontextwert der Wert an dem entsprechenden Index.

**Anmerkung**  
Das einzige unterstützte Indexzugriffsformat ist `[<integer>]` oder`[*]`. Derzeit unterstützt Guard keine Bereiche wie`[2..4]`.

## Arrays
<a name="arrays"></a>

Oft werden an Stellen, an denen ein Array akzeptiert wird, auch Einzelwerte akzeptiert. Wenn es beispielsweise nur einen Container gibt, kann das Array gelöscht werden und die folgende Eingabe wird akzeptiert.

```
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
```

Wenn ein Attribut ein Array akzeptieren kann, stellen Sie sicher, dass Ihre Regel die Array-Form verwendet. Im vorherigen Beispiel verwenden Sie `containers[*]` und nicht`containers`. Guard führt beim Durchlaufen der Daten eine korrekte Auswertung durch, wenn es nur auf die Form mit einem Wert trifft.

**Anmerkung**  
Verwenden Sie immer die Array-Form, wenn Sie den Zugriff auf eine Regelklausel ausdrücken, wenn ein Attribut ein Array akzeptiert. Guard wertet auch dann korrekt aus, wenn nur ein einziger Wert verwendet wird.

## Verwenden Sie das Formular `spec.containers[*]` anstelle von `spec.containers`
<a name="containers"></a>

Guard-Abfragen geben eine Sammlung aufgelöster Werte zurück. Wenn Sie das Formular verwenden`spec.containers`, enthalten die aufgelösten Werte für die Abfrage das Array, auf das von verwiesen wird`containers`, nicht die darin enthaltenen Elemente. Wenn Sie das Formular verwenden`spec.containers[*]`, beziehen Sie sich auf jedes einzelne enthaltene Element. Denken Sie daran, das `[*]` Formular immer dann zu verwenden, wenn Sie jedes in der Matrix enthaltene Element auswerten möchten.

## Wird verwendet`this`, um auf den aktuellen Kontextwert zu verweisen
<a name="this"></a>

Wenn Sie eine Guard-Regel erstellen, können Sie auf den Kontextwert verweisen, indem Sie `this` Oft `this` ist dies implizit, weil es an den Wert des Kontextes gebunden ist. Zum Beispiel `this.spec` sind `this.apiVersion``this.kind`, und an den Stamm oder das Dokument gebunden. Im Gegensatz dazu `this.resources` ist an jeden Wert für gebunden`containers`, z. B. `/spec/containers/0/` und`/spec/containers/1`. `this.cpu`Ähnliches gilt für die `this.memory` Zuordnung zu Grenzwerten, insbesondere `/spec/containers/0/resources/limits` und`/spec/containers/1/resources/limits`. 

Im nächsten Beispiel wurde die vorherige Regel für die Kubernetes-Pod-Konfiguration so umgeschrieben, dass sie explizit verwendet wird. `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
            >>
        }
    }
}
```

Sie müssen dies nicht explizit verwenden. `this` Die `this` Referenz kann jedoch nützlich sein, wenn Sie mit Skalaren arbeiten, wie im folgenden Beispiel gezeigt.

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

Im vorherigen Beispiel `this` wird verwendet, um auf jede Portnummer zu verweisen.

## Mögliche Fehler bei der Verwendung von implizit `this`
<a name="common-errors"></a>

Beim Verfassen von Regeln und Klauseln treten häufig Fehler auf, wenn auf Elemente aus dem impliziten Kontextwert verwiesen wird. `this` Stellen Sie sich zum Beispiel das folgende Eingabedatum vor, anhand dessen ausgewertet werden soll (dieses muss erfolgreich sein).

```
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
```

Beim Testen mit der vorherigen Vorlage führt die folgende Regel zu einem Fehler, da sie fälschlicherweise davon ausgeht, dass das Implizite `this` genutzt wird.

```
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
                >>
            }                
        }
    }
}
```

Um dieses Beispiel durchzugehen, speichern Sie die vorherige Regeldatei mit dem Namen `any_ip_ingress_check.guard` und die Daten mit dem Dateinamen. `ip_ingress.yaml` Führen Sie dann den folgenden `validate` Befehl mit diesen Dateien aus.

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

In der folgenden Ausgabe gibt die Engine an, dass ihr Versuch, eine Eigenschaft für `InputParameters.TcpBlockedPorts[*]` den Wert abzurufen`/configuration/ipPermissions/0`, `/configuration/ipPermissions/1` fehlgeschlagen ist.

```
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[*]
```

Um dieses Ergebnis besser zu verstehen, schreiben Sie die Regel neu, indem Sie `this` explizit referenziert verwenden.

```
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`verweist auf jeden Wert, der in der Variablen `any_ip_permissions` enthalten ist. Die der Variablen zugewiesene Abfrage wählt `configuration.ipPermissions` übereinstimmende Werte aus. Der Fehler weist auf einen Abrufversuch `InputParamaters` in diesem Kontext hin, der jedoch im Stammkontext `InputParameters` erfolgte.

Der innere Block verweist auch auf Variablen, die außerhalb des Gültigkeitsbereichs liegen, wie im folgenden Beispiel gezeigt.

```
{
    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`bezieht sich auf jeden Portwert in`[21, 22, 110]`, bezieht sich aber auch auf `fromPort` und`toPort`. Sie gehören beide zum Bereich des äußeren Blocks.

### Behebung von Fehlern mit der impliziten Verwendung von `this`
<a name="common-errors-resolution"></a>

Verwenden Sie Variablen, um Werte explizit zuzuweisen und zu referenzieren. Erstens `InputParameter.TcpBlockedPorts` ist es Teil des Eingabekontextes (Stammkontextes). `InputParameter.TcpBlockedPorts`Verlassen Sie den inneren Block und weisen Sie ihn explizit zu, wie im folgenden Beispiel gezeigt.

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

Verweisen Sie dann explizit auf diese Variable.

```
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
                >>
            }
        }
    }        
}
```

Machen Sie dasselbe für innere `this` Verweise im Inneren`%ports`.

Es sind jedoch noch nicht alle Fehler behoben, da die interne Schleife `ports` immer noch eine falsche Referenz enthält. Das folgende Beispiel zeigt das Entfernen der falschen Referenz.

```
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
                    >>
                }
            }
        }       
    }   
}
```

Führen Sie als Nächstes den `validate` Befehl erneut aus. Diesmal ist es vorbei.

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

Das Folgende ist die Ausgabe des `validate` Befehls.

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

Um diesen Ansatz auf Fehler zu testen, wird im folgenden Beispiel eine Payload-Änderung verwendet.

```
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 liegt im Bereich von 89—109, für den jede beliebige IPv6 Adresse zulässig ist. Im Folgenden wird der `validate` Befehl ausgegeben, nachdem er erneut ausgeführt wurde.

```
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
```