Lezárt AI-szerver egy próbagépen: kimenő forgalom, felügyelet, HTTPS
Miről szól ez a bejegyzés?
Az eddigi bejegyzések arról szóltak, mennyire tud magyarul egy helyi modell. Egy cégnél azonban ez csak az egyik kérdés. A másik az, amit egy rendszergazda kérdez meg először: tényleg nem megy ki semmi a gépről? És ki veszi észre, ha egy hétvégén leáll?
Erre a brAInharborhoz egy Ansible-csomagot készítek, amely egy üres Debian vagy Ubuntu gépből egy paranccsal kész, megerősített AI-szervert csinál, és utána karban is tartja. Ez lesz a projekt fizetős része; az ára és a kiadás ideje még nem dőlt el. Ebben a bejegyzésben nem a csomagot mutatom be, hanem azt, hogyan próbáltam ki három részét, és mi derült ki közben:
- a kimenő forgalom lezárását, és azt az ellenőrzést, amely ezt bizonyítja;
- a felügyeletet, amely e-mailt küld, ha valami elromlik;
- a HTTPS-tanúsítványt: belső CA-val és Let's Encrypttel is.
A próbagép
Egy friss Debian 13 virtuális gép VirtualBoxban, hídra kötött hálózattal, mintha egy irodai szerver lenne a helyi hálózaton. Videokártya nincs benne, így a GPU-s ágat ez a teszt nem fedi le (erről a végén).
A fejlesztés közben mindent először egy konténerben próbáltam ki, internet nélkül. Ott a levelezést egy kis helyi SMTP-szerver, a Let's Encryptet pedig a Pebble nevű tesztszervere helyettesítette. A konténerben azonban minden kicsit túl tiszta: nincs valódi DNS, nincs valódi levelezőszolgáltató, és nincs egy otthoni router sem. Ezért kellett a virtuális gép.
1. Semmi nem megy ki a gépről
Az ígéret pontosan ez: a brAInharbor szolgáltatásai nem küldenek ki adatot, és ezt bárki ellenőrizheti. Nem azt ígérem, hogy a gép soha semmit nem tölt le (a rendszerfrissítések és a modellek letöltése kell), hanem azt, hogy a kérdések, a válaszok és a feltöltött dokumentumok nem jutnak ki.
A lezárás két rétegből áll:
- Docker-hálózatok. Az Ollama és az Open WebUI egy belső (
internal) Docker-hálózaton van, ahonnan nincs útvonal kifelé, és internetes nevet sem tudnak feloldani. A Caddy webszerver egy második hálózaton fogadja a böngészők kéréseit. Az Ollama csak modell letöltésekor kapcsolódik egy harmadik hálózathoz, amelyről csak HTTPS-en érheti el a modelltárat, utána rögtön lekapcsolódik. - Tűzfal. Egy saját nftables-tábla eldob minden kapcsolatot, amelyet a belső vagy a Caddy-féle hálózatból kifelé kezdeményeznek. Ez akkor is véd, ha valaki kézzel átállítaná a Docker-hálózatot.
Egy dolgot itt tanultam meg, ami bárkinek hasznos lehet, aki Docker mellé ír tűzfalszabályt: a Debian nftables.service szolgáltatását nem szabad Docker mellett használni. Leállításkor minden szabályt töröl, a Dockerét is, és utána a konténerek hálózata nem működik. A saját táblát ezért egy külön systemd-szolgáltatás tölti be, és csak hozzáad tiltásokat, a Docker szabályaihoz nem nyúl.
Az ellenőrzés
Ezt nem kell vakon elhinni. Egy ellenőrző parancs mindhárom szolgáltatás hálózatából megpróbál kijutni: TCP-kapcsolatot nyit ismert internetes címekre IPv4-en és IPv6-on, a 443-as és az 53-as porton, és neveket próbál feloldani. Előtte megnézi, hogy maga a gép eléri-e az internetet; ha nem, a többi próba nem bizonyít semmit, és ezt a jelentés ki is írja.
A próbák a gép saját parancsaival futnak a konténer hálózatában, így nem számít, mi van a konténer képében. A végeredmény egy magyar nyelvű jelentés, amelyet oda lehet adni a vezetőségnek vagy egy ügyfélnek. A parancs naponta magától is lefut, és azt is jelzi, ha az előző ellenőrzés óta valami más is próbált kijutni.
A próbagépen minden futás így végződött:
EREDMÉNY: minden ellenőrzés rendben (0 figyelmeztetés).
2. Felügyelet: szóljon, ha baj van
Egy kis cégnél nincs, aki egész nap figyelné a szervert. A felügyelet ezért ötpercenként magától ellenőrzi:
- futnak-e a szolgáltatások, és válaszol-e a webes felület HTTPS-en;
- megvannak-e a beállított modellek;
- friss-e az éjszakai mentés;
- be vannak-e töltve a lezáró szabályok, és rendben volt-e a napi ellenőrzés;
- érvényes-e a tanúsítvány, és van-e elég hely a lemezen.
Ha egy hiba két egymást követő körben is fennáll, e-mailt küld, amikor megszűnik, azt is, naponta emlékeztet, ha nem szűnik meg, hétfő reggel pedig összefoglalót küld. A levelekben csak állapot van (mi a hiba, mióta, mi a teendő), felhasználói adat nincs. Mentés, visszaállítás, modell-letöltés és frissítés alatt nem riaszt, mert ilyenkor egy-egy szolgáltatás szándékosan áll le.
A hibapróba
A legegyszerűbb próba: leállítottam a Caddy-t, és kézzel háromszor lefuttattam az ellenőrzést. A naplóban ez állt:
Hiba (1/2): Caddy: leállt (kilépési kód: 0).
PROBLÉMA: Caddy: leállt (kilépési kód: 0).
Megoldódott: Caddy: fut.
Az első hibás kör még csak feljegyzés, a második a riasztás, és a Caddy újraindítása után jön a „Megoldódott”. A két kör azért kell, hogy egy pillanatnyi újraindítás miatt ne jöjjön levél éjjel.
Utána valódi levelezéssel is kipróbáltam, egy Gmail-fiókon keresztül, alkalmazásjelszóval. Mind a négy levél megjött: a próbalevél, a PROBLÉMA, a Megoldódott és a heti összefoglaló. A próba után az alkalmazásjelszót visszavontam.
Egy apróság a konténeres tesztből: a Debian 13 Pythonja (3.13) szigorúbban ellenőrzi a tanúsítványokat. Egy saját CA-t, amelyből hiányzik a keyUsage bővítmény, elutasít. Aki házi levelezőszerverrel tesztel, ebbe könnyen belefut.
3. HTTPS: belső CA vagy Let's Encrypt
Egy belső hálózaton futó szervernek is kell HTTPS. A brAInharbor alapból a Caddy saját CA-ját használja. Ez beállítás nélkül működik, de a böngészők csak akkor fogadják el figyelmeztetés nélkül, ha a CA gyökértanúsítványa fent van a gépeken. Az Ansible ezért lehozza a gyökértanúsítványt a vezérlőgépre, kiírja az ujjlenyomatát, a leírás pedig megmutatja, hogyan lehet kiosztani (Windows csoportházirenddel, Firefoxban, macOS-en, Linuxon és telefonokon).
Ha a cégnek van saját domainje, egyszerűbb a Let's Encrypt: ezt a tanúsítványt minden böngésző magától elfogadja. Csakhogy a Let's Encrypt szokásos módja (HTTP-01) azt kéri, hogy a szerver az internetről is elérhető legyen a 80-as porton. Egy belső AI-szervernél ezt nem szeretném.
Miért nem a Caddy kéri a tanúsítványt?
A megoldás a DNS-alapú ellenőrzés (DNS-01): a Let's Encrypt egy ideiglenes TXT rekordot kér a domain DNS-ében, és ezt egy API-token segítségével automatikusan ki lehet tenni. A szervernek így nem kell kívülről elérhetőnek lennie.
Kézenfekvő lett volna egy DNS-bővítményes Caddy, mert a Caddy maga is tud tanúsítványt kérni. Csakhogy a Caddy az a szolgáltatás, amely minden kérést és választ titkosítatlanul lát. Ha ő kéri a tanúsítványt, kifelé kell kapcsolódnia, és nála kell lennie a DNS-tokennek is. Ez éppen azt a lezárást lyukasztaná ki, amelyről az első rész szól.
Ezért a tanúsítványt a gazdagépen futó certbot kéri (a Debian saját csomagjából, Cloudflare-bővítménnyel), és egy kis szkript adja át a kész fájlokat a Caddy-nek, amely csak olvasni tudja őket. A token csak a gazdagépen van, csak a root olvashatja, és a mentésekbe sem kerül bele. A kimenőforgalom-jelentés feltünteti, hogy igényléskor és megújításkor maga a gép beszél a Let's Encrypttel és a Cloudflare-rel. A Cloudflare-ben a token csak egy zóna DNS-ét kezelheti, és a cég IP-címére is korlátozható.
A próba
A brainharbor.net egyik aldomainjére kértem tanúsítványt a próbagépről, amely közben a belső hálózaton maradt:
- Igénylés: sikerült, a tanúsítvány 90 napig érvényes, a Caddy azonnal azt használta. A kimenőforgalom-ellenőrzés és a felügyelet minden pontja rendben maradt.
- Megújítás: először próbafuttatással, aztán kikényszerített valódi megújítással. Az új tanúsítványt a certbot átadta, a Caddy újratöltötte, a naplóban ott a sor róla.
- Böngésző: Firefoxban „Connection secure”, figyelmeztetés nélkül.
- Visszaállítás: a próba után visszaállítottam a gépet belső CA-ra. Az Ansible törölte a tokent és a tanúsítvány másolatát, a certbot-tanúsítványt és a Cloudflare-tokent kézzel töröltem.
Ha a megújítás valamiért elakadna, a felügyelet akkor szól, amikor a tanúsítvány élettartamának utolsó negyedébe ér: 90 napos tanúsítványnál nagyjából 22 nappal a lejárat előtt. A certbot magától 30 nappal a lejárat előtt újít, így marad egy hét a javításra.
Közben előjött néhány apróság, amelyet beírtam a dokumentációba:
- A Debian
python3-cloudflarecsomagja minden futásnál egy nagy, felkiáltójelekkel keretezett figyelmeztetést ír ki egy jövőbeli verzióváltásról. Ártalmatlan, de elsőre ijesztő. - A
certbot renewterminál nélkül (vagyis időzítőből vagy szkriptből) véletlenszerűen akár nyolc percet is vár indulás előtt. Teszteléskor a--no-random-sleep-on-renewkapcsoló segít. - Az
ansible-vault encrypt_stringjelszókérdése bekerül a fájlba, ha a kimenetet>>-vel egyenesen oda irányítjuk. Jobb kimásolni és beilleszteni. - A
journalctlsudo nélkül csak akkor mutatja a rendszer naplóját, ha a felhasználó benne van azadmcsoportban. Friss Debianon nincs benne, ezért a leírásban most mindenholsudo journalctláll.
Amit még nem próbáltam ki
- GPU. A próbagépen nincs videokártya, így az NVIDIA-s ág (Container Toolkit, GPU-ellenőrzés a felügyeletben) még egyik tesztben sem futott valódi kártyával. Ez a következő próba.
- Más DNS-szolgáltató. A Let's Encrypt DNS-en át egyelőre csak Cloudflare-rel működik. A Debianban több más szolgáltató bővítménye is megvan, ezek jöhetnek később.
A frissítést és a visszaállást (frissítés előtti mentés, hiba esetén automatikus visszaállás az előző verzióra) egy nappal korábban ugyanezen a gépen próbáltam ki, az is rendben volt.
Mit tanultam?
- A lezárást mérni kell, nem elhinni. A tűzfalszabály megírása a könnyebb fele. Az a nehezebb, hogy kiderüljön, tényleg nem jut-e ki semmi, és hogy ez egy nem rendszergazda számára is érthető jelentésben látszódjon.
- Egy kényelmes funkció könnyen kilyukasztja a lezárást. A DNS-bővítményes Caddy egyszerűbb lett volna, de pont annak a szolgáltatásnak adott volna kijáratot, amely minden adatot lát.
- A konténeres teszt nem pótolja a valódi gépet. A Pebble és a helyi SMTP-szerver sokat segített, de a figyelmeztetések, a csoportjogok és a várakozási idők csak a valódi Debianon és a valódi szolgáltatásokkal jöttek elő.
- A visszaállítás is a teszt része. Mindegyik próbát úgy terveztem, hogy a végén a gép visszakerüljön az alapállapotba, és ezt is ellenőriztem. Egy ügyfélnél is így kell majd: ami bekapcsolható, az nyom nélkül ki is kapcsolható legyen.