UCSPI

A Unix/Linux szerverek üzemeltetése wikiből

UCSPI: UNIX Client-Server Program Interface (elvileg úgy kéne ejteni, hogy ooks-pie, úkszpáj; mi maradjunk az ú-cé-es-pé-í-nél :)

Tartalomjegyzék

1 Mire való?

Kliens-szerver architektúrájú programok egymás közötti kommunikációjának néhány konvencióját fekteti le.

Alapötlet: válasszuk szét a programok hálózati részét a nemhálózati résztől. Ezt gyakran meg lehet tenni, mert egy kliensprogram számára igazából csak az a fontos, hogy beszélhessen egy szerverrel, de igazából mindegy neki, hogy ez milyen konkrét átviteltechnikával történik.

Az UCSPI a kliens számára biztosít két fájldeszkriptort: amit az egyikbe ír, azt megkapja a szerver, és amit a szerver mond, azt kiolvashatja a másikból.

Szerveroldalon kb. úgy működik, mint az inetd: figyel egy (csak egy!) porton, és ha kapcsolat jön, elindítja a szerveralkalmazást úgy, hogy a standard inputja és outputja is a kapcsolat socketje legyen.

Előnyök:

  • (Majdnem) független a protokolltól; pl. elvileg transzparens a számára, hogy IPv4 vagy IPv6 van alatta.
  • A hálózati funkciót elválasztja az alkalmazástól, így egy egyszerű shellscript is lehet szerver vagy kliens.
  • Egy csomó információt környezeti változókba rak, így akár scriptekből is könnyen felhasználhatjuk őket.
  • A nem kifejezetten UCSPI-hez írt (szerver)programok jó része is működik így.
  • Azáltal, hogy egy szerver csak egy porton figyel, a szerverek konfigurációja automatikusan szét van választva; futhatnak különböző chrootban stb.

2 Alapvető működés

Egy UCSPI-eszköz parancssora ilyesmi:

[tool] [options] [address] [application]

  • tool: maga az eszköz
  • options: a saját opciói, pl. verbozitás
  • address: szerver esetén itt kell figyelni, kliens esetén ide kell kapcsolódni
  • application: az elindítandó kliens vagy szerver neve és opciói

Ha létrejön a kapcsolat, tool elindítja a kliens- vagy szerveralkalmazást.

Vegyük észre, hogy igazából itt a tool is csak egy olyan program, mint mondjuk a chpst a runitnál: beállít valamilyen kezdeti környezetet, majd exec() hívással ebben a környezetben indít el egy gyermekfolyamatot. Itt a környezet létrehozásához néhány socket megnyitása is hozzá tartozik.

Így amúgy elvileg megfordíthatjuk a kliens-szerver működést: pl. megcsinálhatjuk, hogy az axfrdns-ünk "kapcsolódjon" egy (UCSPI-t támogató) AXFR-kliensre.

Az UCSPI-kliens által indított alkalmazás

  • a 6-os fájlleírón olvashatja a szerver üzeneteit,
  • a 7-es fájlleiróra pedig írhatja azt, amit a szervernek mondani szeretne.
  • Az stdin-t és az stdout-ot a szülőjétől örökli (ha interaktívan futtatjuk, a konzol marad az stdin/stdout).
    • Így nem UCSPI-felhasználásra készült program sajnos nem használható UCSPI-kliensként (mert nem tudják, hogy a 6-os és a 7-es fájlleíró különleges). Workaround (nem próbáltam, de mennie kéne):
#!/bin/zsh
exec $1 $@ >&7 <&6

(Ez egy olyan script, ami a neki átadott programot úgy futtatja, hogy annak az stdin/stdout párosára rámásolja a saját 6-os ill. 7-es fájlleíróját.)

3 Protokollfüggetlenség

Elvileg az UCSPI "nagyjából" protokollfüggetlen.

Egy-egy tool általában egy protokollt támogat; gondoljunk rájuk úgy, mint hálózati kommunikációs backendekre.

Egy protokoll a következő dolgokat határozza meg:

  • address formátumát és jelentését;
  • azt, hogy milyen környezeti változókat kell beállítani.

Így persze lehet olyan kliens- vagy szerveralkalmazást írni, ami protokollfüggő, mert olyan környezeti változót szeretne, ami csak az egyik protokollra jellemző, de nem muszáj.

A toolok a következő környezeti változókat állítják be:

  • PROTO: a protokoll, amit használunk. TCP esetén pl. TCP.
  • PROTOLOCAL*: a kapcsolat helyi végére jellemző adatok, pl:
    • TCPLOCALIP: a kapcsolathoz tartozó helyi IP-cím;
    • TCPLOCALPORT: a kapcsolathoz tartozó helyi port;
    • TCPLOCALHOST: a helyi IP-hez tartozó név (vagy üres);
  • PROTOREMOTE*: a kapcsolat túlvégére jellemző adatok, pl:
    • TCPREMOTEIP: a távoli IP (szervernél a kliens IP-je, kliensnél a szerver IP-je);
    • TCPREMOTEPORT, TCPREMOTEHOST: értelemszerű;
    • TCPREMOTEINFO: távoli usernév (ident/auth szolgáltatás révén), vagy üres.

A szervertoolok általában a kliens IP-jének/nevének függvényében egyéb környezeti változókat is be tudnak állítani.

4 Példa

Minden ilyesmit példákon keresztül a legegyszerűbb megérteni.

4.1 qmail-smtpd

Nézzük pl. a qmail SMTP-daemonját, a qmail-smtpd-t. Ez egy elég egyszerű kis program, ami a konfigurációja jelentős részét környezeti változókból veszi. Saját hálózatkezelés nincs benne; az ucspi-tcp csomag tcpserver programja (ami egy UCSPI-szervertool) nyújtja neki a TCP-porton való figyelést, mint szolgáltatást.

Egy qmail-smtpd-parancssor pl. a következőképpen festhet:

#!/bin/sh
exec 2>&1
exec env - PATH="$PATH" \
        /usr/bin/softlimit -d 10485760 \
        envuidgid qmaild \
        /usr/bin/tcpserver -U -c20 -v -h -p -t3 -x /etc/tcp.smtp.cdb -X 0 smtp \
        /usr/bin/rblsmtpd \
        -rcn-kr.blackholes.us \
        -rblackholes.wirehub.net \
        /usr/sbin/qmail-smtpd

Lássuk, mi minden van itt:

  • env: környezettisztítás; egyedül a PATH változót örökítjük át a gyermekfolyamatainkra.
  • softlimit: a daemontools csomag egyik chpst-szerű segédprogramja, erőforráskorlátokat állít be, majd exec-kel elindít egy gyermekfolyamatot.
  • envuidgid: szintén a daemontoolsból van; ez az UID és a GID környezeti változókat állítja be a megadott user (itt qmaild) user- ill. group ID-jára.
    • Így külön programba szerveződött ki az NSS (Name Service Switch) kezelése; a gyermekfolyamat már numerikus ID-kat kap az UID/GID változókban.
  • tcpserver: egy TCP-porton figyel. A paraméterek jelentése:
    • -U: a port megnyitása után válts az UID változóban megadott user- és a GID változóban megadott group ID-ra.
    • -c20: legfeljebb 20 párhuzamos kapcsolatot fogadj el (legfeljebb ennyi példány futhat a qmail-smtpd-ből).
    • -v: légy szószátyár (mindenféle státusz-üzenetekkel szórakoztat minket).
    • -h: oldd fel a kliens nevét a DNS-ben és állítsd be a TCPREMOTEHOST változót a qmail-smtpd elindítása előtt.
    • -p: paranoid működés: az imént kapott neved oldd fel a DNS-ben, és ha a kapott A rekordok egyike sem egyezik meg a kliens IP-jével, töröld a TCPREMOTEHOST változót.
    • -t3: legfeljebb 3 másodpercig próbálkozz a TCPREMOTEINFO (távoli usernév) megszerzésével.
    • -x /etc/tcp.smtp.cdb: a megadott cdb-fájlban felsorolt szabályok szerint egyes kliensek számára állíts be további környezeti változókat is.
    • -X: akkor is fogadd el a kapcsolatokat, ha a megadott cdb-fájl nem létezik.
    • 0: a 0.0.0.0-ás IP-n (vagyis a gép összes interface-ének összes IP-jén) nyújtsd a szolgáltatást.
    • smtp: az "smtp" nevű szolgáltatás portján figyelj (általában a /etc/services fájlban vannak a szolgáltatásnév-portszám-párosok).
  • rblsmtpd: ez egy minimális smtp-szerver, ami megnézi, hogy a kliens (TCPREMOTEIP) szerepel-e a megadott RBL-ekben, és ha igen, mond neki egy 451-et; ha nem, elindítja a megadott programot, ami itt a qmail-smtpd, és átadja neki a klienst.
  • qmail-smtpd: végül elindul maga a qmail-smtpd és lebonyolítja az SMTP-tranzakciót.

A qmail-smtpd működését az alábbi környezeti változók befolyásolják (ezeknek egy része a SPAMCONTROL nevű patch-csel jön):

  • DATABYTES: legfeljebb mekkora levelet küldhet a kliens.
  • HELODNSCHECK: ellenőrizze-e, hogy a HELO-ban átadott névnek van-e MX vagy A rekordja (noha elvileg csak A kéne hogy legyen, az MX irreleváns); ha nincs, 451-es kódot mond.
  • RELAYCLIENT: ha be van állítva, a kliens bárhova küldhet levelet (olyat is, ami nem a mi gépünknek szól).
  • MAXRECIPIENTS: legfeljebb hány címzettje lehet a levélnek.
  • MFDNSCHECK: ha be van állítva, visszautasítja a levelet (451), ha a MAIL FROM-ban szereplő domainnek sem MX, sem A rekordja nincs.
  • stb.

Látható, hogy ezeknek az értéke kliensfüggő kéne, hogy legyen. Az egyes kliensekre vonatkozó szabályokat a tcp.smtp.cdb fájlban adhatjuk meg.

A tcp.smtp.cdb egy szövegfájlból készül, amelyben ilyesmik vannak (a formátum neve amúgy tcprules, az után a program után, ami cdb-vé fordítja):

127.0.0.1:allow,RELAYCLIENT="",RBLSMTPD="",MAXRECIPIENTS="100"
1.2.3.:allow,HELODNSCHECK="",MFDNSCHECK="",MAXRECIPIENTS="100"
1.2.3.4:deny
1.2.5-15.:allow,RBLSMTPD=/-mail blocked. Keep your spam./
:allow,HELODNSCHECK="",MFDNSCHECK="",MAXRECIPIENTS="10"

(Ez valószínűleg részben átkerül majd az ucspi-tcp ill. a qmail szócikkbe, ha elkészülnek.)

4.2 axfr-get

Az axfr-get egy egyszerű AXFR-kliens, amivel egy domain zónafájlját tudjuk letölteni egy szerverről.

Futtatása:

tcpclient -R -v a.root-servers.net 53 axfr-get . rootzone rootzone.tmp

5 Potenciális ZH-kérdések

  • Vázolja fel egy UCSPI-s szolgáltatás architektúráját! Milyen elemekből áll? Ezek hogyan kommunikálnak egymással?
Személyes eszközök