Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.
Mendefinisikan kueri Guard dan pemfilteran
Topik ini mencakup penulisan kueri dan menggunakan pemfilteran saat menulis klausa aturan Guard.
Prasyarat
Penyaringan adalah AWS CloudFormation Guard konsep lanjutan. Kami menyarankan Anda meninjau topik dasar berikut sebelum Anda belajar tentang pemfilteran:
Mendefinisikan kueri
Ekspresi kueri adalah ekspresi terpisah dot (.
) sederhana yang ditulis untuk melintasi data hierarkis. Ekspresi kueri dapat mencakup ekspresi filter untuk menargetkan subset nilai. Ketika kueri dievaluasi, mereka menghasilkan kumpulan nilai, mirip dengan kumpulan hasil yang dikembalikan dari kueri. SQL
Contoh query berikut mencari AWS CloudFormation template untuk AWS::IAM::Role
sumber daya.
Resources.*[ Type == 'AWS::IAM::Role' ]
Pertanyaan mengikuti prinsip-prinsip dasar ini:
-
Setiap titik (
.
) bagian dari kueri melintasi hierarki ketika istilah kunci eksplisit digunakan, sepertiResources
atauProperties.Encrypted.
Jika ada bagian dari kueri yang tidak cocok dengan datum yang masuk, Guard akan melempar kesalahan pengambilan. -
Bagian dot (
.
) dari kueri yang menggunakan wildcard*
melintasi semua nilai untuk struktur pada tingkat itu. -
Bagian dot (
.
) dari kueri yang menggunakan wildcard array[*]
melintasi semua indeks untuk array tersebut. -
Semua koleksi dapat disaring dengan menentukan filter di dalam tanda kurung siku
[]
. Koleksi dapat ditemukan dengan cara-cara berikut:-
Array yang terjadi secara alami dalam datum adalah koleksi. Berikut ini adalah contoh:
Pelabuhan:
[20, 21, 110, 190]
Tag:
[{"Key": "Stage", "Value": "PROD"}, {"Key": "App", "Value": "MyService"}]
-
Saat melintasi semua nilai untuk struktur seperti
Resources.*
-
Setiap hasil kueri itu sendiri merupakan kumpulan dari mana nilai dapat difilter lebih lanjut. Lihat contoh berikut ini.
let all_resources = Resource.* # query let iam_resources = %resources[ Type == /IAM/ ] # filter from query results let managed_policies = %iam_resources[ Type == /ManagedPolicy/ ] # further refinements %managed_policies { # traversing each value # do something with each }
-
Berikut ini adalah contoh cuplikan CloudFormation template.
Resources: SampleRole: Type: AWS::IAM::Role ... SampleInstance: Type: AWS::EC2::Instance ... SampleVPC: Type: AWS::EC2::VPC ... SampleSubnet1: Type: AWS::EC2::Subnet ... SampleSubnet2: Type: AWS::EC2::Subnet ...
Berdasarkan template ini, jalur yang dilalui adalah SampleRole
dan nilai akhir yang dipilih adalah. Type: AWS::IAM::Role
Resources: SampleRole: Type: AWS::IAM::Role ...
Nilai yang dihasilkan dari query Resources.*[ Type == 'AWS::IAM::Role' ]
dalam YAML format ditunjukkan dalam contoh berikut.
- Type: AWS::IAM::Role ...
Beberapa cara yang dapat Anda gunakan kueri adalah sebagai berikut:
-
Tetapkan kueri ke variabel sehingga hasil kueri dapat diakses dengan mereferensikan variabel-variabel tersebut.
-
Ikuti kueri dengan blok yang menguji setiap nilai yang dipilih.
-
Bandingkan kueri secara langsung dengan klausa dasar.
Menetapkan kueri ke variabel
Guard mendukung penugasan variabel satu-shot dalam lingkup tertentu. Untuk informasi selengkapnya tentang variabel dalam aturan Guard, lihatMenetapkan dan mereferensikan variabel dalam aturan Guard.
Anda dapat menetapkan kueri ke variabel sehingga Anda dapat menulis kueri sekali dan kemudian mereferensikannya di tempat lain dalam aturan Guard Anda. Lihat contoh penugasan variabel berikut yang menunjukkan prinsip kueri yang dibahas nanti di bagian ini.
# # Simple query assignment # let resources = Resources.* # All resources # # A more complex query here (this will be explained below) # let iam_policies_allowing_log_creates = Resources.*[ Type in [/IAM::Policy/, /IAM::ManagedPolicy/] some Properties.PolicyDocument.Statement[*] { some Action[*] == 'cloudwatch:CreateLogGroup' Effect == 'Allow' } ]
Langsung perulangan melalui nilai-nilai dari variabel yang ditugaskan ke kueri
Guard mendukung langsung berjalan terhadap hasil dari kueri. Dalam contoh berikut, when
blok menguji terhadapEncrypted
,VolumeType
, dan AvailabilityZone
properti untuk setiap AWS::EC2::Volume
sumber daya yang ditemukan dalam CloudFormation template.
let ec2_volumes = Resources.*[ Type == 'AWS::EC2::Volume' ] when %ec2_volumes !empty { %ec2_volumes { Properties { Encrypted == true VolumeType in ['gp2', 'gp3'] AvailabilityZone in ['us-west-2b', 'us-west-2c'] } } }
Perbandingan tingkat klausa langsung
Guard juga mendukung kueri sebagai bagian dari perbandingan langsung. Misalnya, lihat yang berikut ini.
let resources = Resources.* some %resources.Properties.Tags[*].Key == /PROD$/ some %resources.Properties.Tags[*].Value == /^App/
Dalam contoh sebelumnya, dua klausa (dimulai dengan some
kata kunci) yang dinyatakan dalam bentuk yang ditampilkan dianggap klausa independen dan dievaluasi secara terpisah.
Klausa tunggal dan formulir klausa blok
Secara keseluruhan, dua contoh klausa yang ditunjukkan di bagian sebelumnya tidak setara dengan blok berikut.
let resources = Resources.* some %resources.Properties.Tags[*] { Key == /PROD$/ Value == /^App/ }
Ini memblokir kueri untuk setiap Tag
nilai dalam koleksi dan membandingkan nilai propertinya dengan nilai properti yang diharapkan. Bentuk gabungan dari klausa di bagian sebelumnya mengevaluasi dua klausa secara independen. Pertimbangkan masukan berikut.
Resources: ... MyResource: ... Properties: Tags: - Key: EndPROD Value: NotAppStart - Key: NotPRODEnd Value: AppStart
Klausul dalam bentuk pertama mengevaluasi. PASS
Saat memvalidasi klausa pertama dalam bentuk pertama, jalur berikut melintasiResources
,, Properties
Tags
, dan Key
cocok dengan nilai NotPRODEnd
dan tidak cocok dengan nilai yang diharapkan. PROD
Resources: ... MyResource: ... Properties: Tags: - Key: EndPROD Value: NotAppStart - Key: NotPRODEnd Value: AppStart
Hal yang sama terjadi dengan klausa kedua dari bentuk pertama. Jalur melintasiResources
,, Properties
Tags
, dan Value
cocok dengan nilainyaAppStart
. Akibatnya, klausa kedua secara independen.
Hasil keseluruhannya adalah aPASS
.
Namun, formulir blok mengevaluasi sebagai berikut. Untuk setiap Tags
nilai, itu membandingkan jika keduanya Key
dan Value
tidak cocok; NotAppStart
dan NotPRODEnd
nilai tidak cocok dalam contoh berikut.
Resources: ... MyResource: ... Properties: Tags: - Key: EndPROD Value: NotAppStart - Key: NotPRODEnd Value: AppStart
Karena evaluasi memeriksa keduanyaKey == /PROD$/
, danValue ==
/^App/
, pertandingan tidak lengkap. Karena itu, hasilnya adalahFAIL
.
catatan
Saat bekerja dengan koleksi, kami sarankan Anda menggunakan formulir klausa blok saat Anda ingin membandingkan beberapa nilai untuk setiap elemen dalam koleksi. Gunakan formulir klausa tunggal ketika koleksi adalah sekumpulan nilai skalar, atau ketika Anda hanya bermaksud untuk membandingkan satu atribut.
Hasil kueri dan klausa terkait
Semua query mengembalikan daftar nilai. Setiap bagian dari traversal, seperti kunci yang hilang, nilai kosong untuk array (Tags: []
) saat mengakses semua indeks, atau nilai yang hilang untuk peta saat menghadapi map (Resources: {}
) kosong, dapat menyebabkan kesalahan pengambilan.
Semua kesalahan pengambilan dianggap gagal saat mengevaluasi klausa terhadap kueri tersebut. Satu-satunya pengecualian adalah ketika filter eksplisit digunakan dalam kueri. Saat filter digunakan, klausa terkait dilewati.
Kegagalan blok berikut dikaitkan dengan menjalankan kueri.
-
Jika template tidak berisi sumber daya, maka kueri akan dievaluasi
FAIL
, dan klausa tingkat blok terkait juga mengevaluasi.FAIL
-
Ketika template berisi blok sumber daya kosong seperti
{ "Resources": {} }
, kueri akan dievaluasiFAIL
, dan klausa tingkat blok terkait juga mengevaluasi.FAIL
-
Jika template berisi sumber daya tetapi tidak ada yang cocok dengan kueri, maka kueri mengembalikan hasil kosong, dan klausa tingkat blok dilewati.
Menggunakan filter dalam kueri
Filter dalam kueri secara efektif adalah klausa Penjaga yang digunakan sebagai kriteria pemilihan. Berikut ini adalah struktur klausa.
<query> <operator> [query|value literal] [message] [or|OR]
Ingatlah poin-poin penting berikut dari AWS CloudFormation Guard Aturan penulisan saat Anda bekerja dengan filter:
-
Gabungkan klausa dengan menggunakan Conjunctive Normal Form
(). CNF -
Tentukan setiap klausa konjungsi (
and
) pada baris baru. -
Tentukan disjunctions (
or
) dengan menggunakanor
kata kunci antara dua klausa.
Contoh berikut menunjukkan klausa konjungtif dan disjungtif.
resourceType == 'AWS::EC2::SecurityGroup' InputParameters.TcpBlockedPorts not empty InputParameters.TcpBlockedPorts[*] { this in r(100, 400] or this in r(4000, 65535] }
Menggunakan klausa untuk kriteria seleksi
Anda dapat menerapkan pemfilteran ke koleksi apa pun. Pemfilteran dapat diterapkan langsung pada atribut di input yang sudah menjadi koleksi sepertisecurityGroups:
[....]
. Anda juga dapat menerapkan pemfilteran terhadap kueri, yang selalu merupakan kumpulan nilai. Anda dapat menggunakan semua fitur klausa, termasuk bentuk normal konjungtif, untuk penyaringan.
Kueri umum berikut sering digunakan saat memilih sumber daya berdasarkan jenis dari CloudFormation template.
Resources.*[ Type == 'AWS::IAM::Role' ]
Query Resources.*
mengembalikan semua nilai yang ada di Resources
bagian input. Untuk contoh masukan template diMendefinisikan kueri, query mengembalikan berikut ini.
- Type: AWS::IAM::Role ... - Type: AWS::EC2::Instance ... - Type: AWS::EC2::VPC ... - Type: AWS::EC2::Subnet ... - Type: AWS::EC2::Subnet ...
Sekarang, terapkan filter terhadap koleksi ini. Kriteria untuk mencocokkan adalah. Type == AWS::IAM::Role
Berikut ini adalah output dari query setelah filter diterapkan.
- Type: AWS::IAM::Role ...
Selanjutnya, periksa berbagai klausa untuk AWS::IAM::Role
sumber daya.
let all_resources = Resources.* let all_iam_roles = %all_resources[ Type == 'AWS::IAM::Role' ]
Berikut ini adalah contoh query pemfilteran yang memilih semua AWS::IAM::Policy
dan AWS::IAM::ManagedPolicy
sumber daya.
Resources.*[ Type in [ /IAM::Policy/, /IAM::ManagedPolicy/ ] ]
Contoh berikut memeriksa apakah sumber daya kebijakan ini memiliki yang PolicyDocument
ditentukan.
Resources.*[ Type in [ /IAM::Policy/, /IAM::ManagedPolicy/ ] Properties.PolicyDocument exists ]
Membangun kebutuhan penyaringan yang lebih kompleks
Perhatikan contoh item AWS Config konfigurasi berikut untuk informasi grup keamanan ingress dan egress.
--- resourceType: 'AWS::EC2::SecurityGroup' configuration: ipPermissions: - fromPort: 172 ipProtocol: tcp toPort: 172 ipv4Ranges: - cidrIp: 10.0.0.0/24 - cidrIp: 0.0.0.0/0 - fromPort: 89 ipProtocol: tcp ipv6Ranges: - cidrIpv6: '::/0' toPort: 189 userIdGroupPairs: [] ipv4Ranges: - cidrIp: 1.1.1.1/32 - fromPort: 89 ipProtocol: '-1' toPort: 189 userIdGroupPairs: [] ipv4Ranges: - cidrIp: 1.1.1.1/32 ipPermissionsEgress: - ipProtocol: '-1' ipv6Ranges: [] prefixListIds: [] userIdGroupPairs: [] ipv4Ranges: - cidrIp: 0.0.0.0/0 ipRanges: - 0.0.0.0/0 tags: - key: Name value: good-sg-delete-me vpcId: vpc-0123abcd InputParameter: TcpBlockedPorts: - 3389 - 20 - 21 - 110 - 143
Perhatikan hal berikut:
-
ipPermissions
(aturan ingress) adalah kumpulan aturan di dalam blok konfigurasi. -
Setiap struktur aturan berisi atribut seperti
ipv4Ranges
danipv6Ranges
untuk menentukan kumpulan CIDR blok.
Mari kita menulis aturan yang memilih aturan masuk apa pun yang memungkinkan koneksi dari alamat IP apa pun, dan memverifikasi bahwa aturan tidak mengizinkan port yang TCP diblokir diekspos.
Mulailah dengan bagian query yang mencakupIPv4, seperti yang ditunjukkan pada contoh berikut.
configuration.ipPermissions[ # # at least one
ipv4Ranges
equals ANY IPv4 # some ipv4Ranges[*].cidrIp == '0.0.0.0/0' ]
some
Kata kunci berguna dalam konteks ini. Semua kueri mengembalikan kumpulan nilai yang cocok dengan kueri. Secara default, Guard mengevaluasi bahwa semua nilai yang dikembalikan sebagai hasil dari kueri dicocokkan dengan pemeriksaan. Namun, perilaku ini mungkin tidak selalu menjadi apa yang Anda butuhkan untuk pemeriksaan. Pertimbangkan bagian input berikut dari item konfigurasi.
ipv4Ranges: - cidrIp: 10.0.0.0/24 - cidrIp: 0.0.0.0/0 # any IP allowed
Ada dua nilai yang hadir untukipv4Ranges
. Tidak semua ipv4Ranges
nilai sama dengan alamat IP yang dilambangkan dengan. 0.0.0.0/0
Anda ingin melihat apakah setidaknya satu nilai cocok0.0.0.0/0
. Anda memberi tahu Guard bahwa tidak semua hasil yang dikembalikan dari kueri harus cocok, tetapi setidaknya satu hasil harus cocok. some
Kata kunci memberitahu Guard untuk memastikan bahwa satu atau lebih nilai dari query yang dihasilkan cocok dengan cek. Jika tidak ada nilai hasil kueri yang cocok, Guard akan melempar kesalahan.
Selanjutnya, tambahkanIPv6, seperti yang ditunjukkan pada contoh berikut.
configuration.ipPermissions[ # # at-least-one ipv4Ranges equals ANY IPv4 # some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or # # at-least-one ipv6Ranges contains ANY IPv6 # some ipv6Ranges[*].cidrIpv6 == '::/0' ]
Akhirnya, dalam contoh berikut, validasi bahwa protokol tidakudp
.
configuration.ipPermissions[ # # at-least-one ipv4Ranges equals ANY IPv4 # some ipv4Ranges[*].cidrIp == '0.0.0.0/0' or # # at-least-one ipv6Ranges contains ANY IPv6 # some ipv6Ranges[*].cidrIpv6 == '::/0' # # and ipProtocol is not udp # ipProtocol != 'udp' ] ]
Berikut ini adalah aturan lengkapnya.
rule any_ip_ingress_checks { let ports = InputParameter.TcpBlockedPorts[*] let targets = 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 %targets !empty { %targets { ipProtocol != '-1' << result: NON_COMPLIANT check_id: HUB_ID_2334 message: Any IP Protocol is allowed >> when fromPort exists toPort exists { let each_target = this %ports { this < %each_target.fromPort or this > %each_target.toPort << result: NON_COMPLIANT check_id: HUB_ID_2340 message: Blocked TCP port was allowed in range >> } } } } }
Memisahkan koleksi berdasarkan jenis yang terkandung
Saat menggunakan templat konfigurasi infrastruktur sebagai kode (IAc), Anda mungkin menemukan koleksi yang berisi referensi ke entitas lain dalam templat konfigurasi. Berikut ini adalah contoh CloudFormation template yang menjelaskan tugas Amazon Elastic Container Service (AmazonECS) dengan referensi lokalTaskRoleArn
, referensi keTaskArn
, dan referensi string langsung.
Parameters: TaskArn: Type: String Resources: ecsTask: Type: 'AWS::ECS::TaskDefinition' Metadata: SharedExectionRole: allowed Properties: TaskRoleArn: 'arn:aws:....' ExecutionRoleArn: 'arn:aws:...' ecsTask2: Type: 'AWS::ECS::TaskDefinition' Metadata: SharedExectionRole: allowed Properties: TaskRoleArn: 'Fn::GetAtt': - iamRole - Arn ExecutionRoleArn: 'arn:aws:...2' ecsTask3: Type: 'AWS::ECS::TaskDefinition' Metadata: SharedExectionRole: allowed Properties: TaskRoleArn: Ref: TaskArn ExecutionRoleArn: 'arn:aws:...2' iamRole: Type: 'AWS::IAM::Role' Properties: PermissionsBoundary: 'arn:aws:...3'
Pertimbangkan kueri berikut.
let ecs_tasks = Resources.*[ Type == 'AWS::ECS::TaskDefinition' ]
Query ini mengembalikan koleksi nilai-nilai yang berisi semua tiga AWS::ECS::TaskDefinition
sumber daya yang ditampilkan dalam contoh template. Pisahkan ecs_tasks
yang berisi referensi TaskRoleArn
lokal dari yang lain, seperti yang ditunjukkan pada contoh berikut.
let ecs_tasks = Resources.*[ Type == 'AWS::ECS::TaskDefinition' ] let ecs_tasks_role_direct_strings = %ecs_tasks[ Properties.TaskRoleArn is_string ] let ecs_tasks_param_reference = %ecs_tasks[ Properties.TaskRoleArn.'Ref' exists ] rule task_role_from_parameter_or_string { %ecs_tasks_role_direct_strings !empty or %ecs_tasks_param_reference !empty } rule disallow_non_local_references { # Known issue for rule access: Custom message must start on the same line not task_role_from_parameter_or_string << result: NON_COMPLIANT message: Task roles are not local to stack definition >> }