Săriți la conținut

Blog ·

Un canar care rulează față de servere reale

Un simulator nu poate dovedi autentificarea. Două defecte de monitorizare pe care doar servere reale le puteau arăta și ce anume numără o pagină de stare.

De Provibr

O verificare de monitorizare care se autentifică la o bază de date este o bucată mică de cod de protocol cu o însușire neplăcută: aproape orice fel de a o testa testează altceva decât trebuie.

Testul evident este un server simulat care vorbește protocolul. Funcționează și dovedește destule: încadrarea, expirările, ce se întâmplă dacă legătura cade în mijlocul unei strângeri de mână, dacă un refuz produce codul de eroare potrivit. Ceea ce nu poate dovedi este autentificarea, și asta dintr-un motiv structural. Un simulator care recalculează propriul nostru amestec folosește propria noastră implementare la ambele capete ale firului. Dacă schimbi între ele două argumente într-un rezumat, ambele părți le schimbă, iar testul rămâne verde pe cod pe care niciun server real nu l-ar accepta.

Golul acela era notat ca punct deschis la capătul rundei anterioare de monitorizare: SCRAM fusese măsurat doar față de o implementare scrisă tot de noi. Un canar îl închide.

Ce este aici un canar

Un canar este un set mic de probe care rulează față de lucrul real în loc de un model al lui. Containere de unică folosință pe propriul nostru server, legate la localhost, cu un PostgreSQL 17, un Redis 7, un MySQL 8.4 și un MariaDB 11 adevărate înăuntru. Adaptoarele sunt apelate direct și nu prin plicul de comenzi, fiindcă subiectul este firul și nu instalația din jurul lui. Unsprezece probe, toate sărite în mod implicit, iar fiecare refuză să ruleze cât timp adresa ei nu este într-adevăr definită.

Al doilea zăvor nu este de prisos alături de primul. Dacă cineva pornește întreaga colecție ridicând marcajul de sărire, o probă s-ar conecta altfel în tăcere la 127.0.0.1:0 și ar produce un eșec care se citește ca un defect al codului nostru în loc de o variabilă de mediu lipsă.

Ce acoperă cele unsprezece:

ServerCe se dovedește
PostgreSQL 17SCRAM cu parola bună și cu una greșită, md5, trust, TLS cerut față de un server cu TLS stins, verificare completă a certificatului față de o ancoră străină
Redis 7Autentificare cu parolă și un utilizator cu ACL, plus lista permisă a lui INFO
MySQL 8.4caching_sha2 cu memorie rece și cu memorie caldă, plus o parolă greșită
MariaDB 11.8Aceeași strângere de mână față de o altă implementare a ei

Deosebirea dintre memorie rece și caldă contează fiindcă sunt două căi de cod diferite. MySQL folosește la caching_sha2_password un amestec ieftin de îndată ce serverul a mai văzut contul, și cere autentificare completă prima dată după o repornire. Doar un server real repornește.

Două defecte pe care niciun simulator nu le putea arăta

PostgreSQL, parolă greșită. Cu parola greșită, PostgreSQL nu răspunde la primul răspuns SASL cu o continuare. Răspunde cu o eroare care poartă SQLSTATE 28P01, parolă nevalidă. Adaptorul avea acolo o destructurare goală, care transforma tot ce nu era o continuare într-o eroare de protocol: acest serviciu nu a răspuns ca un server PostgreSQL. SQLSTATE-ul era aruncat. O clientă ale cărei acreditări de monitorizare expiraseră citea că baza ei de date este stricată.

Un simulator nu trece niciodată pe ramura aceea, fiindcă un simulator care testează calea fericită trimite continuarea. Ambele locuri în care poate sosi acum o eroare, după primul pas și după al doilea, o păstrează drept eșec de autentificare.

Redis, un câmp gol. Adaptorul citește exact două câmpuri din INFO: versiunea și rolul. Cerea secțiunea server, iar role nu se află în acea secțiune. Se află în replication. Câmpul era gol de la runda care l-a introdus, și nimic nu spunea asta, fiindcă simulatorul răspundea cu un bloc INFO scris chiar de noi, în care role stătea binevoitor exact unde îl așteptam.

Remedierea sunt două apeluri INFO separate și nu unul singur care să numească ambele secțiuni, fiindcă Redis acceptă mai multe secțiuni într-un INFO abia de la versiunea 7.0. Pe 6.x aceea este o eroare de sintaxă, iar fiindcă un INFO eșuat nu duce dinadins verificarea pe roșu, câmpul ar fi rămas pur și simplu iar gol pe serverele mai vechi. O degradare tăcută care înlocuiește o degradare tăcută.

Ambele defecte au aceeași formă. Niciunul nu a produs o eroare. Unul a produs eroarea greșită și celălalt nu a produs nimic, iar amândouă erau invizibile pentru o colecție de teste care era verde.

MySQL și biblioteca ce nu a fost adăugată

Aceeași rundă a adăugat MySQL și MariaDB drept tip de verificare. În spate nu stă nicio bibliotecă de client, iar asta este o repetare voită a unei decizii anterioare și nu o decizie nouă.

Pentru PostgreSQL alegerea fusese să nu scriem un SCRAM propriu, fiindcă SCRAM are un pas în care serverul semnează ceva ce clientul ar trebui să verifice, iar o implementare care sare peste acel pas este verde față de un server real și la fel de verde față de unul contrafăcut. Aceasta este scurtătura primejdioasă: nu eșuează niciodată zgomotos.

MySQL nu are un asemenea pas. Autentificarea nativă cu parolă este un lanț de rezumate SHA1, iar calea rapidă a lui caching_sha2_password este un lanț de rezumate SHA256, și în niciuna dintre ele serverul nu semnează ceva a cărui verificare am putea să o omitem. Rămân încadrarea și două rezumate, iar ambele biblioteci de rezumat erau deja în arbore. Nici un constructor de interogări nu era necesar: există o interogare, SELECT 1, stă drept constantă, iar răspunsul ei așteptat la fel.

Ultimul lucru se citește ca o funcție lipsă. Deasupra unei interogări fixe, o așteptare configurabilă este un buton la care orice poziție în afara celei implicite face verificarea să eșueze, iar un asemenea buton îl învață pe administrator că butoanele nu înseamnă nimic. Locul lui va fi în ziua în care va exista o a doua interogare fixă, și nu cu o zi mai devreme. Nici versiunea nu este o a doua interogare: MySQL o trimite fără să fie cerută în pachetul de strângere de mână, iar a doua interogare este întotdeauna aceea căreia peste un an îi crește un câmp de text liber.

Două lucruri sunt respinse din capul locului. Capacitatea de client care permite unui server să îi ceară clientului să citească un fișier de pe discul său și să îl trimită nu este solicitată, iar octetul de răspuns corespunzător este o eroare de protocol în loc de o ramură care face ceva. Iar autentificarea caching_sha2 completă se face numai peste TLS: alternativa pe o linie goală este aducerea cheii publice a serverului pe aceeași linie neautentificată, iar cine stă la mijloc răspunde cu a lui. Refuzul numește reparația, și anume pornirea TLS.

O pagină de stare numără ticuri, nu rezultate

Aceeași rundă a pus o pagină de stare publică în fața tuturor acestora, iar socoteala de pe acea pagină cerea o decizie care iese ușor greșit.

Fiecare execuție datorată a unei reguli este un tic. Un tic se poate încheia ca up, degraded sau down, și se mai poate încheia ca source_unavailable (propria noastră sursă de măsurare nu era acolo), configuration_error (regula nu avea sens), unsupported (agentul nu cunoștea acel tip de verificare) sau ratat. Ultimele patru nu spun nimic despre serviciul clientelei.

Dacă numitorul unei disponibilități zilnice ar fi fiecare tic datorat, o defecțiune a noastră ar coborî cifra de pe pagina de stare a clientelei. De aceea numitorul este up plus degraded plus down: ticurile în care cineva chiar s-a uitat la serviciu. Dacă din acelea nu rămâne nimic, răspunsul acelei zile este că nu există răspuns, în mod explicit nici zero, nici o sută la sută. Pe pagina însăși cele patru stări apar ca necunoscut și niciodată ca down.

Degraded se numără pe jumătate. Este o alegere și nu o lege a naturii: un serviciu care răspunde toată ziua, dar stă peste pragul lui de latență, nu este nici căzut, nici în ordine, iar numărarea lui spre oricare dintre cele două părți lasă una dintre ele fără înțeles.

Golurile sunt arătate drept goluri. O regulă creată săptămâna trecută nu are nouăzeci de zile de istoric, iar o bară care umple asta cu valoarea zilei vecine afirmă ceva ce nu a fost măsurat niciodată.

Ce ne-a costat o colecție de teste verde

Fiecare dintre defectele de mai sus stătea sub un test trecut. Colecția de simulatoare nu era greșită; răspundea la o altă întrebare decât cea pe care toată lumea o credea pusă. Valoarea canarului nu este că ar fi o probă mai bună. Este că este o probă în care celălalt capăt al firului a fost scris de cineva care nu împărtășește presupunerile noastre.

Asta valorează șase containere de unică folosință, iar merită scris că sunt de unică folosință: rulează pe propria noastră mașină, sunt îndepărtate când se opresc, iar probele refuză să ruleze dacă adresele lipsesc.

Din munca de monitorizare a Provibr. De ce o sondă găzduită acceptă exact un singur fel de mesaj se află în o sondă care nu poate fi convinsă de nimic, iar de ce un secret de monitorizare călătorește o singură dată se află în un secret care călătorește o dată.

Începeți cu o gazdă și un agent

Creați un cont, instalați agentul cu o singură comandă și conectați primul dumneavoastră host Proxmox. Perioada de probă durează 14 zile și nu cere date de plată.