Unix-alapok

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
(Signalok: bővítés 2008-2)
(Processz állapottere: capabilityk)
23. sor: 23. sor:
 
* root directory (chroot esetén változhat)
 
* root directory (chroot esetén változhat)
 
* umask
 
* umask
* (capability-k)
+
* capability-k
 
* újabban: namespace (külön fs-, hálózati-, processz-, esetleg IPC-)
 
* újabban: namespace (külön fs-, hálózati-, processz-, esetleg IPC-)
 
* stb.
 
* stb.
  +
  +
== Capability-k ==
  +
* A root jogosultságainak szétparticionálása kisebb jogosultság-csomagokra, amikkel egy-egy thread külön-külön is rendelkezhet. Pl:
  +
** CAP_DAC_OVERRIDE: fájlrendszer-jogok figyelmen kívül hagyása;
  +
** CAP_KILL: nemcsak saját processznek küldhet signalt;
  +
** CAP_MKNOD: hozhat létre device node-ot a fájlrendszerben;
  +
** CAP_NET_BIND_SERVICE: bind()-olhat 1024 alatti portra;
  +
** CAP_SETUID: válthat user ID-t;
  +
** CAP_SYS_BOOT: újraindíthatja a gépet;
  +
** stb. (több tucat van)
  +
* Majd egyszercsak rendesen működni fog, és akkor jó lesz. :) Szerencsére talán már belátható időn belül.
  +
* A 2.6.24-es kernel óta elvileg a fájlrendszerben is adhatók capabilityk a programoknak, kb. mint a setuid bit. Mindössze kb. 10 évet késett ez a ''fícsör''.
  +
  +
=== Capabilityk a processzekben/threadekben ===
  +
* Minden threadhez három capability-halmaz tartozik:
  +
** Permitted ("megengedett"): A thread által birtokolható, örökíthető capabilityk halmaza. Ami nincs benne, azt az adott thread sehogy sem tudja megszerezni (kivéve, ha új binárist tölt magára exec()-kel, és azon pl. setuid bit volt).
  +
*** 2.6.24 előtt a CAP_SETPCAP-pel rendelkező processz bármelyik másik processz permitted készletében állítgathatta azokat a capabilityket, amik a saját permitted készletében megvoltak.
  +
** Inheritable ("örökíthető"): Amit a thread ebbe rak a permittedből, az lesz az általa exec() hívással indított új folyamat permitted készlete.
  +
** Effective ("hatásos"): ezeket szeretnénk éppen használni. A kernel a jogosultságellenőrzések során ezt nézi.
  +
* A processz a <tt>capset()</tt> hívással viszonylag szabadon állítgathatja a két utóbbi halmaz tartalmát (de csak a permitted készletben levő capabilitykkel gazdálkodhat).
  +
* fork() során az új processz a szülő capability-készleteit örökli.
  +
  +
=== Capabilityk a fájlrendszerben ===
  +
* A [[Filerendszerek|filerendszerben]] egy bővített attribútumban tárolódik, hogy az adott binárishoz milyen capability-halmazokat rendeltünk.
  +
* Szintén három halmaz van:
  +
** Permitted: a bináris futtatásával létrejövő processz permitted-készlete kiegészül az itt felsorolt capabilitykkel.
  +
** Inheritable: ez egy maszk, amit a kernel az <tt>exec()</tt> hívás során ÉS-kapcsolatba hoz a hívó processz inheritable halmazával; az így kapott halmazhoz adódik hozzá az iménti permitted halmaz.
  +
*** Ezzel korlátozható, hogy a bináris mit örökölhet a szülőjétől: pl. megoldható, hogy a <tt>/bin/sh</tt> ne örökölhessen CAP_DAC_OVERRIDE-ot és CAP_NET_BIND_SERIVCE-t. Ezzel egy csomó exploitot ki is heréltünk (persze ez csak látszatmegoldás lenne).
  +
*** Jobban használható arra, hogy démonoktól eleve elvegyünk olyan capabilityket, amikre nincs szükségük: pl. a webszervernek nem kellhet CAP_MKNOD.
  +
** Effective: ez itt nem halmaz, hanem egy jelzőbit. Ha 1, az új processz permitted-készletében szereplő összes capability azonnal megjelenik az effective készletben is. Ha 0, akkor nem.
  +
*** Buta programokhoz jó, amik nem tudják maguknak állítgatni az effective készletet akkor, amikor éppen kell.
  +
**** Ez a jelenlegi programok kb. 99,9999%-ára igaz.
  +
  +
=== capability bounding set ===
  +
* Ez régebben egy olyan rendszerszintű maszk volt, ami korlátozta, milyen capabilityket rakhat egy thread a permittedből az inheritable készletébe (ha nem volt benne elve).
  +
* A boot után a maszkban csak törölni lehetett a biteket, bebillenteni nem.
  +
** Ez a mechanizmus kevés valódi rendszerben volt igazán hasznos.
  +
* Újabban a binárisok permitted-készletét is maszkolja (így a rendszerbe bárhogy bejuttatott CAP_DAC_OVERRIDE-os bináris sem juthat ehhez a capabilityhez, ha kivesszük a bounding setből).
  +
* Még újabban (a 2.6.25 óta) már nem rendszer-, hanem szálszintű maszkról van szó.
  +
** A szál, ha van CAP_SETPCAP capabilityje, törölhet biteket a maszkból.
  +
** A maszk fork() és exec() során is öröklődik a gyermekfolyamatra.
  +
* Vannak még ezzel kapcsolatos finomságok; l. <tt>man 7 capabilities</tt>
  +
  +
=== Capabilityk használhatósága ===
  +
* Egyelőre nem az igazi a sok kompatibilitási megoldás miatt, pl:
  +
** sajnos a 0-ás UID-val futó processzek exec() után is teljes capability-halmazt kapnak.
  +
** Setuid root binárisok szintén.
  +
** Teljes root->nemroot uidváltás esetén a permitted és az effective halmaz törlődik.
  +
** root->nemroot EUID-váltás esetén a kernel törli az effective halmazt.
  +
** nemroot->root EUID-váltás esetén a kernel a permitted halmazt az effective halmazra másolja.
  +
*** A 2.6.26-ban javítottak ezen, az okos programok (az összes program 0,0001%-a) már alighanem tudnák normálisan használni a capabilityket.
   
 
== Signalok ==
 
== Signalok ==

A lap 2008. szeptember 17., 01:23-kori változata

A Unix:

  • Többfelhasználós (multiuser)
  • Többfeladatos (multitaszking)
  • Hálózati (network)

operációs rendszer (operating system). De ezt úgyis tudtuk.

Egy csomó minden mást is tudunk, úgyhogy koncentráljunk inkább olyasmire, amit talán nem, vagy nem annyira pontosan.

Tartalomjegyzék

1 Processz állapottere

  • UID
  • GID
  • supplementary group memberships
  • effektív UID (pl. setuid bit miatt lehet más)
  • effektiv GID (pl. setgid bit miatt lehet más)
  • current working directory
  • filedeszkriptorok
  • környezeti változók
  • PGID
    • kill -SIGNAL -PGID az egész process groupnak elküldi a signalt
  • scheduler
  • niceness (Linuxon újabban külön ionice is)
  • resource limits
  • root directory (chroot esetén változhat)
  • umask
  • capability-k
  • újabban: namespace (külön fs-, hálózati-, processz-, esetleg IPC-)
  • stb.

2 Capability-k

  • A root jogosultságainak szétparticionálása kisebb jogosultság-csomagokra, amikkel egy-egy thread külön-külön is rendelkezhet. Pl:
    • CAP_DAC_OVERRIDE: fájlrendszer-jogok figyelmen kívül hagyása;
    • CAP_KILL: nemcsak saját processznek küldhet signalt;
    • CAP_MKNOD: hozhat létre device node-ot a fájlrendszerben;
    • CAP_NET_BIND_SERVICE: bind()-olhat 1024 alatti portra;
    • CAP_SETUID: válthat user ID-t;
    • CAP_SYS_BOOT: újraindíthatja a gépet;
    • stb. (több tucat van)
  • Majd egyszercsak rendesen működni fog, és akkor jó lesz. :) Szerencsére talán már belátható időn belül.
  • A 2.6.24-es kernel óta elvileg a fájlrendszerben is adhatók capabilityk a programoknak, kb. mint a setuid bit. Mindössze kb. 10 évet késett ez a fícsör.

2.1 Capabilityk a processzekben/threadekben

  • Minden threadhez három capability-halmaz tartozik:
    • Permitted ("megengedett"): A thread által birtokolható, örökíthető capabilityk halmaza. Ami nincs benne, azt az adott thread sehogy sem tudja megszerezni (kivéve, ha új binárist tölt magára exec()-kel, és azon pl. setuid bit volt).
      • 2.6.24 előtt a CAP_SETPCAP-pel rendelkező processz bármelyik másik processz permitted készletében állítgathatta azokat a capabilityket, amik a saját permitted készletében megvoltak.
    • Inheritable ("örökíthető"): Amit a thread ebbe rak a permittedből, az lesz az általa exec() hívással indított új folyamat permitted készlete.
    • Effective ("hatásos"): ezeket szeretnénk éppen használni. A kernel a jogosultságellenőrzések során ezt nézi.
  • A processz a capset() hívással viszonylag szabadon állítgathatja a két utóbbi halmaz tartalmát (de csak a permitted készletben levő capabilitykkel gazdálkodhat).
  • fork() során az új processz a szülő capability-készleteit örökli.

2.2 Capabilityk a fájlrendszerben

  • A filerendszerben egy bővített attribútumban tárolódik, hogy az adott binárishoz milyen capability-halmazokat rendeltünk.
  • Szintén három halmaz van:
    • Permitted: a bináris futtatásával létrejövő processz permitted-készlete kiegészül az itt felsorolt capabilitykkel.
    • Inheritable: ez egy maszk, amit a kernel az exec() hívás során ÉS-kapcsolatba hoz a hívó processz inheritable halmazával; az így kapott halmazhoz adódik hozzá az iménti permitted halmaz.
      • Ezzel korlátozható, hogy a bináris mit örökölhet a szülőjétől: pl. megoldható, hogy a /bin/sh ne örökölhessen CAP_DAC_OVERRIDE-ot és CAP_NET_BIND_SERIVCE-t. Ezzel egy csomó exploitot ki is heréltünk (persze ez csak látszatmegoldás lenne).
      • Jobban használható arra, hogy démonoktól eleve elvegyünk olyan capabilityket, amikre nincs szükségük: pl. a webszervernek nem kellhet CAP_MKNOD.
    • Effective: ez itt nem halmaz, hanem egy jelzőbit. Ha 1, az új processz permitted-készletében szereplő összes capability azonnal megjelenik az effective készletben is. Ha 0, akkor nem.
      • Buta programokhoz jó, amik nem tudják maguknak állítgatni az effective készletet akkor, amikor éppen kell.
        • Ez a jelenlegi programok kb. 99,9999%-ára igaz.

2.3 capability bounding set

  • Ez régebben egy olyan rendszerszintű maszk volt, ami korlátozta, milyen capabilityket rakhat egy thread a permittedből az inheritable készletébe (ha nem volt benne elve).
  • A boot után a maszkban csak törölni lehetett a biteket, bebillenteni nem.
    • Ez a mechanizmus kevés valódi rendszerben volt igazán hasznos.
  • Újabban a binárisok permitted-készletét is maszkolja (így a rendszerbe bárhogy bejuttatott CAP_DAC_OVERRIDE-os bináris sem juthat ehhez a capabilityhez, ha kivesszük a bounding setből).
  • Még újabban (a 2.6.25 óta) már nem rendszer-, hanem szálszintű maszkról van szó.
    • A szál, ha van CAP_SETPCAP capabilityje, törölhet biteket a maszkból.
    • A maszk fork() és exec() során is öröklődik a gyermekfolyamatra.
  • Vannak még ezzel kapcsolatos finomságok; l. man 7 capabilities

2.4 Capabilityk használhatósága

  • Egyelőre nem az igazi a sok kompatibilitási megoldás miatt, pl:
    • sajnos a 0-ás UID-val futó processzek exec() után is teljes capability-halmazt kapnak.
    • Setuid root binárisok szintén.
    • Teljes root->nemroot uidváltás esetén a permitted és az effective halmaz törlődik.
    • root->nemroot EUID-váltás esetén a kernel törli az effective halmazt.
    • nemroot->root EUID-váltás esetén a kernel a permitted halmazt az effective halmazra másolja.
      • A 2.6.26-ban javítottak ezen, az okos programok (az összes program 0,0001%-a) már alighanem tudnák normálisan használni a capabilityket.

3 Signalok

  • Aszinkron jelzések processzek között.
    • A címzett nem tudja, kitől kapja a signalt.
  • Lehet hozzájuk handlereket regisztrálni
    • Handler nélkül öt alapakció egyikét eredményezik:
      1. Term (kilépés)
      2. Ign (semmi)
      3. Core (kilépés és coredump)
      4. Stop (felfüggesztés)
      5. Cont (folytatás, ha a processz fel volt függesztve)
    • Van alapértelmezés, hogy ezek közül melyiket, de a processz megváltoztathatja.
    • A signal disposition (hogy mi történjen egy adott signal hatására) a processz tulajdonsága, minden threadben ugyanaz.
  • Az érdektelenek kimaszkolhatók, blokkolhatók (kivéve KILL és STOP).
    • A blokkolt signalok sorbaállnak, amíg a processz vagy szál meg nem szünteti a blokkolásukat.
    • A processz lekérdezheti, milyen sorbanálló signaljai vannak (sigpending()).
    • Egyféle signalból csak egy állhat sorba.
    • A sorbanálló signalok kézbesítési sorrendje nem specifikált.
  • Signal címzettje lehet processz vagy szál (thread).
    • Ha többszálú processz kap signalt, a kernel az adott signalt éppen nem blokkoló szálak közül véletlenszerűen választja ki azt, amelyik megkapja.
  • Kézbesítésük amúgy sem feltétlenül azonnali (pl. D állapotú processz nem kapja meg, l. később)
  • A signal megkapásakor a program futása megszakad, és a vezérlés a handlerre kerül.
    • Signal handlert óvatosan kell írni, mert a program tetszőlegesen inkonzisztens állapotban lehet a handler indulásakor.
    • A POSIX szabvány specifikál néhány tucat könyvtári függvényt, amiket elvileg gond nélkül hívhatunk.
    • Signal érkezése rendszerhívást is megszakíthat; a hívás ebben az esetben EINTR hibakóddal tér vissza, de a processz kérheti, hogy ilyenkor a legutóbbi rendszerhívás automatikusan hívódjon újra a signal handler kilépése után (ennél igazából bonyolultabb, függ a hívás jellegétől is, itt nem megyünk bele, l. man 7 signal).
  • Signal küldése
    • kill paranccsal
    • kill() rendszerhívással
    • sigqueue() rendszerhívással (l. a real-time signaloknál)
  • Néhány gyakori signal:
    • HUP (HangUp)
      • Ha eltűnik a terminál (pl. a user bezárja az ablakot)
      • Daemonoknál gyakran: olvasd újra a configot
    • INT (Interrupt)
      • CTRL-C
    • QUIT
      • CTRL-\
    • ILL (Illegal instruction)
      • CPU-kivétel
      • súlyos program- vagy hardverhibára utal
    • KILL
      • Kilépteti a processzt, nem maszkolható
    • USR1
      • Nincs előre definiált jelentése, szabadon felhasználható
    • SEGV (Segmentation Violation)
      • "A program érvénytelen műveletet hajtott végre"
      • Tipikus okok:
        • Pointer rossz helyre mutat (pl. túlcímeztünk egy tömböt)
        • Valamit kértünk, nem sikerült, mégis használjuk (pl. nemlétező file-t próbáltunk megnyitni, és annak ellenére, hogy az open() hibát adott vissza, az érvénytelen filedescriptorból olvasni próbálunk)
      • Vagyis: programhiba
      • Lehet hardverhiba is
    • USR2
      • l. mint fent
    • PIPE ("broken pipe")
      • A pipe-unkat olvasó processz kilépett
    • ALRM (Alarm)
      • Időzítő lejárt
    • TERM (Terminate)
      • Default "lépj ki" signal
    • CHLD (Child exited)
      • Véget ért egy gyermekfolyamatunk
    • CONT (Continue)
      • Folytatódhat a végrehajtás STOP vagy TSTP után
    • STOP
      • Felfüggesztés, nem maszkolható
    • TSTP (Terminal stop)
      • Felfüggesztés CTRL-Z hatására
    • XCPU (CPU time exceeded)
      • Lejárt a megengedett CPU-idő
    • WINCH (Window changed)
      • Megváltozott a terminál mérete
  • Létezik "real-time signal" is; a Linux 32 különbözőt támogat.
    • Nincs előre definiált jelentésük, arra használjuk, amire akarjuk.
    • Különlegességek:
      • Ugyanabból a real-time signalból egynél több is állhat sorba egyszerre.
      • A sigqueue() rendszerhívással adat is küldhető a signal mellé; a címzett ki tudja olvasni.
      • Ha sigqueue()-t használunk, a címzett megnézheti, milyen UID-val és PID-val futott a küldő-processz.
      • A kézbesítés sorrendje garantált. Először a signalok sorszáma szerinti növekvő sorrendben, és ha ez nem egyértelmű, akkor a sorbaállítás ideje szerinti növekvő sorrendben kézbesülnek.
      • A 2.6.8-as kernel óta userenkénti rlimit szabályozza, hány signal állhat sorba.
        • Az rlimit a processz állapotteréhez tartozik; több azonos UID-val futó processz limitje különbözhet. Az ellenőrzés a sigqueue() meghívásakor történik, és a kernel a hívó processz limitjét hasonlítja össze az adott user által már sorbaállított signalok számával.

4 Processzek állapotai

Pl. a ps(1) outputjában látszanak:

% ps axu
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
[...]
korn      1842  0.0  0.1   4932  2592 ?        Ds   Sep07   0:23 SCREEN -D
korn     17670  0.0  0.0   4896   948 pts/0    R+   20:55   0:00 ps axu
[...]
  • D: I/O-ra vár (itt alighanem épp a read() vagy a write() rendszerhívásban)
  • R: fut vagy futásra kész
  • S: vár (ilyenkor kaphat signalt)
  • T: fel van függesztve
  • Z: "zombie" - véget ért, de a szülője még nem olvasta ki a visszatérési értékét a wait() vagy a waitpid() rendszerhívással

Egyéb flagek:

  • <: negatív nice (több processzoridőt kap)
  • N: pozitív nice (kevesebb processzoridőt kap)
  • s: session leader (igazából nincs jelentősége)
  • l: többszálú
  • +: előtérben van

5 Ajánlott irodalom

Ezeket részben hallgatók írták egy másik tárgy óráin elhangzottak alapján, úgyhogy lehetnek bennük tévedések:

Ezek megbízhatóbbak, de csak a signalokról van szó bennük:

A processzek lehetséges állapotairól:

A process groupokról:

6 Potenciális zh-kérdések

  • Soroljon fel legalább tíz olyan állapotváltozót (az állapottér elemét), amelyet egy unixos gyermekfolyamat örököl a szülőjétől!
    • current working directory
    • UID
    • GID
    • effektív UID (pl. setuid bit miatt lehet más)
    • effektiv GID (pl. setgid bit miatt lehet más)
    • PGID
    • filedeszkriptorok
    • környezeti változók
    • umask
    • root directory (chroot esetén változhat)
  • Mi a process group?
    • Több processz összerendelése egy csoportba. Hasznos, ha futás közben forkol a processz. Nem szükséges figyelni, hogy indult-e újabb, mert a PGID segítségével a csoport összes folyamata megkapja a nekik szánt signal-t.
  • Mi a supplementary group?
    • A felhasználó több csoportnak is a tagja, a /etc/group-ban több grouphoz is be van jegyezve.
  • A processzek szempontjából mit jelent az, hogy egy felhasználó tagja egy csoportnak?
    • Szigorúan véve nem a felhasználóknak, hanem a futó folyamatoknak vannak csoporttagságaik, ezért a változtatások csak az újonnan induló processzekre lesznek érvényesek, ha a felhasználót hozzáadjuk egy újabb csoporthoz.
    • Még szigorúbban véve az adott buroktól öröklik a jogokat az újonnan induló folyamatok is, ezért aztán újra be kell lépni. Vagy legalábbis újra beolvastatni a burokkal a csoporttagságokat.
Személyes eszközök