Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.
Menulis klausa untuk melakukan evaluasi sadar konteks
AWS CloudFormation Guard klausa dievaluasi terhadap data hierarkis. Mesin evaluasi Guard menyelesaikan kueri terhadap data yang masuk dengan mengikuti data hierarkis seperti yang ditentukan, menggunakan notasi putus-putus sederhana. Seringkali, beberapa klausa diperlukan untuk mengevaluasi terhadap peta data atau koleksi. Guard menyediakan sintaks yang nyaman untuk menulis klausa tersebut. Mesin sadar kontekstual dan menggunakan data terkait yang terkait untuk evaluasi.
Berikut ini adalah contoh konfigurasi Kubernetes Pod dengan kontainer, di mana Anda dapat menerapkan evaluasi konteks-sadar.
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
Anda dapat membuat klausa Guard untuk mengevaluasi data ini. Saat mengevaluasi file aturan, konteksnya adalah seluruh dokumen input. Berikut ini adalah contoh klausul yang memvalidasi limit enforcement untuk container yang ditentukan dalam sebuah 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 >> } } }
Pemahaman context
dalam evaluasi
Pada tingkat blok aturan, konteks yang masuk adalah dokumen lengkap. Evaluasi untuk when
kondisi terjadi terhadap konteks root yang masuk ini di mana kind
atribut apiVersion
and berada. Dalam contoh sebelumnya, kondisi ini mengevaluasitrue
.
Sekarang, lintasi hierarki yang spec.containers[*]
ditunjukkan pada contoh sebelumnya. Untuk setiap lintasan hierarki, nilai konteks berubah sesuai dengan itu. Setelah traversal spec
blok selesai, konteksnya berubah, seperti yang ditunjukkan pada contoh berikut.
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
Setelah melintasi containers
atribut, konteksnya ditampilkan dalam contoh berikut.
- 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
Memahami loop
Anda dapat menggunakan ekspresi [*]
untuk mendefinisikan loop untuk semua nilai yang terkandung dalam array untuk containers
atribut. Blok dievaluasi untuk setiap elemen di dalamnyacontainers
. Dalam cuplikan aturan contoh sebelumnya, klausa yang terkandung di dalam blok menentukan pemeriksaan yang akan divalidasi terhadap definisi kontainer. Blok klausa yang terkandung di dalamnya dievaluasi dua kali, satu kali untuk setiap definisi kontainer.
{ spec.containers[*] { ... } }
Untuk setiap iterasi, nilai konteks adalah nilai pada indeks yang sesuai.
catatan
Satu-satunya format akses indeks yang didukung adalah [<integer>]
atau[*]
. Saat ini, Guard tidak mendukung rentang seperti[2..4]
.
Array
Seringkali di tempat-tempat di mana array diterima, nilai tunggal juga diterima. Misalnya, jika hanya ada satu kontainer, array dapat dijatuhkan dan input berikut diterima.
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
Jika atribut dapat menerima array, pastikan bahwa aturan Anda menggunakan formulir array. Dalam contoh sebelumnya, Anda menggunakan containers[*]
dan tidak. containers
Guard mengevaluasi dengan benar saat melintasi data ketika hanya menemukan bentuk nilai tunggal.
catatan
Selalu gunakan formulir array saat mengekspresikan akses untuk klausa aturan ketika atribut menerima array. Guard mengevaluasi dengan benar bahkan dalam kasus bahwa satu nilai digunakan.
Menggunakan formulir spec.containers[*]
alih-alih spec.containers
Kueri penjaga mengembalikan kumpulan nilai yang diselesaikan. Saat Anda menggunakan formulirspec.containers
, nilai yang diselesaikan untuk kueri berisi array yang dirujuk olehcontainers
, bukan elemen di dalamnya. Ketika Anda menggunakan formulirspec.containers[*]
, Anda merujuk ke setiap elemen individu yang terkandung. Ingatlah untuk menggunakan [*]
formulir setiap kali Anda berniat untuk mengevaluasi setiap elemen yang terkandung dalam array.
Menggunakan this
untuk mereferensikan nilai konteks saat ini
Saat Anda membuat aturan Guard, Anda dapat mereferensikan nilai konteks dengan menggunakanthis
. Seringkalithis
, implisit karena terikat pada nilai konteks. Misalnya,this.apiVersion
,this.kind
, dan this.spec
terikat pada root atau dokumen. Sebaliknya, this.resources
terikat pada setiap nilai untukcontainers
, seperti /spec/containers/0/
dan/spec/containers/1
. Demikian pula, this.cpu
dan this.memory
memetakan ke batas, secara khusus /spec/containers/0/resources/limits
dan/spec/containers/1/resources/limits
.
Dalam contoh berikutnya, aturan sebelumnya untuk konfigurasi Kubernetes Pod ditulis ulang untuk digunakan secara eksplisit. 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 >> } } }
Anda tidak perlu menggunakan secara this
eksplisit. Namun, this
referensi dapat berguna saat bekerja dengan skalar, seperti yang ditunjukkan pada contoh berikut.
InputParameters.TcpBlockedPorts[*] { this in r[0, 65535) << result: NON_COMPLIANT message: TcpBlockedPort not in range (0, 65535) >> }
Pada contoh sebelumnya, this
digunakan untuk merujuk ke setiap nomor port.
Potensi kesalahan dengan penggunaan implisit this
Saat membuat aturan dan klausa, ada beberapa kesalahan umum saat mereferensikan elemen dari nilai konteks implisit. this
Misalnya, pertimbangkan datum masukan berikut untuk dievaluasi (ini harus lulus).
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
Ketika diuji terhadap template sebelumnya, aturan berikut menghasilkan kesalahan karena membuat asumsi yang salah untuk memanfaatkan implisit. 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 >> } } } }
Untuk menelusuri contoh ini, simpan file aturan sebelumnya dengan nama any_ip_ingress_check.guard
dan data dengan nama file. ip_ingress.yaml
Kemudian, jalankan validate
perintah berikut dengan file-file ini.
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
Dalam output berikut, mesin menunjukkan bahwa usahanya untuk mengambil properti InputParameters.TcpBlockedPorts[*]
pada nilai/configuration/ipPermissions/0
, /configuration/ipPermissions/1
gagal.
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[*]
Untuk membantu memahami hasil ini, tulis ulang aturan menggunakan referensi this
eksplisit.
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
referensi setiap nilai yang terkandung di dalam variabelany_ip_permissions
. Kueri yang ditetapkan ke variabel memilih configuration.ipPermissions
nilai yang cocok. Kesalahan menunjukkan upaya untuk mengambil InputParamaters
dalam konteks ini, tetapi InputParameters
berada dalam konteks root.
Blok dalam juga mereferensikan variabel yang berada di luar cakupan, seperti yang ditunjukkan pada contoh berikut.
{ 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
mengacu pada setiap nilai port di[21, 22, 110]
, tetapi juga mengacu pada fromPort
dantoPort
. Keduanya termasuk dalam lingkup blok luar.
Menyelesaikan kesalahan dengan penggunaan implisit this
Gunakan variabel untuk secara eksplisit menetapkan dan mereferensikan nilai. Pertama, InputParameter.TcpBlockedPorts
adalah bagian dari konteks input (root). Pindah InputParameter.TcpBlockedPorts
keluar dari blok dalam dan tetapkan secara eksplisit, seperti yang ditunjukkan pada contoh berikut.
rule check_ip_procotol_and_port_range_validity { let ports = InputParameters.TcpBlockedPorts[*] # ... cut off for illustrating change }
Kemudian, lihat variabel ini secara eksplisit.
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 >> } } } }
Lakukan hal yang sama untuk this
referensi batin di dalam%ports
.
Namun, semua kesalahan belum diperbaiki karena loop di dalamnya ports
masih memiliki referensi yang salah. Contoh berikut menunjukkan penghapusan referensi yang salah.
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 >> } } } } }
Selanjutnya, jalankan validate
perintah lagi. Kali ini, berlalu.
cfn-guard validate -r any_ip_ingress_check.guard -d ip_ingress.yaml --show-clause-failures
Berikut ini adalah output dari validate
perintah.
Summary Report Overall File Status = PASS PASS/SKIP rules check_ip_procotol_and_port_range_validity PASS
Untuk menguji pendekatan ini untuk kegagalan, contoh berikut menggunakan perubahan payload.
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 berada dalam kisaran 89—109 yang memiliki IPv6 alamat apa pun yang diizinkan. Berikut ini adalah output dari validate
perintah setelah menjalankannya lagi.
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