Lezárt AI-szerver egy próbagépen: kimenő forgalom, felügyelet, HTTPS

· minusat · 8 perc olvasás

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 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:

  1. 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.
  2. 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:

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:

  1. 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.
  2. 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.
  3. Böngésző: Firefoxban „Connection secure”, figyelmeztetés nélkül.
  4. 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:

Amit még nem próbáltam ki

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?

  1. 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.
  2. 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.
  3. 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ő.
  4. 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.