Docker Konténerbiztonság ML-hez: AI Sebezhetőségvizsgálat Trivy és Clair eszközökkel

2025.10.17.
AI Biztonság Blog

Oké, beszéljünk őszintén. Építettél egy zseniális gépi tanulási modellt. Olyan, mint egy precíziós műszer, egy digitális szike, ami képes előre jelezni a piaci trendeket, felismerni a rákos sejteket, vagy épp tökéletes macskás mémeket generálni. Bármi is legyen a cél, a modelled a koronaékszered. Hónapok, talán évek munkája van benne.

Most pedig fogod ezt a csodát, és becsomagolod egy Docker konténerbe, hogy kitehesd a nagyvilágba. És itt kezdődnek a bajok.

Kapcsolati űrlap

AI Biztonság kérdésed van? Itt elérsz minket:

Gondolj a modelledre úgy, mint egy tökéletesre csiszolt, halálos pengére a legkiválóbb mesterkovács műhelyéből. Most képzeld el, hogy ezt a pengét egy rozsdás, ezeréves, nyitott lakatú faládában akarod szállítani egy veszélyes környéken. Nevetségesen hangzik, igaz? Pedig a legtöbb fejlesztő pontosan ezt csinálja, amikor egy ML modellt egy sebezhetőségekkel teli, ellenőrizetlen Docker konténerbe zár.

Nem a modelled fogják feltörni. Nem a zseniális neurális háló architektúrádban keresnek majd hibát. Ó, nem. Annál sokkal egyszerűbb dolguk van. Megkeresik a legkisebb rést a konténered falán – egy elavult libraryt, egy rossz konfigurációt, egy rég elfelejtett függőséget –, és azon keresztül jutnak be. Onnantól pedig a koronaékszered az övék. Ellophatják, megmérgezhetik a tanító adatait, vagy egyszerűen csak lefoglalják a drága GPU-idődet, hogy kriptót bányásszanak vele.

Üdv a klubban. Ez itt az AI Red Teaming világa, ahol nem a modell eleganciája számít, hanem a futtatókörnyezet kíméletlen valósága. Ma arról fogunk beszélni, hogyan teheted a rozsdás faládádat egy modern páncélszekrénnyé. Két eszközt fogunk a boncasztalra fektetni: a Trivy-t és a Clair-t. Kapcsold be a biztonsági öved, mert mélyre merülünk.

Miért más az ML konténerek biztonsága? A fenyegetések árnyalatai

„De hát egy konténer az csak egy konténer, nem?” – hallom a kérdést. „A web-backendem is konténerben fut, azt is szkenneljük. Mi itt a különbség?”

A különbség a tét. És a támadási felület finom, de annál fontosabb eltérései.

Egy átlagos webalkalmazás konténerének kompromittálása általában adatbázis-hozzáféréshez, ügyféladatok kiszivárgásához vagy a szolgáltatás megbénításához vezet. Ezek is súlyos problémák, ne érts félre. De egy ML-rendszer esetében a játszma sokkal komplexebb.

Gondolj a konténerre nem csak egy sima futtatókörnyezetként, hanem az AI modell élettereként. Mint egy akvárium egy ritka, egzotikus hal számára. Ha a víz (a környezet) szennyezett, a hal (a modell) megbetegszik vagy elpusztul.

  1. Modell-lopás (Model Theft): Ha egy támadó shell hozzáférést szerez a konténeredhez egy sebezhetőségen keresztül, az első dolga lesz megkeresni a modell súlyait tartalmazó fájlokat (.pt, .h5, .pb, stb.). Ezek a te szellemi tulajdonod. Egy jól betanított modell vagyonokat érhet. A támadó egyszerűen lemásolja és elsétál vele.
  2. Adatmérgezés (Data Poisoning): Sok ML rendszer folyamatosan újra tanul, vagy hozzáfér a tanító adatkészlethez. Ha a támadó bejut a konténerbe, és módosítani tudja a modell által olvasott adatforrásokat (pl. egy csatolt volume-on), akkor szándékosan „elronthatja” a modelledet. Finoman, észrevétlenül adagolva a hamis adatokat, amíg a modelled el nem kezd katasztrofális hülyeségeket jósolni.
  3. Inferencia-eltérítés (Inference Hijacking): A támadó nem lopja el a modellt, csak manipulálja a működését. Bejut a konténerbe, és egy man-in-the-middle támadással a modell bemenete és kimenete közé ékelődik. Képzeld el, hogy a hitelképességet vizsgáló modelled minden „rossz” ügyfélre „jót” fog mondani, mert a támadó átírja a kéréseket vagy a válaszokat.
  4. Erőforrás-eltulajdonítás (Resource Hijacking): Ez a legprimitívebb, de leggyakoribb támadás. Az ML-hez drága, erős hardver kell, főleg GPU-k. Egy támadónak ez ingyen számítási kapacitás. Bejut, telepít egy XMRig-et vagy más kriptobányász szoftvert, és a te kontódra bányássza a Monerót. Te csak annyit látsz, hogy a felhőszámlád az egekbe szökött, a modelled pedig belassult.

Az AI modelled csak annyira biztonságos, amennyire a leggyengébb láncszem a futtatókörnyezetében. És ez a láncszem szinte mindig a Docker image egy elfeledett, sebezhető library-ja.

Egy ML konténer anatómiája: A bűn rétegei

Mielőtt szkennelni kezdenénk, értsük meg, mit is szkennelünk. Egy Docker image nem egy monolitikus fekete doboz. Inkább olyan, mint egy geológiai rétegsor, ahol minden réteg a saját veszélyeit rejti.

Képzelj el egy tortát. Egy többrétegű tortát.

  1. A piskótaalap: A Base Image. Ez a FROM ubuntu:22.04 vagy FROM python:3.10-slim-bullseye sor a Dockerfile-od elején. Ez a torta alapja. Hoztad magaddal az operációs rendszer alapvető csomagjait, a C library-ket, a shellt, mindent. Ha az alap, amire építkezel, már eleve tele van lyukakkal (pl. egy régi OpenSSL, egy sebezhető glibc), akkor a tortád már az elején süllyedni kezd.
  2. Az első krémréteg: Rendszerszintű függőségek. Ezek a RUN apt-get update && apt-get install -y ... parancsok. Telepítesz egy libgomp1-et, egy build-essential-t, egy curl-t. Minden egyes install egy újabb potenciális sebezhetőségi pont. Tényleg szükséged van a curl-re a production konténeredben? Tényleg?
  3. A gyümölcsréteg: Nyelvi függőségek. A híres-hírhedt RUN pip install -r requirements.txt. A requirements.txt fájlod egy aknamező. Egy elavult numpy, egy régi Pillow (ami tele volt RCE sebezhetőségekkel), egy obskúrus adatelemző library, amit valaki egyszer betett, de már senki sem tudja, mire való. Minden egyes sor egy potenciális behatolási pont.
  4. A második krémréteg: Az ML Framework. tensorflow, pytorch, scikit-learn. Ezek gigantikus, komplex szoftverek, saját, több százas függőségi fával. Egy-egy framework frissítés nem csak új feature-öket, de biztonsági javításokat is hoz. Használod a legfrissebbet? Vagy megragadtál egy két évvel ezelőtti verziónál, mert „az működik”?
  5. A díszítés: A te kódod és a modelled. Végül a tetején ott van a saját alkalmazáskódod és a betöltött modell. Ironikus módon a legtöbb fejlesztő erre koncentrál, miközben a veszélyek 99%-a az alatta lévő rétegekben lapul.

Egy sebezhetőség-szkenner pontosan ezt a rétegződést tárja fel. Végigmegy minden rétegen, és összeveti a talált szoftververziókat (pl. openssl 1.1.1f-1ubuntu2) az ismert sebezhetőségek globális adatbázisaival (CVE-kkel).

Egy ML Docker Image Anatómiai Rétegei 5. réteg: Alkalmazáskód és Modell (`app.py`, `model.pt`) 4. réteg: ML Framework (`tensorflow`, `pytorch`) 3. réteg: Nyelvi Függőségek (`requirements.txt` -> numpy, pandas, Pillow) 2. réteg: Rendszerszintű Függőségek (`apt-get install libgomp1 curl`) 1. réteg: Base Image (`FROM ubuntu:22.04`) Minden réteg újabb potenciális támadási felületet ad hozzá.

A kihívók a ringben: Trivy vs. Clair

Most, hogy értjük a problémát, nézzük a megoldásokat. A piacon rengeteg szkenner van, de kettő emelkedik ki a nyílt forráskódú világban: a Trivy és a Clair. Nem ellenségek, inkább két különböző filozófiát képviselő harcosok.

Clair: A központosított őrszem

A Clair a CoreOS (most már Red Hat) projektje, és a nagyvállalati, központosított megközelítést képviseli. A Clair nem egy egyszerű parancssori eszköz. Ez egy szerver-kliens architektúra.

Hogyan működik? Feltelepítesz egy Clair szervert, ami egy PostgreSQL adatbázisra támaszkodik. Ez a szerver folyamatosan szinkronizálja magát a különböző sebezhetőségi adatbázisokkal (Debian Security Bug Tracker, Red Hat Security Data, stb.). Amikor te elemezni akarsz egy image-et, egy kliens (pl. a clair-scanner) segítségével elküldöd az image rétegeinek listáját a Clair szervernek. A szerver elvégzi az elemzést a saját, naprakész adatbázisa alapján, és visszaküldi az eredményt.

Gondolj a Clair-re úgy, mint egy központi hírszerző ügynökségre. Az ügynökök (a kliensek) a terepről jelentik a látottakat (az image-ek tartalmát), a központ pedig elemzi az információt a globális adatbázisa alapján.

Clair Architektúra: A Központosított Modell Clair Szerver API Endpoint Vulnerability DB PostgreSQL CI/CD Pipeline Fejlesztői Gép Scan kérés Scan kérés Upstream CVE Adatbázisok Szinkronizáció

Trivy: A svájci bicska

A Trivy, amit az Aqua Security fejleszt, a másik véglet. A filozófiája az egyszerűség és a sebesség. A Trivy egyetlen, statikusan linkelt bináris, aminek nincsenek külső függőségei. Letöltöd, futtatod, és kész.

Hogyan működik? Amikor először futtatod, a Trivy letölti a saját sebezhetőségi adatbázisát egy bolt-db formátumban a ~/.cache/trivy könyvtárba. Innentől kezdve minden elemzés lokálisan történik. Nincs szükség szerverre, adatbázisra, semmire. Ez teszi hihetetlenül könnyen integrálhatóvá CI/CD pipeline-okba vagy használhatóvá egy fejlesztő laptopján.

A Trivy olyan, mint egy zsebedben hordott, mindenre jó svájci bicska. Nem kell hozzá központi parancsnokság, egyszerűen előkapod és használod, amikor szükséged van rá.

Trivy Architektúra: A Hordozható Modell CI/CD Pipeline / Fejlesztői Gép Trivy Bináris Docker Image 1. Szkennelés Lokális Trivy DB Upstream CVE Adatbázisok 2. Adatbázis szinkronizáció (ha szükséges)

Fej-fej mellett: Összehasonlító táblázat

Nézzük meg a két eszközt egy praktikus táblázatban, hogy könnyebb legyen a döntés.

Jellemző Trivy Clair
Architektúra Önálló bináris, szerver nélküli Szerver-kliens architektúra
Telepítés, beállítás Rendkívül egyszerű (egy bináris letöltése) Komplexebb (szerver, adatbázis, kliens telepítése és konfigurálása)
Használat trivy image [IMAGENAME] clair-scanner [IMAGENAME] (feltételezve a futó szervert)
Szkennelési képességek OS csomagok, nyelvi függőségek (pip, npm, stb.), IaC fájlok (Dockerfile, Terraform), titkosított adatok keresése OS csomagok. Más típusú szkennelésekhez kiegészítő eszközök szükségesek.
Sebesség Nagyon gyors, mivel minden lokálisan történik. Lassabb lehet a hálózati kommunikáció miatt, de az eredményeket gyorsítótárazza.
Integráció Kiváló CI/CD-be (pl. GitLab CI, GitHub Actions), egyszerű parancssori használat. API-vezérelt, jól integrálható nagyobb, központosított rendszerekbe, registry-kbe (pl. Quay).
Ideális felhasználási terület Fejlesztői munkaállomások, egyszerű CI/CD pipeline-ok, gyors, ad-hoc ellenőrzések. Nagyvállalati környezetek, ahol központi szabályozásra és auditálásra van szükség, szoros registry integráció.

Gyakorlat teszi a mestert: Mocskoljuk be a kezünket!

Elég a száraz elméletből. Építsünk egy szándékosan sebezhető ML Docker image-et, és nézzük meg, mit találnak a szkennereink.

1. Lépés: A „bűnös” Dockerfile elkészítése

Készítünk egy egyszerű Flask alkalmazást, ami egy (ál)modellt szolgál ki. A trükk az, hogy szándékosan régi, sebezhető komponenseket használunk.

Hozzunk létre egy app.py fájlt:


from flask import Flask
import numpy as np

app = Flask(__name__)

@app.route('/predict')
def predict():
    # Szimulál egy egyszerű modell jóslatot
    prediction = np.random.rand(1).tolist()
    return {"prediction": prediction}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Most a requirements.txt. Itt használunk egy régi numpy verziót:


Flask==2.0.0
numpy==1.19.0

És végül a Dockerfile, a fő bűnös. Régi base image-et és egy elavult rendszercsomagot (libcurl3) használunk:


# 1. LÉPÉS: Régi, nem támogatott base image használata
FROM python:3.8-slim-buster

WORKDIR /app

# 2. LÉPÉS: Elavult rendszer-csomag telepítése
RUN apt-get update && apt-get install -y --no-install-recommends \
    libcurl3-gnutls \ 
    && apt-get clean && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .

# 3. LÉPÉS: Sebezhető Python csomagok telepítése
RUN pip install --no-cache-dir -r requirements.txt

COPY app.py .

CMD ["python", "app.py"]

Építsük meg az image-et:

docker build -t vulnerable-ml-app:latest .

Most van egy időzített bombánk. Lássuk, hogyan hatástalanítjuk.

2. Lépés: Támadás a Trivy-vel

A Trivy telepítése (ha még nincs meg) általában egyetlen parancs. A hivatalos dokumentáció a legjobb forrás. Ha megvan, futtassuk a szkennelést:

trivy image vulnerable-ml-app:latest

A kimenet egy hosszú lista lesz a sebezhetőségekről. Valami ilyesmit fogsz látni (a verziók és CVE-k változhatnak):


vulnerable-ml-app:latest (debian 10.13)
=======================================
Total: 159 (UNKNOWN: 0, LOW: 76, MEDIUM: 52, HIGH: 28, CRITICAL: 3)

┌────────────────┬──────────────────┬──────────┬───────────────────┬──────────────┬──────────────────────────────────────────────────────────┐
│    Library     │  Vulnerability   │ Severity │ Installed Version │ Fixed Version│                           Title                            │
├────────────────┼──────────────────┼──────────┼───────────────────┼──────────────┼──────────────────────────────────────────────────────────┤
│ apt            │ CVE-2020-27350   │ HIGH     │ 1.8.2.3           │              │ apt: integer overflows and underflows...                 │
│                │                  │          │                   │              │                                                          │
├────────────────┼──────────────────┼──────────┼───────────────────┼──────────────┼──────────────────────────────────────────────────────────┤
│ curl           │ CVE-2019-5482    │ CRITICAL │ 7.64.0-4+deb10u2  │ 7.64.0-4+de..│ curl: TFTP receive buffer over-read                        │
│                │                  │          │                   │              │                                                          │
├────────────────┼──────────────────┼──────────┼───────────────────┼──────────────┼──────────────────────────────────────────────────────────┤
│ python3.8      │ CVE-2021-3426    │ HIGH     │ 3.8.12-1~deb10u1  │              │ python: information disclosure in pydoc                    │
│ ...            │ ...              │ ...      │ ...               │ ...          │ ...                                                      │
└────────────────┴──────────────────┴──────────┴───────────────────┴──────────────┴──────────────────────────────────────────────────────────┘

Python Packages (requirements.txt)
==================================
Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0)

┌─────────┬─────────────────┬──────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────┐
│ Library │ Vulnerability   │ Severity │ Installed Version │ Fixed Version │                          Title                          │
├─────────┼─────────────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────┤
│ numpy   │ CVE-2021-33430  │ HIGH     │ 1.19.0            │ 1.22.0        │ numpy: buffer overflow in `numpy.fromstring` function   │
│         │                 │          │                   │               │                                                         │
└─────────┴─────────────────┴──────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────┘

Mit látunk itt?

  • A Trivy külön kezeli az OS csomagokat és a Python csomagokat.
  • Minden sornál látjuk a sebezhető library-t (curl, numpy), a CVE azonosítót (ez a sebezhetőség „rendszáma”), a súlyosságot (Severity), a telepített verziót, és ami a legfontosabb: a Fixed Version-t!
  • Ez nem csak egy hibajelentés, ez egy teendőlista. A Trivy megmondja, hogy a numpy 1.19.0-s verziója sebezhető, és a javítás az 1.22.0-s verzióban érhető el.

Szűrhetünk csak a legsúlyosabb hibákra, ami a CI/CD-ben kulcsfontosságú:

trivy image --severity CRITICAL,HIGH vulnerable-ml-app:latest

3. Lépés: A Clair bevetése

A Clair beállítása bonyolultabb. Egy docker-compose.yml fájllal a legegyszerűbb elindítani a szervert és az adatbázisát. Szükséged lesz a clair-scanner kliensre is.

Egy egyszerűsített docker-compose.yml a Clair v4-hez:


version: '3.7'
services:
  clair:
    image: quay.io/project-clair/clair:v4.7.1
    container_name: clair
    ports:
      - "6060:6060"
    volumes:
      - ./clair_config:/config
    command: ["-conf", "/config/config.yaml"]
  
  postgres:
    image: postgres:14
    container_name: clair_postgres
    environment:
      - POSTGRES_USER=clair
      - POSTGRES_PASSWORD=clair
      - POSTGRES_DB=clair
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Kell egy clair_config/config.yaml is, ami megmondja a Clair-nek, hogyan csatlakozzon az adatbázishoz. Miután elindítottad (docker-compose up -d), és a Clair letöltötte a sebezhetőségi adatbázisát (ez eltarthat egy ideig!), futtathatod a szkennert:


# Először be kell pusholni az image-et egy lokális registry-be, vagy elérhetővé tenni a Clair számára.
# Egyszerűbb, ha a clair-scanner-t a host hálózatán futtatjuk.
clair-scanner --ip $(hostname -i) vulnerable-ml-app:latest

A Clair kimenete hasonló lesz, listázza a sebezhetőségeket, a súlyosságukat és a javított verziókat. A fő különbség a működésben és a beállításban van, nem feltétlenül az eredmény alapvető tartalmában.

4. Lépés: Automatizálás a CI/CD-ben – Ahol a mágia történik

A manuális szkennelés jó dolog, de az igazi ereje ezeknek az eszközöknek a CI/CD pipeline-ba való beépítés. A cél az, hogy egy sebezhető image soha ne juthasson el a production registry-be.

A biztonsági ellenőrzés, ami nem állítja meg a folyamatot, csak egy drága naplózási eszköz.

Nézzünk egy egyszerű GitLab CI példát a Trivy-vel:


stages:
  - build
  - scan

build_image:
  stage: build
  image: docker:20.10.16
  services:
    - docker:20.10.16-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

scan_image:
  stage: scan
  image:
    name: aquasec/trivy:latest
    entrypoint: [""]
  script:
    # Exit kód 1, ha HIGH vagy CRITICAL sebezhetőséget talál, ami megállítja a pipeline-t.
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Mi történik itt?

  1. build_image: A build stage-ben megépítjük a Docker image-et és feltöltjük a GitLab saját registry-jébe.
  2. scan_image: A scan stage-ben a Trivy-t használjuk. A kulcs a --exit-code 1 és --severity HIGH,CRITICAL kapcsolókban rejlik. Ez azt mondja a Trivy-nek, hogy ha akár egyetlen HIGH vagy CRITICAL besorolású sebezhetőséget is talál, akkor lépjen ki 1-es hibakóddal. A GitLab CI (és a legtöbb CI/CD rendszer) az 1-es hibakódot a pipeline sikertelenségeként értelmezi.

Az eredmény? A pipeline pirosra vált. A hibás kód nem jut tovább. A fejlesztő azonnali visszajelzést kap, hogy javítania kell a Dockerfile-t vagy a függőségeket, mielőtt a kódja élesbe mehetne.

A szkennelésen túl: Egy Red Teamer gondolatai

Egy szkenner futtatása az első, elengedhetetlen lépés. De ne dőlj hátra elégedetten. A valóság ennél árnyaltabb.

  • A „Nincs javítás” csapdája: Mi van, ha a szkenner talál egy kritikus sebezhetőséget, de a „Fixed Version” oszlop üres? Ez gyakran előfordul. Ilyenkor jön a képbe a kockázatcsökkentés:
    • Minimalista base image-ek: Tényleg szükséged van egy teljes Debianra? Használj slim verziókat, vagy még jobb, distroless image-eket, amik csak az alkalmazásodat és a futtatásához szükséges minimális library-ket tartalmazzák. Nincs shell, nincs apt, nincs curl. Amit nem teszel a konténerbe, az nem lehet sebezhető.
    • Többlépcsős buildek (Multi-stage builds): Használj egy build-konténert, amiben mindenféle fejlesztői eszköz van (build-essential, stb.), majd másold át a lefordított binárist egy tiszta, minimális distroless image-be. A kész production image-ben ne legyenek felesleges fordítók és eszközök.
  • A kontextus ereje (False Positives): A szkenner csak azt látja, hogy egy sebezhető library jelen van. Azt nem tudja, hogy a te kódod használja-e a sebezhető funkciót. Lehet, hogy egy HIGH sebezhetőség a te esetedben nem kihasználható. Ennek eldöntése már a te felelősséged. De az alapértelmezett hozzáállásod legyen a „bűnös, amíg be nem bizonyosodik az ártatlansága”.
  • Az ellátási lánc (Supply Chain): Honnan jön a base image-ed? A Docker Hub-ról? Megbízol a python:3.10 image karbantartójában? Nagyobb cégeknél bevett gyakorlat, hogy saját, „arany” base image-eket készítenek és tartanak karban. Ezeket a belső csapatok alaposan átvizsgálják, megedzik (hardening), és csak ezeket engedik használni a fejlesztőknek. Ne építs várat ingoványra.

Végszó

A gépi tanulási modelled lehet a legfejlettebb a világon, de ha egy lyukacsos, elhanyagolt Docker konténerben fut, akkor csak egy könnyű célpont. A konténerbiztonság nem egy opcionális extra, nem egy „majd megcsináljuk, ha lesz idő” feladat. Ez az alap. Ez a nulladik lépés, mielőtt egyáltalán a modell bevetéséről beszélnénk.

A Trivy és a Clair nem csodaszerek. Hanem olyanok, mint egy erős fényszóró egy sötét pincében. Megmutatják a repedéseket a falon, a pókhálókat a sarokban, a szörnyeket, amikről nem is tudtad, hogy ott vannak.

Integráld őket a folyamataidba. Tedd a szkennelést a CI/CD pipeline megkerülhetetlen részévé. Ne csak futtasd a riportokat, hanem cselekedj is az eredmények alapján. Frissítsd a függőségeidet, cseréld le a base image-eidet, dobd ki a felesleges csomagokat.

És most tedd fel magadnak a kérdést, őszintén: te mikor néztél bele utoljára igazán mélyen abba a konténerbe, ami a legértékesebb modelledet futtatja?