NET_ADMIN este capabilitatea Linux care permite unui proces să facă administrare de rețea, iar într-un container face exact asta în spațiul de nume de rețea în care rulează procesul. Nu este o licență pentru a deschide un socket și nu este un substitut pentru un dispozitiv sau o rută. Întrebarea interesantă nu este niciodată dacă capabilitatea a fost acordată, ci care spațiu de nume primește modificarea.

Ce acoperă capabilitatea

În spațiul său de nume, un proces care deține NET_ADMIN poate ridica și coborî interfețe, poate atribui adrese, poate adăuga și elimina rute și poate scrie reguli de firewall, adică grupul de operații pe care capabilities(7) le adună sub această capabilitate. Poate ajusta și setări la nivel de interfață care controlează modul în care nucleul pune traficul în coadă și îl filtrează. Un client VPN are de obicei nevoie de câteva dintre ele la pornire și de nimic altceva după aceea. De aceea majoritatea imaginilor cer mai multă acoperire decât folosesc.

Operațiile din afara acelui spațiu de nume nu sunt acoperite, iar privilegiile adiacente sunt separate. Citirea și scrierea pachetelor brute aparțin de NET_RAW, iar crearea unui dispozitiv tun necesită atât capabilitatea, cât și acces la nodul dispozitivului. Un proces fără dispozitiv primește o eroare de permisiune care pare o problemă de rețea și nu este.

Spațiul de nume decide raza daunelor

Un container pe propria rețea de punte ajunge la propriul loopback, la propriile interfețe și la propria tabelă de rutare. Acordarea NET_ADMIN acolo permite clientului VPN să construiască un tunel fără a atinge deciziile de rutare ale gazdei, ceea ce este aranjamentul pe care îl presupun majoritatea imaginilor de container.

Comutați acel container pe rețeaua gazdei și aceeași acordare editează acum rutele gazdei și firewall-ul gazdei, pentru că nu a mai rămas niciun spațiu de nume separat. Clientul care trebuia să administreze un singur tunel poate atunci redirecționa traficul întregii mașini, iar o greșeală în scriptul său de demontare lasă pe gazdă reguli pe care nimeni nu le-a scris. Nimic din capabilitate în sine nu s-a schimbat; contextul s-a schimbat. De aceea același fișier compose se comportă diferit pe două gazde.

Trei condiții prealabile fuzionate într-una

Eșecurile la pornire în containerele VPN implică de obicei unul dintre trei lucruri separate: capabilitatea, dispozitivul de tunel și suportul nucleului pentru protocol. Acordarea tuturor trei deodată funcționează și, de asemenea, face următorul eșec imposibil de atribuit. Un fragment compose minim acordă capabilitatea și transmite explicit nodul dispozitivului:

services:
  vpn:
    image: example/vpn-client:latest
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1

Citiți documentația imaginii în sine pentru ce sysctl are nevoie, deoarece un sysctl într-un container poate necesita un privilegiu suplimentar sau o setare la nivel de gazdă. Acolo unde imaginea acceptă doar modul spațiu de utilizator, nu este nevoie deloc de un dispozitiv, iar lista de capabilități se micșorează în consecință.

Acordați operația îngustă, nu modul

Modul privilegiat predă fiecare capabilitate și lărgește în același timp accesul la dispozitive, așa că ascunde care dintre cele trei condiții prealabile lipsea. De asemenea, face modurile de eșec ale containerului imposibil de distins de modificările gazdei. Preferați o listă explicită chiar și atunci când este mai lungă de scris.

Implementările rootless și în spații de nume de utilizator schimbă ce înseamnă o capabilitate în practică, deoarece procesul o poate deține doar în interiorul unui spațiu de nume mapat. Copierea unei liste de capabilități dintr-un exemplu rootful într-un mediu rootless și așteptarea aceleiași acoperiri este o modalitate obișnuită de a pierde o după-amiază.

Secretele aparțin unei decizii separate. Deținerea NET_ADMIN nu spune nimic despre ce identitate de partener prezintă clientul, ce acreditări încarcă sau cum sunt rotite. Administrarea rețelei nu oferă încredere.

Verificați modificarea și notați de ce există acordarea

Confirmați ce a primit de fapt containerul înainte de a depana tunelul. Inspecția de mai jos raportează capabilitățile adăugate și modul de rețea, apoi arată cum arată lista de interfețe din interior:

docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.NetworkMode}} {{.HostConfig.Privileged}}' vpn
docker exec vpn sh -c 'grep CapEff /proc/self/status'
docker exec vpn ip -br addr
docker exec vpn ip route

Dacă capabilitatea este prezentă și lista de interfețe este încă goală, clientul a eșuat înainte de a putea crea ceva, iar jurnalele containerului sunt următorul loc de privit. Dacă tunelul există dar traficul nu curge, tabela de rutare din interiorul containerului răspunde la următoarea întrebare.

Testați și calea de demontare, deoarece un client care scrie reguli de firewall le poate lăsa în urmă. Opriți containerul și verificați din nou setul de reguli al gazdei dacă se folosește rețeaua gazdei. O acordare necesară doar două secunde la pornire nu ar trebui descrisă în fișierul compose ca o cerință permanentă fără o notă care să explice acest lucru.

Scrieți motivul lângă fiecare capabilitate: ce operație avea nevoie de ea și ce eșuează fără ea. Acea notă supraviețuiește actualizărilor de imagine și este diferența dintre o listă de privilegii revizuită și una care a crescut prin copiere și lipire.

Articole similare