Eddigi ZH-kérdések

A Unix/Linux szerverek üzemeltetése wikiből
(Változatok közti eltérés)
a (Unix-alapok: typo)
a (Volt, aki azt hitte, ennyi a tárgy anyaga, és meglepődött, amikor olyat is kérdeztem, ami itt nem szerepelt. :o)
 
(egy szerkesztő 12 közbeeső változata nincs mutatva)
1. sor: 1. sor:
Az alábbiakban leírom az eddig szerepelt zh-kérdéseket, a hozzájuk tartozó helyes válaszokat (ezeket a zh-ban elvárt minimális részletességnél szabatosabban kifejtve), és esetleg néhány, a hallgatók által írt választ, kommentárokkal. Az olvashatatlan szavakat "????" jelöli; a megtippelt, de nem jól olvasható szavakat "????megtippelt_szó????". A helyes válaszokban zárójelben szereplő mondatok csak kiegészítik a választ, nem elvárás, hogy a zh-ban is leírjátok őket.
+
Az alábbiakban leírom a 2009-ig szerepelt zh-kérdéseket (a frissebbek megtalálhatók az egyes szócikkek alatt), a hozzájuk tartozó helyes válaszokat (ezeket a zh-ban elvárt minimális részletességnél szabatosabban kifejtve), és esetleg néhány, a hallgatók által írt választ, kommentárokkal. Az olvashatatlan szavakat "????" jelöli; a megtippelt, de nem jól olvasható szavakat "????megtippelt_szó????". A helyes válaszokban zárójelben szereplő mondatok csak kiegészítik a választ, nem elvárás, hogy a zh-ban is leírjátok őket.
   
 
A megadott pontszámok a zh-ban szereplő pontszámok; elvileg a kérdések relatív nehézségén és fontosságán kívül azt is hivatottak tükrözni, mennyire részletes választ várok el. A későbbiekben a tapasztalatok alapján ezek a pontszámok módosulhatnak, és egy itt leírt négypontos kérdés legközelebb szerepelhet pl. hatpontosként.
 
A megadott pontszámok a zh-ban szereplő pontszámok; elvileg a kérdések relatív nehézségén és fontosságán kívül azt is hivatottak tükrözni, mennyire részletes választ várok el. A későbbiekben a tapasztalatok alapján ezek a pontszámok módosulhatnak, és egy itt leírt négypontos kérdés legközelebb szerepelhet pl. hatpontosként.
   
 
Sajnos a zh-k helyesírása és magyarsága több esetben hagyott kívánnivalót maga után. Szerintem helytelenül írni igénytelenség. Ezzel persze nem kötelező egyetérteni, és a pontozásnál sem vettem figyelembe a nem értelemzavaró hibákat, de azért próbáljatok erre is odafigyelni.
 
Sajnos a zh-k helyesírása és magyarsága több esetben hagyott kívánnivalót maga után. Szerintem helytelenül írni igénytelenség. Ezzel persze nem kötelező egyetérteni, és a pontozásnál sem vettem figyelembe a nem értelemzavaró hibákat, de azért próbáljatok erre is odafigyelni.
  +
  +
'''Figyelem!''' A félreértések elkerülése végett: ennek az oldalnak az elolvasása, esetleg bemagolása nem pótolja a segédanyag többi részének elolvasását és megértését. Sajnálatos, hogy erre külön fel kell hívni a figyelmet.
   
 
== [[Unix-alapok]] ==
 
== [[Unix-alapok]] ==
296. sor: 298. sor:
 
: A HTTP virtualhosting működési elve az, hogy a HTTP ''Host:'' fejlécében megmondjuk a webszervernek, melyik virtuális szerver címterében kell értelmezni a kérésben átadott elérési utat és fájlnevet. Ha HTTPS-t használunk, akkor a böngésző és a webszerver közötti TCP-kapcsolat felépülése után először az SSL handshake-re kerül sor; ennek során a böngésző hitelesíti a szervert, és többek között azt is ellenőrzi, hogy a szerver tanúsítványa az URL-ben szereplő hosztnévre szól-e.
 
: A HTTP virtualhosting működési elve az, hogy a HTTP ''Host:'' fejlécében megmondjuk a webszervernek, melyik virtuális szerver címterében kell értelmezni a kérésben átadott elérési utat és fájlnevet. Ha HTTPS-t használunk, akkor a böngésző és a webszerver közötti TCP-kapcsolat felépülése után először az SSL handshake-re kerül sor; ennek során a böngésző hitelesíti a szervert, és többek között azt is ellenőrzi, hogy a szerver tanúsítványa az URL-ben szereplő hosztnévre szól-e.
 
: A gond az, hogy a szerver ebben a pillanatban még nem tudja, hogy mi az URL, így, ha virtuális hosztokat használunk, nem tudja eldönteni, melyik virtuális hoszt tanúsítványával hitelesítse magát a böngésző felé; ha nem a jót választja, a böngésző hibaüzenetet ad (vagy az alkalmazásszintű proxy megtagadja a webszájthoz való hozzáférést). A szerver csak az SSL-handshake után, a HTTP-tranzakcióból tudja meg (a ''Host:'' fejlécből), hogy melyik virtualhostra kíváncsi a kliens.
 
: A gond az, hogy a szerver ebben a pillanatban még nem tudja, hogy mi az URL, így, ha virtuális hosztokat használunk, nem tudja eldönteni, melyik virtuális hoszt tanúsítványával hitelesítse magát a böngésző felé; ha nem a jót választja, a böngésző hibaüzenetet ad (vagy az alkalmazásszintű proxy megtagadja a webszájthoz való hozzáférést). A szerver csak az SSL-handshake után, a HTTP-tranzakcióból tudja meg (a ''Host:'' fejlécből), hogy melyik virtualhostra kíváncsi a kliens.
: Két megoldás van: az egyik az, hogy minden HTTPS-es virtualhostos külön IP:port páron futtatunk, így a szerver abból, hogy melyik IP:portra jött kapcsolatkérés, meg tudja állapítani, melyik virtualhostot szólítják meg éppen, és így azt is tudja, melyik tanúsítvánnyal kell hitelesítenie magát.
+
: Négy megoldás van: az egyik az, hogy minden HTTPS-es virtualhostot külön IP:port páron futtatunk, így a szerver abból, hogy melyik IP:portra jött kapcsolatkérés, meg tudja állapítani, melyik virtualhostot szólítják meg éppen, és így azt is tudja, melyik tanúsítvánnyal kell hitelesítenie magát.
: A másik megoldást csak akkor használhatjuk, ha minden virtualhostunk ugyanabba a domainbe tartozik, és a hitelesítő hatóságunk (CA, certificate authority) hajlandó nekünk joker-tanúsítványt adni erre a domainre; ekkor a tanúsítvány elvileg minden, az adott domainbe tartozó konkrét hosztnévre érvényes lesz, tehát a szerver nem kerül olyan dilemma elé, hogy több tanúsítvány közül kellene választania, vagyis az összes virtualhost maradhat ugyanazon az IP:port páron, hanem hitelesítheti magát azzal az eggyel, ami van. A módszer hátránya, hogy nem minden CA ad ki - és nem minden böngésző fogadja el - a joker-tanúsítványt.
+
: A második megoldást csak akkor használhatjuk, ha minden virtualhostunk ugyanabba a domainbe tartozik, és a hitelesítő hatóságunk (CA, certificate authority) hajlandó nekünk joker-tanúsítványt (wildcard cert) adni erre a domainre; ekkor a tanúsítvány elvileg minden, az adott domainbe tartozó konkrét hosztnévre érvényes lesz, tehát a szerver nem kerül olyan dilemma elé, hogy több tanúsítvány közül kellene választania, vagyis az összes virtualhost maradhat ugyanazon az IP:port páron, hanem hitelesítheti magát azzal az eggyel, ami van. A módszer hátránya, hogy nem minden CA ad ki joker-tanúsítványt, és elképzelhető, hogy nem minden böngésző fogadja el.
  +
: A harmadik megoldás az SNI, aminek az a lényege, hogy a kliens még az SSL handshake előtt jelzi a szervernek, milyen névre szóló tanúsítványt vár.
  +
: A negyedik megoldás az, hogy a tanúsítványban a Subject Alternative Name mezőben felsoroljuk az összes virtualhost nevét (a felsorolás tartalmazhat jokereket is).
   
 
: '''Egy válasz a zh-ból (betű szerint):'''
 
: '''Egy válasz a zh-ból (betű szerint):'''
303. sor: 305. sor:
 
: ''1. különböző portra kell tenni a virtuális HTTPS-szervereket és port alapján tudjuk, melyik cert. kell''
 
: ''1. különböző portra kell tenni a virtuális HTTPS-szervereket és port alapján tudjuk, melyik cert. kell''
 
: ''2. a domain-re vonatkozó cert. kell''
 
: ''2. a domain-re vonatkozó cert. kell''
: '''Kommentár:''' A válasz 5 pontot ér. Fél pont levonás jár azért, mert nem fejti ki, mi "a domain" (impliciten feltételezi, hogy minden virtualhost ugyanabba a domainbe tartozik), és másik fél pont levonás azért, mert nemcsak külön portra, hanem külön IP-re is tehetjük az egyes virtuális szervereket; az IP:port ''párnak'' kell különböznie.
+
: '''Kommentár:''' A válasz 3 pontot ér. Fél pont levonás jár azért, mert nem fejti ki, mi "a domain" (impliciten feltételezi, hogy minden virtualhost ugyanabba a domainbe tartozik), és másik fél pont levonás azért, mert nemcsak külön portra, hanem külön IP-re is tehetjük az egyes virtuális szervereket; az IP:port ''párnak'' kell különböznie. Két pont levonás jár az SNI kihagyásáért.
   
 
== [[Filerendszerek]] ==
 
== [[Filerendszerek]] ==
423. sor: 425. sor:
 
: ''Fontos még a naplózás is. ext3 fizikai naplót vezet. Az xfs pedig logikai naplót. Ez utóbbi fontos előnye hogy csak a metaadatokat írja a log file-ba, ezáltal gyorsabban ????visszaállítható????, mint pl. az ext3.''
 
: ''Fontos még a naplózás is. ext3 fizikai naplót vezet. Az xfs pedig logikai naplót. Ez utóbbi fontos előnye hogy csak a metaadatokat írja a log file-ba, ezáltal gyorsabban ????visszaállítható????, mint pl. az ext3.''
 
: ''Ha nem linuxban gondolkodunk jó megoldás pl a SUN-os zfs ahol a logikai kötetek és a filerendszer "összenőtt". Az átméretezés online így nem gond. (plane hogy a használt ufs fs már régóta tudja ezt)''
 
: ''Ha nem linuxban gondolkodunk jó megoldás pl a SUN-os zfs ahol a logikai kötetek és a filerendszer "összenőtt". Az átméretezés online így nem gond. (plane hogy a használt ufs fs már régóta tudja ezt)''
: '''Kommentár:''' A válasz 4 pontot ér. Egyrészt nem említi a snapshot-támogatást, másrészt az LVM pont a fizikai kötetek méretét nem tudja megváltoztatni. A logikai és a fizikai napló nem abban különbözik, hogy csak a meta-adatokat naplózza, vagy az összeset, hanem abban, hogy milyen módon teszi ezt. A fizikai naplóban a diszkre kiírandó szektorok vannak benne, egy az egyben, míg a logikai naplóban az elvégzendő módosítások magas szintű leírása, ami sokkal kompaktabb.
+
: '''Kommentár:''' A válasz 4 pontot ér. Egyrészt nem említi a snapshot-támogatást, másrészt az LVM pont a fizikai kötetek méretét nem tudja megváltoztatni (megj.: ez azóta változott, most már van <tt>pvresize</tt> is). A logikai és a fizikai napló nem abban különbözik, hogy csak a meta-adatokat naplózza, vagy az összeset, hanem abban, hogy milyen módon teszi ezt. A fizikai naplóban a diszkre kiírandó szektorok vannak benne, egy az egyben, míg a logikai naplóban az elvégzendő módosítások magas szintű leírása, ami sokkal kompaktabb.
   
 
== [[A bootfolyamat]] ==
 
== [[A bootfolyamat]] ==
573. sor: 575. sor:
 
: '''Helyes válasz:'''
 
: '''Helyes válasz:'''
 
: Mivel a TCP kapcsolatorientált, a naplózószerver elvileg túlterhelhető kapcsolatokkal.
 
: Mivel a TCP kapcsolatorientált, a naplózószerver elvileg túlterhelhető kapcsolatokkal.
: Mivel a TCP-ben van torlódásvezérlés, elképzelhető, hogy a naplóüzeneteket író programok blokkolódnak arra várva, hogy a helyi üzenetpuffert hálózaton át üríteni lehessen; ha nem ezt várják meg, egy idő után megtelik a memória a pufferrel vagy elküldés nélkül el kell dobni üzeneteket.
+
: Mivel a TCP-ben van torlódásvezérlés, elképzelhető, hogy a naplóüzeneteket író programok blokkolódnak arra várva, hogy a helyi üzenetpuffert hálózaton át üríteni lehessen; ha ezt nem várják meg, egy idő után megtelik a memória a pufferrel vagy elküldés nélkül el kell dobni üzeneteket.
 
: A TCP-kapcsolatok szemantikájának megvalósítása növeli a naplózókliens összetettségét; kezelni kell pl., ha megszakad a hálózati kapcsolat.
 
: A TCP-kapcsolatok szemantikájának megvalósítása növeli a naplózókliens összetettségét; kezelni kell pl., ha megszakad a hálózati kapcsolat.
 
: Az UDP kapcsolatmentes, "fire and forget" jellegű. Emiatt kliensoldalon nem feltétlenül van visszajelzés arról, hogy egy üzenet elküldése sikeres volt-e, vagyis hálózati problémák esetén naplóüzenetek elveszhetnek útközben.
 
: Az UDP kapcsolatmentes, "fire and forget" jellegű. Emiatt kliensoldalon nem feltétlenül van visszajelzés arról, hogy egy üzenet elküldése sikeres volt-e, vagyis hálózati problémák esetén naplóüzenetek elveszhetnek útközben.
697. sor: 699. sor:
   
 
== Gyakorlati rész ==
 
== Gyakorlati rész ==
  +
  +
=== Routingos feladat 2006. ===
   
 
* Adott a következő routing-tábla, és a 127.128.129.130-as géppel szeretnénk kommunikálni. A tábla mely sorai milyen sorrendben jutnak szerephez? Milyen üzeneteket kell kiküldenünk, ha feltételezzük, hogy az ARP-táblánk üres? Töltse ki a táblázat üresen hagyott mezőit (a megadott adatokból a hiányzó adatok kikövetkeztethetők)! Rajzolja le a routing-tábla által leírt hálózat topológiáját! (20 pont)
 
* Adott a következő routing-tábla, és a 127.128.129.130-as géppel szeretnénk kommunikálni. A tábla mely sorai milyen sorrendben jutnak szerephez? Milyen üzeneteket kell kiküldenünk, ha feltételezzük, hogy az ARP-táblánk üres? Töltse ki a táblázat üresen hagyott mezőit (a megadott adatokból a hiányzó adatok kikövetkeztethetők)! Rajzolja le a routing-tábla által leírt hálózat topológiáját! (20 pont)
780. sor: 784. sor:
 
Azzal nem kell törődni, hogy bizonyos állomások látszólag több helyen is vannak; a routing mindenképpen a legspecifikusabb route-ot választja.
 
Azzal nem kell törődni, hogy bizonyos állomások látszólag több helyen is vannak; a routing mindenképpen a legspecifikusabb route-ot választja.
   
----
+
=== Scriptes feladat 2006. ===
   
 
* Keressen versenyhelyzeteket, logikai vagy biztonsági hibákat az alábbi scriptben! Hogyen lehetne ezeket elkerülni? Amúgy mit próbál csinálni a script? (20 pont)
 
* Keressen versenyhelyzeteket, logikai vagy biztonsági hibákat az alábbi scriptben! Hogyen lehetne ezeket elkerülni? Amúgy mit próbál csinálni a script? (20 pont)
828. sor: 832. sor:
 
# További probléma a 16. sorral, hogy itt is lőhetünk ki ártatlan processzt, pl. ha a mi tailünk bármi (mondjuk I/O error) miatt kilépett, és az ő PID-jával indult valami más, akkor ezt a mást lőjük ki még akkor is, ha a 10 sorban a mi tailünk PID-ja (és csak az) került a TAILPID változóba.
 
# További probléma a 16. sorral, hogy itt is lőhetünk ki ártatlan processzt, pl. ha a mi tailünk bármi (mondjuk I/O error) miatt kilépett, és az ő PID-jával indult valami más, akkor ezt a mást lőjük ki még akkor is, ha a 10 sorban a mi tailünk PID-ja (és csak az) került a TAILPID változóba.
   
----
+
=== Scriptes feladat 2007. ===
  +
  +
Keressen versenyhelyzeteket, logikai vagy biztonsági hibákat az alábbi scriptben! Melyiknek mi a (nem kívánt) hatása? Melyik mennyire súlyos, ha a scriptet a rendszergazda futtatja? És ha egy normális felhasználó? Hogyan lehetne ezeket a hibákat elkerülni? Amúgy mit próbál csinálni a script? (20 pont)
  +
  +
<pre>
  +
1. #!/bin/sh
  +
2. # redirect stdout to log
  +
3. exec >foobar.$$.log
  +
4. # copy log to stderr
  +
5. tail -f foobar.$$.log >&2 &
  +
6. pidof tail >/tmp/tail.pid
  +
7. mkdir $(date)
  +
8. for i in $(find $@ -name *.mp3); do ln $i $(date)/; done
  +
9. touch $(date)/*
  +
10. find / -name *.mp3 >/tmp/mp3.log &
  +
11. FINDPID1=$(pidof find)
  +
12. find . –atime +30d | xargs echo rm –rf 2>/tmp/delete.log >/tmp/deleteold.sh &
  +
13. FINDPID2=$(pidof find)
  +
14. echo "Operation in progress... please wait."
  +
15. sleep 5
  +
16. cd $(date)
  +
17. echo Start:
  +
18. ls
  +
19. for i in *; do
  +
20. mv $i $(echo $i | sed 's/.*-/$ARTIST-/')
  +
21. done
  +
22. echo End:
  +
23. ls
  +
24. echo Waiting for find to exit.
  +
25. for i in $FINDPID1 $FINDPID2; do
  +
26. while [ -d /proc/$i ]; do
  +
27. echo Waiting...
  +
28. sleep 5
  +
29. done
  +
30. done
  +
31. # don't need this anymore, clean it up
  +
32. kill $(cat /tmp/tail.pid)
  +
33. rm –rf /tmp/tail.pid
  +
</pre>
  +
  +
Erről a feladatról rengeteget lehet írni, mert nagyon sok a hiba a scriptben. A 20 pontért nem kellett mindet megtalálni.
  +
  +
Hibák (az abszolút teljesség igénye nélkül):
  +
  +
# A 3. sorban megjósolható nevű ideiglenes fájlt hozunk létre; ha a scriptet egy támadó számára is írható könyvtárban futtatjuk, a támadó előre elhelyezhet <tt>foobar.{2-32767}.log</tt> nevű, valamilyen számára nem írható fájlra mutató symlinkeket ebben a könyvtárban; a script indításakor a kérdéses fájl a futtató felhasználó jogait használva íródik felül. Ha a scriptet a rendszergazda futtatja, szinte bármelyik fájl felülírható. A hibát úgy lehetett volna elkerülni, ha egyrészt nem abban a könyvtárban hozzuk létre az ideiglenes fájlt, amelyben éppen vagyunk (és amelyről nem tudunk semmit), hanem egy csak számunkra írható, konkrétan megadott könyvtárban; másrészt pedig ha a - például az <tt>mktemp(1)</tt> segítségével - biztonságos módon, mindenképpen egyedi néven hozzuk létre a fájlt.
  +
# A 3. sorban megkísérlünk létrehozni egy fájlt, ám nincs hibakezelés; egyszerűen feltételezzük, hogy sikerült, és az 5. sorban megpróbálunk olvasni a fájlból. Ha egy támadó olyan fájlra mutató symlinket helyezett el a mi fájlunk helyén, amelyet írni nem tudunk, csak olvasni, akkor a 3. sor nem írta felül, az 5. sor viszont kiírja az utolsó tíz sort és a később keletkezőket a képernyőre. Ha a fájl érzékeny adatokat tartlamaz, akkor ez nem kívánatos. A jó megoldás ugyanaz, mint az előbb. (Hasonló, de nehezebben kivitelezhető támadás, ha az aktuális könyvtár nem sticky és a támadónak is van rá írási joga: a támadó a 3. sor végrehajtása után, de az 5. végrehajtása előtt az érzékeny adatokat tartalmazó fájlra mutató symlinkkel helyettesíti a 3. sorban létrehozott fájlt. Így az sem akadály, ha a scriptet futtató usernek van rá írási joga; nem fog felülíródni, csak kilistázódni.)
  +
# A 6. sorban lekérdezzük azoknak a processzeknek a PID-ját, amelyeknek a neve <tt>tail</tt>, és az eredményt egy megjósolható nevű, mindenki által írható könyvtárban található fájlba írjuk. Ugyanazok a megfontolások érvényesek, mint a harmadik sornál.
  +
# A 6. sorban található <tt>pidof</tt> találhat nulla, egy vagy több futó <tt>tail</tt> nevű processzt; a script később (a 32. sorban) feltételezi, hogy egy volt, mégpedig az, amit az 5. sorban indítottunk. Ez több okból sem biztos. Egyrészt, az 5. és a 6. sor között versenyhelyzet van, mivel az 5. sort a háttérben indítjuk ("&" van a végén); elképzelhető, hogy amíg a háttérben indított shell a <tt>tail</tt>-t próbálja <tt>exec()</tt>-elni, addig nekünk lefut a 6. sorunk, és így még nincs is meg az <tt>tail</tt>, amit az 5. sorban indítottunk. Az is lehet, hogy mire a 6. sor lefut, az 5. sorban indított <tt>tail</tt> már kilépett (pl. mert a 3. sorban nem sikerült létrehozni a fájlt, mert nincs írási jogunk az aktuális könyvtárra, így az 5. sornál nem is létezik a fájl, vagyis a <tt>tail</tt> hibaüzenetet ad és "azonnal" kilép), és egy a rendszerben valaki más által futtatott <tt>tail</tt> nevű folyamatot találunk (vagyis még az sem garancia arra, hogy jó PID-t tárolunk, ha csak egy <tt>tail</tt> nevű folyamatot talál a <tt>pidof</tt>).
  +
# A 7. sorban a <tt>date</tt> kimenetét közvetlenül átadjuk az mkdirnek, idézőjel nélkül. A <tt>date</tt> kimenete viszont a területi beállításoktól függően mindenféle karaktert tartalmazhat: szóközt, / karaktert stb. Ha szóközt tartalmaz, és a /bin/sh nem a zsh, akkor az mkdir minden bizonnyal több argumentumot fog látni, vagyis több alkönyvtárat hoz létre; ha /-t, akkor pedig egy könyvtár-részfát próbál létrehozni, de hacsak nem létezik az összes könyvtár az utolsót leszámítva, a -p kapcsoló nélkül hibát ad. Az lett volna a helyes, ha a date-nek előírjuk, hogy milyen formátumban szeretnénk a dátumot (pl. <tt>date +%Y%m%d</tt>).
  +
# A 8. sor egy ciklus: <tt>for i in $(find $@ -name *.mp3); do ln $i $(date)/; done</tt>. Több gond is van vele.
  +
#* Egyrészt, ha az argumentumlistánk szóközöket is tartalmazó könyvtárneveket is tartalmazott, akkor a <tt>find</tt> ezeket esetleg a szóközök mentén szétdarabolva kapja meg, vagyis nem mindegyikben fog keresni.
  +
#* Másrészt, mivel a *.mp3-at nem tettük idézőjelbe, nem védtük meg a shelltől a csillagot, úgyhogy meg fogja próbálni az aktuális könyvtárban glob patternként értelmezni, vagyis behelyettesíteni a *.mp3 helyére az összes, az aktuális könyvtárban található .mp3 kiterjesztésű fájl nevét.
  +
#** Ha nincs ilyen, akkor bizonyos shellek (pl. a zsh) hibaüzenetet adnak; mások (pl. a bash) meghagyják a parancssorban a *.mp3 sztringet betű szerint.
  +
#** Ha pontosan egy .mp3 kiterjesztésű fájl van, akkor vele megegyező nevűeket keres a ciklus a megadott könyvtárak közül azokban, amelyeknek a nevében nem volt szóköz vagy egyéb szóhatároló karakter.
  +
#** Ha egynél több .mp3 kiterjesztésű fájl van, a <tt>find</tt> nem tudja feldolgozni a parancssorát és hibaüzenetet ad.
  +
#* Mindenesetre a ciklus csak abban az esetben keresi meg a megadott könyvtárakban található valamennyi mp3-at, ha az aktuális könyvtárban nincs mp3 kiterjesztésű fájl, a /bin/sh a bash, és a megadott könyvtárak nevében nincs szóhatároló karakter.
  +
#* Ha a find kimenete szóhatárolót tartalmazó fájlneveket vagy könyvtárneveket is tartalmaz, a shell ezeket esetleg feldarabolja, mielőtt a ciklusváltozóval végigiterálna rajtuk. Így végül a ciklusváltozó nem a find által megtalált fájlok nevein megy végig, hanem azokon a szavakon, amelyekből ezek a nevek állnak.
  +
#* A ciklusmagban minden iterációban újra lekérdezzük a dátumot, és feltételezzük, hogy az éppen lekérdezett dátumról elnevezett alkönyvtár létezik, és bele tudjuk hardlinkelni a megtalált fájlt. A dátum megváltozhatott azóta, hogy a könyvtárat létrehoztuk a 7. sorban (ha ugyan akkor sikerült egyáltalán létrehozni, ugye). A helyes megoldás az lett volna, hogy a dátumot egyszer kérdezzük le, egy változóban tároljuk, és mindig a változó értékét használjuk a $(date) helyett.
  +
#* Az <tt>ln</tt> hardlinket hozna létre, de ez csak akkor lehetséges, ha a régi és az új fájlnév is ugyanazon a fájlrendszeren található; tehát a ciklus csak azokat a fájlokat fogja tudni odahardlinkelni a dátumról elnevezett, esetleg nem is létező könyvtárba, amelyek ugyanazon a fájlrendszeren vannak, mint ez a könyvtár. A többi fájl esetében hibaüzenetet kapunk (alighanem az "invalid cross-device link" szövegűt).
  +
#* Több felsorolt könyvtárban is lehet azonos nevű mp3-fájl; ezeket mind ugyanabba a könyvtárba próbáljuk hardlinkelni (leszámítva a dátumváltozásokat :), így tehát fájlnév-ütközéskor nem jön létre a hardlink és hibaüzenetet kapunk.
  +
# A 9. sorban megint gondjaink lesznek a szóhatároló karaktert tartalmazó fájlnevekkel, mert ezeket minden védelem nélkül adjuk át a touch-nak. zsh-ban ezt megtehetjük így (hogy glob expansion eredménye a szóközt tartalmazó fájlnév), de más shellben nem feltétlenül, úgyhogy oda kell rá figyelni.
  +
# A 10. sorban korábbi hibák ismétlődnek: *.mp3, /tmp alatt létrehozott ismert nevű fájl stb.
  +
# A 11. sor a 6. sor pepitában (nem biztos hogy pont annak a findnak a PID-ját találjuk meg, amit az előbb indítottunk stb.)
  +
# A 12. sorban (<tt>find . –atime +30d | xargs echo rm –rf 2>/tmp/delete.log >/tmp/deleteold.sh &</tt>) elvileg a 30 napnál régebben megnyitott fájlokat keressük az aktuális munkakönyvtár alatt, és olyan parancssorokat építünk, amelyek ezeket a fájlokat letörlik. Itt is gond van a szóhatárolókat tartalmazó fájl- és könyvtárnevekkel; ezeknek a szavait egyenként fogjuk megpróbálni törölni. Két ismert nevű /tmp alatti fájlt is írunk, ami a korábban részletezett okok miatt veszélyes.
  +
# A 13. sorban a script megint azt feltételezi, hogy csak az imént indított find PID-ját fogja megtalálni a pidof, pedig legalábbis a 10. sorban indított előző find is jó eséllyel fut még.
  +
# A 14. és a 15. sorban nincs hiba(!) :).
  +
# A 16. sorban az aktuális dátumról elnevezett könyvtárba próbálunk váltani, és itt is minden ezzel kapcsolatos eddig említett probléma érint minket (szóközök; változott a dátum a róla elnevezett könyvtár létrehozása óta; stb.). Nincs hibakezelés; ha itt nem sikerült könyvtárat váltani, a későbbi műveleteket akkor is végrehajtjuk, csak nem azokon a fájlokon, amiken szerettük volna.
  +
# Nézzük a 19-21. sorban látható ciklust: <tt>for i in *; do mv $i $(echo $i | sed 's/.*-/$ARTIST-/'); done</tt>.
  +
#* $i-re idézőjel nélkül hivatkozunk, így esetleg több szóra darabolódik.
  +
#* A sed által előállított új fájlnév szintén tartalmazhat szóközt; azt is idézőjelbe kellett volna tenni.
  +
#* $ARTIST sehol sem kapott értéket a scriptben; feltehetően a szülőfolyamattól kellene örökölni, de nem ellenőrizzük, hogy nem üres-e.
  +
#* A sed-script aposztrófok között van, nem macskakörmök között, így $ARTIST értéke akkor sem helyettesítődik be, ha amúgy van neki; helyette szó szerint az fog szerepelni az új fájlnévben, hogy $ARTIST.
  +
#* Ezzel a sed-scripttel több különböző fájlnevet is leképezhetünk ugyanarra a fájlnévre, vagyis az mv-vel kombinálva felülírhatunk fájlokat egymással.
  +
# A 25-30. sorban levő ciklus arra próbál várni, hogy a findok kilépjenek. Gyakorlatilag mostanra simán elavulhattak azok a PID-k, amiket akkor találtunk, ha ugyan találtunk; úgyhogy ki tudja, hány és milyen processz kilépésére várunk, ráadásul sorban. Amíg arra várunk, hogy az első kilépjen, a többinek a PID-ja még akkor is érvénytelenné válhat (megkaphatja más processz), ha a külső ciklus elején még érvényes volt. Jó eséllyel lesznek olyan processzek, amelyeknek a kilépésére kétszer is várunk (mert mindkét változó tartalmazza a PID-jukat); ez tovább növeli annak az esélyét, hogy más processz kapja meg közben valamelyik PID-t, és esetleg sosem lép ki, vagyis az idők végezetéig csak várunk. Emellett persze nem biztos (csak nagyon valószínű), hogy be van mountolva a /proc fájlrendszer.
  +
# A 32. sorban feltételezzük, hogy:
  +
## A <tt>/tmp/tail.pid</tt> fájl az, amit mi hoztunk létre;
  +
## hogy csak az általunk indított <tt>tail</tt> PID-ját tartalmazza, de azt igen;
  +
## hogy az általunk indított <tt>tail</tt> még mindig fut;
  +
## vagy ha esetleg nem futna, akkor a PID-ját még nem kapta meg más folyamat;
  +
## hogy a 6. sorban megtalált <tt>tail</tt> processzek közül egyikért sem kár;
  +
## hogy ezek a processzek sem léptek még ki;
  +
## vagy ha igen, akkor nem kapta meg más processz egyiknek a PID-ját sem;
  +
## vagy ha mégis, akkor ezekért az új processzekért, amelyek a hajdani <tt>tail</tt>-ek PID-jaival futnak, szintén nem kár.
  +
# A 33. sorban, ha a /tmp/tail.pid egy könyvtár volt (ami már létezett, mielőtt mi megpróbáltunk ezen a néven fájlt létrehozni), akkor most a teljes tartalmával együtt letöröljük.
  +
  +
=== Tűzfalas feladat 2006. ===
   
 
* Írjon iptables tűzfalscriptet. Topológia és követelmények:
 
* Írjon iptables tűzfalscriptet. Topológia és követelmények:
906. sor: 910. sor:
   
 
Általában célszerű arra törekedni, hogy egy tűzfalban az "amit nem szabad, azt tilos" elv jusson érvényre, nem pedig az "amit nem tilos, azt szabad"; a kettőt keverni pedig nagyon nem szerencsés (átláthatatlanná válik).
 
Általában célszerű arra törekedni, hogy egy tűzfalban az "amit nem szabad, azt tilos" elv jusson érvényre, nem pedig az "amit nem tilos, azt szabad"; a kettőt keverni pedig nagyon nem szerencsés (átláthatatlanná válik).
  +
  +
=== 2009/1. feladat ===
  +
  +
Egy linuxos rendszeren a rendszergazda beállított ütemezett feladatként egy olyan shellscriptet, amely óránként lefut rendszergazdai jogkörrel, és a következő két sorból áll:
  +
  +
<pre>
  +
#!/bin/bash
  +
chmod 777 /tmp/*
  +
</pre>
  +
A <tt>/tmp</tt> könyvtár jogosultságbeállításai a szokásosak. Ha valamilyen nem-root felhasználóként be tudunk jelentkezni erre a rendszerre, hogyan tudunk a fentiek ismeretében rendszergazdai jogosultságokat szerezni (vagyis tetszőleges parancsot rootként lefuttatni)? Legalább két módszert találjon ki! (8 pont)
  +
  +
'''Megoldás:''' A chmod, ha nem mondunk neki mást, ész nélkül követi a symlinkeket és azon az inode-on állítja be a jogokat, amelyikre a symlink végeredményben mutat. A <tt>/tmp</tt> könyvtár mindenki számára írható, vagyis bárki bármilyen symlinket elhelyezhet itt. Az okos script gondoskodik arról, hogy a symlinkek célpontjai mindenki számára írhatóvá, olvashatóvá és végrehajthatóvá váljanak.
  +
  +
Ezután csak az a kérdés, mire mutasson az általunk létrehozott symlink. Nagyon sok jó megoldás van, ezért először kiemelnék két rosszat. Volt, aki úgy gondolta, hogy egy setuid root binárisra mutató symlinket hoz létre, amit majd felülír, ha megkapja rá a jogot, és utána a setuid bit miatt rendszergazdai jogosultságokkal futtatja. Ez azért nem működik, mert a <tt>chmod 777</tt> törli a setuid-bitet (ugye a jogosultságbitek igazából 4x3 bitből állnak, úgyhogy a 777 voltaképpen 0777-et jelent; az első 3 bit a setuid, a setgid és a sticky bit, amelyeket a 0777-es chmoddal nullázunk).
  +
  +
Egy másik rossz megoldás egy "<tt>foo; parancs;</tt>" nevű symlink létrehozása abban a reményben, hogy a shell, amikor a <tt>chmod 777 *</tt> parancssorát előállítja, a pontosvessző utáni részt új parancsként építi be a parancssorba. A shell ennyire szerencsére nem buta; a globbing nem parancssort, csak argumentumlistát állít elő.
  +
  +
Jó megoldások pl., a teljesség igénye nélkül:
  +
  +
* <tt>ln -s /etc/passwd /tmp</tt>, majd a saját userünk uid-ját átírjuk nullára; a legközelebbi belépésünkkor rendszergazdai jogaink lesznek.
  +
* <tt>ln -s /etc/shadow /tmp</tt>, majd a root user jelszó-hash-ét átírjuk egy olyanra, amelyhez ismerjük a plaintextet.
  +
* <tt>ln -s /az/adott/ütemezett/script /tmp</tt> (vagyis a fenti két sorból álló konkrét script); amikor írási jogot kapunk rá, hozzáfűzünk tetszőleges parancsokat a végéhez.
  +
* <tt>ln -s /etc/crontab /tmp</tt>, és új ütemezett feladatot veszünk fel, amely rootként fut majd.
  +
* <tt>ln -s /bin/bash /tmp</tt>; tudjuk, hogy a rendszergazdai jogokkal futó ütemezett script parancsértelmezője a bash, úgyhogy ha a basht felülírjuk, az általunk kívánt kód fog lefutni helyette, szintén rootként.
  +
* <tt>ln -s /dev/sda /tmp</tt>; ezután megkerssük az sda-n található fájlrendszerben egy saját binárisunk inode-ját, átállítjuk a tulajdonosát rootra, majd beállítjuk rajta a setuid bitet is.
  +
* <tt>ln -s /lib/libc.so.6 /tmp</tt>; majd a libc-t felülírva majdnem minden program a mi kódunkat is tartalmazni fogja, hiszen betöltik a libc6-ot.
  +
* <tt>ln -s /etc /tmp</tt>; így az összes konfigurációs fájlt törölhetjük, majd sajátot hozhatunk létre a helyén, tetszőleges tartalommal. Ez a fenti támadások egy részének az általánosítása.
  +
* <tt>ln -s / /tmp/gyökér</tt>; így végeredményben minden könyvtárat és fájlt helyettesíthetünk sajáttal, mert a gyökérkönyvtár összes elemét jogunk van átnevezni (a mountpointokat kivéve), és az eredeti néven saját fájlt vagy könyvtárat elhelyezni ott. Ez egy további általánosítás.
  +
  +
Sok-sok jó megoldás van még. Ezek közül kellett legalább kettőt leírni a maximális pontszámért.
  +
  +
=== 2009/2. feladat ===
  +
  +
Egy kollégánk több szerveren szeretne egymás után bizonyos parancsokat lefut­tatni. A parancsokat minden szerveren elhelyezte a <tt>/root/bin/somescript</tt> nevű fájlban. A szerverek nevét a <tt>servers.txt</tt> fájl tartalmazza, soronként egyet. Kollégánk a következő egysoros scriptet írta a feladat megoldására:
  +
<pre>
  +
while read server; do ssh $server /root/bin/somescript; done <servers.txt
  +
</pre>
  +
Meglepve tapasztalja, hogy annak ellenére, hogy a <tt>servers.txt</tt> fájl számos sorból áll, a <tt>/root/bin/somescript</tt> csak az első szerveren hajtódik végre, a többin nem. A szerverek minden lényeges szempontból teljesen megegyeznek egymással. Ha a listán egy másikat rak az első helyre, akkor csak azon hajtódik végre a <tt>/root/bin/somescript</tt>. Mi lehet a probléma oka? (4 pont)
  +
  +
Segítségképpen felírtam a táblára, hogy a következő parancssor működik, és azt csinálja, amit gondolunk:
  +
  +
<pre>
  +
echo foo | ssh user@bar cat \>baz
  +
</pre>
  +
  +
'''Megoldás:''' itt arra kellett rájönni, hogy az ssh a kliensprogram standard inputját elérhetővé teszi a szerveren az ssh sessionön át elindított program számára. Azt kellett még végiggondolni, mi történik a fájldeszkriptorokkal az egysoros script végrehajtása során:
  +
  +
# A shell indít egy gyermekfolyamatot.
  +
# Ez a gyermekfolyamat lezárja a standard inputját, majd megnyitja helyette a <tt>servers.txt</tt>-t (tehát minden input ebből a fájlból jön majd a továbbiakban).
  +
# A gyermekfolyamat megkezdi az egysoros script feldolgozását. Lefut a <tt>read</tt> parancs, amely az input első sorát beolvassa a <tt>$server</tt> változóba.
  +
# Az stdin fájldeszkriptorban ekkor az offszet (vagyis a következő olvasás pozíciójára mutató "pointer") a <tt>servers.txt</tt> fájl második sorának elejére mutat.
  +
# Elindul az első <tt>ssh</tt> processz, amelynek az összes fájldeszkriptort, így az stdin-t is, örökli az őt indító shelltől. Vagyis az ő stdin-je is a <tt>servers.txt</tt> fájl, és az offszet továbbra is a második sor eleje.
  +
# Felépül az ssh-kapcsolat, és a szerveren elindul a <tt>/root/bin/somescript</tt>, amely az <tt>sshd</tt>-től örökli az stdin FD-t. Az <tt>sshd</tt> (az ssh-szerverprogram) úgy indítja el a <tt>somescriptet</tt>, hogy egy-egy olyan pipe-ot örököl stdin-ként, stdout-ként és stderr-ként, amelyeknek a másik vége az <tt>sshd</tt> processzé, és a titkosított hálózati kapcsolaton keresztül az <tt>ssh</tt> kliensprogrammal van összekapcsolva. Tehát amit az ssh-szerveren futó program kiír, azt az ssh-kliens megkapja és kiírja a helyi konzolon, és fordítva, amit a szerveren futó program olvas, azt az ssh-kliens standard inputjáról olvassa (ezt tette egyértelművé a táblára felírt segítség).
  +
# Ha minden a terv szerint menne, a <tt>somescript</tt> kilépne, erre az <tt>ssh</tt> is kilépne, a <tt>read</tt> beolvasná a következő sort, és mennénk tovább a következő szerverre.
  +
  +
Ehelyett azonban az történik, hogy a <tt>read</tt> a második alkalommal már nem olvas semmit, és emiatt sikertelen visszatérési értékkel tér vissza; emiatt pedig véget ér a <tt>while</tt>-ciklus (mert a ciklusban maradás feltétele az, hogy a <tt>read</tt> sikerüljön). Ezzel az egysoros scriptnek is vége.
  +
  +
Ha az lenne a baj, hogy nem érjük el a további szervereket, akkor az ssh hibaüzenetet adna és sokáig kellene várnunk, amíg próbál kapcsolódni. Ez aligha lepné meg annyira a kollégát, hogy tanácsot kellene kérnie tőlünk, és fontos körülményként biztosan megemlítené, ha mégis így lenne. Arra sem ad magyarázatot ez az elmélet, miért mindig a lista legelső elemén fut le a script, a többin sosem.
  +
  +
A valódi magyarázat az, hogy a <tt>somescript</tt> tartalmaz valami olyat, ami végigolvassa a standard inputot. Ezt az ssh-kapcsolaton keresztül megteheti; végeredményben elolvassa a <tt>servers.txt</tt> további sorait a későbbi <tt>read</tt> parancsok elől. Nem azért, mert ugyanabból a fájlból olvasnak (természetesen megnyithatja két egymástól független processz ugyanazt a fájlt és mindketten végigolvashatják), hanem azért, mert ugyanazon a fájldeszkriptoron keresztül olvassák a fájlt, így az offszet-mezőn is osztoznak.
  +
  +
Erre csak két hallgató jött rá; ők 4-4 pontot kaptak. Részpontszámot lehetett kapni olyan elméletekért, amelyek viszonylag plauzibilisek voltak, de a valóságban nem pont a leírt (és elmondott) viselkedést eredményezték volna. Nem zárom ki, hogy létezik az itt helyesként bemutatottól eltérő másik helyes válasz is, de a ZH-ban senki nem írt ilyet.
  +
  +
=== 2009/3. feladat ===
  +
  +
Legalább hány 1TB kapacitású diszkre van szükségünk ahhoz, hogy olyan [[Software RAID Linux alatt|linuxos soft-RAID-tömböt]] szervezzünk belőlük, amely legalább 2TB nettó kapacitással rendelkezik, bármely két diszk kiesését az esetleges bithibáktól eltekintve biztosan túléli, hozzáadható egy diszk melegtartalékként és van esély arra, hogy egy harmadik diszk kiesését is túlélje? Hogyan szerveződjön a [[RAID|tömb]]? Ha több minimális számú diszket igénylő értelmes megoldás is van, mindegyiket írja le! (8 pont)
  +
  +
'''Megoldás:''' először gondoljuk végig, hány diszk kell mindenképpen. 1, 2 és 3 triviálisan kevés, hiszen nem élheti túl három diszk kiesését. Négy szintén kevés, mert három kiesése esetén csak egy maradna, azon pedig nem fér el a kívánt 2TB nettó kapacitás. Tehát biztosan kell legalább öt diszk. Kérdés, elég-e öt?
  +
  +
Igen, elég. Több jó megoldás is van (az ezekkel izomorf megoldásokat nem tekintjük különbözőnek):
  +
  +
* raid5(raid1(sda, sdb), raid1(sdc, sdd), sde) (erre volt, aki rájött)
  +
* raid6(sda, sdb, sdc, raid1(sdd, sde)) (erre is volt, aki rájött)
  +
* raid4(raid1(sda, sdb), raid1(sdc, sdd), sde) (az sde a paritásdiszk; erre is volt, aki rájött) (ugyanez működhetne raid4 helyett raid3-mal is)
  +
* Daraboljuk fel mind az öt diszket 2-2 500G-s partícióra, majd: raid5(raid1(sda1, sde2), raid1(sdb1, sda2), raid1(sdc1, sdb2), raid1(sdd1, sdc2), raid1(sde1, sdd2)) – így homogén tömböt kapunk, ami ráadásul garantáltan túléli három diszk kiesését. Erre nem jött rá senki.
  +
  +
Hat diszk evidens, hogy elég; nagyon sok jó megoldás van hat diszkkel (pl. RAID10, mindenből három példányt tárolva). Aki szerint hatnál több diszk a minimum, nem kapott sok pontot.
  +
  +
Aki a fenti jó megoldások közül legalább kettőt megemlített (és a leírás nem tartalmazott súlyos hibát), a nyolcból legalább hét pontot megkapott.

A lap jelenlegi, 2012. december 12., 20:33-kori változata

Az alábbiakban leírom a 2009-ig szerepelt zh-kérdéseket (a frissebbek megtalálhatók az egyes szócikkek alatt), a hozzájuk tartozó helyes válaszokat (ezeket a zh-ban elvárt minimális részletességnél szabatosabban kifejtve), és esetleg néhány, a hallgatók által írt választ, kommentárokkal. Az olvashatatlan szavakat "????" jelöli; a megtippelt, de nem jól olvasható szavakat "????megtippelt_szó????". A helyes válaszokban zárójelben szereplő mondatok csak kiegészítik a választ, nem elvárás, hogy a zh-ban is leírjátok őket.

A megadott pontszámok a zh-ban szereplő pontszámok; elvileg a kérdések relatív nehézségén és fontosságán kívül azt is hivatottak tükrözni, mennyire részletes választ várok el. A későbbiekben a tapasztalatok alapján ezek a pontszámok módosulhatnak, és egy itt leírt négypontos kérdés legközelebb szerepelhet pl. hatpontosként.

Sajnos a zh-k helyesírása és magyarsága több esetben hagyott kívánnivalót maga után. Szerintem helytelenül írni igénytelenség. Ezzel persze nem kötelező egyetérteni, és a pontozásnál sem vettem figyelembe a nem értelemzavaró hibákat, de azért próbáljatok erre is odafigyelni.

Figyelem! A félreértések elkerülése végett: ennek az oldalnak az elolvasása, esetleg bemagolása nem pótolja a segédanyag többi részének elolvasását és megértését. Sajnálatos, hogy erre külön fel kell hívni a figyelmet.

Tartalomjegyzék

[szerkesztés] 1 Unix-alapok

  • Hogyan jut érvényre az, hogy egy felhasználó tagja egy csoportnak? Mi történik a felhasználó futó folyamataival, ha a felhasználó egy csoporttagságát megszüntetjük? (6 pont)
Helyes válasz:
Egy unixos felhasználónak van egy felhasználó-azonosítója (UID) és egy csoport-azonosítója (GID), vagyis egy csoportnak mindenképpen tagja. Ez az elsődleges csoporttagság. Emellett a felhasználó más csoportoknak is tagja lehet; a /etc/group fájlban (vagy az ezt helyettesítő címtárban/adatbázisban) fel van sorolva, hogy az egyes csoportoknak mely felhasználók a tagjai. Amikor a felhasználó belép, az őt beléptető processz a login shell elindítása előtt belép ezekbe a csoportokba is, és ezeket a csoporttagságokat a shell örökli tőle. Így tehát igazából a kiegészítő csoporttagságok halmaza a unixos processz egyik állapotváltozója, a GID pedig egy másik.
Ha egy felhasználót kiveszünk egy csoportból, az a futó folyamatokat sehogyan sem érinti.
Egy válasz a zh-ból (betű szerint):
A felhasználó által birtokolt fájlok révén a fájlrendszerben (Unixban minden fájl) valamint a folyamatok állapotterében (GID, EGID mellett a supplementary - tehát nem elsődleges - csoport is megjelenik.
Alapesetben a felhasználó saját UID-je és elsődleges csoportja ami az általa indított processzhez tartozik, de az effektív - tehát a jogosultságok vizsgálatához felhasznált - EUID/EGID lehet más (UID=0 vagy setuid, setgid bit pl.). Ehhez a felhasználóhoz képest nézi a rendszer, melyik supplementary csoportokba lépjen a processz (setgid/uid ????esetén????).
Nem jut érvényre sehogyan sem, egy felhasználó futó folyamataiban ha megszüntetjük a csoporttagságot. Ellenbel egy rendszerhívással tudja maga a folyamat frissíteni ezeket (pl. mert kapott tőlünk egy jelzést, hogy tegyen így).
Kommentár: A válasz 4 pontot ér; alapvetően jó, de sokmindent összekever, zavaros, részben érthetetlen. Mit jelent az, hogy "ehhez a felhasználóhoz képest nézi a rendszer, melyik supplementary groupokba lépjen a processz"? Setuidos bináris esetén csak az effektív UID állítódik át, a cél-UID supplementary groupjait nem kapja meg az új processz. (Nehéz is lenne megoldani, hiszen a csoporttagságok egy a kernelen kívüli adatbázisban vannak nyilvántartva, ami lehet akár relációs vagy LDAP is, ráadásul csoportnév szerint - a kernelnek a setuidos bináris futtatásakor valahogyan az adatbázisból kéne kinéznie, melyik usernév tartozik az adott uidhez, aztán pedig azt, hogy ez a user milyen csoportoknak tagja még.)

  • Mit jelent a kiegészítő csoporttagság (supplementary group membership)? (4 pont)
Helyes válasz:
Egy unixos felhasználónak van egy felhasználó-azonosítója (UID) és egy csoport-azonosítója (GID), vagyis egy csoportnak mindenképpen tagja. Ez az elsődleges csoporttagság. Emellett a felhasználó más csoportoknak is tagja lehet; a /etc/group fájlban (vagy az ezt helyettesítő címtárban/adatbázisban) fel van sorolva, hogy az egyes csoportoknak mely felhasználók a tagjaik. Amikor a felhasználó belép, az őt beléptető processz a login shell elindítása előtt belép ezekbe a csoportokba is, és ezeket a csoporttagságokat a shell örökli tőle. Így tehát igazából a kiegészítő csoporttagságok halmaza a unixos processz egyik állapotváltozója.
Ha egy processz tagja egy csoportnak, akkor vonatkoznak rá a fájlrendszerben a csoportra vonatkozó jogosultságbitek.
Egy válasz a zh-ból (betű szerint):
az amikor egy felhasználó a /etc/group fileban nem csak a saját group-jában szerepel, hanem egyibb csoportokban is pl: audio,floppy. Így több fájlhoz is hozzáférhet a filok nála groupra vonatkozó rwx csoportnál ezeket a csoport tagságokat is figyelembe veszi. csak a változtatás után indított process-ekre igaz, hiszen igazából nem a felh.nak, hanem a process-eknek van UID-jük és GID-jük.
Kommentár: A válasz 2,5 pontot ér; több súlyos tévedést is tartalmaz, de ezekre szerencsére részben nem vonatkozott a kérdés.
A /etc/group file-ban az egyes csoportok tagjai vannak felsorolva, nem az, hogy az egyes felhasználók mely csoportoknak tagjai (ha így lenne, az utolsó csoporttag törlésével elvesztenénk a csoportot is). A /etc/group amúgy csak a kiegészítő csoporttagságokat tartalmazza, a felhasználó a GID-jához tartozó csoportnál alapértelmezés szerint nincs felsorolva.
Ha egy felhasználót berakunk egy új csoportba, akkor az általa indított új processzek nem kapják meg automatikusan az új csoporttagságot, hiszen a csoporttagságokat a szülőfolyamatuktól öröklik, az pedig nem rendelkezett ezzel a csoporttagsággal. Az új csoporttagságra új login session indításával vagy a newgrp segítségével lehet ténylegesen szert tenni.

[szerkesztés] 2 IP-alapok

  • Hogyan működik a SYN támadás? (8 pont)
Helyes válasz:
A TCP SYN-elárasztás a TCP-kapcsolatfelépítés (handshake) egy sajátosságát használja ki. A kapcsolatot kezdeményező kliens először olyan csomagot küld a szervernek, amelyen a SYN bit be van állítva, egy kezdeti szekvenciaszámmal. A szerver erre egy SYNACK csomaggal válaszol, a saját szekvenciaszámával. Végül a kapcsolat létrejön, amint a kliens visszaküld egy olyan csomagot, amelyben csak az ACK bit van beállítva, és a szerver kezdeti szekvenciaszámát nyugtázza.
Ahhoz azonban, hogy a szerver el tudja dönteni, hogy egy beérkező ACK üzenet egy kapcsolat-felépítés utolsó üzenete-e, meg kell jegyeznie, melyik kliensnek milyen kezdeti szekvenciaszámú SYNACK csomagot küldött. Ezek az adatok a TCP kapcsolatpuffer (TCP backlog-queue) nevű adatstruktúrában tároldónak, amelynek a mérete véges (gyakran portonként csak pár tucat félig nyitott kapcsolat tárolására elegendő).
A SYN-elárasztás során a támadó rengeteg SYN csomagot küld, gyakran hamis IP-címről, ám egyik kapcsolat felépítését sem fejezi be ACK üzenettel, így a szerver puffere megtelik, nem képes új kapcsolatokat fogadni. A pufferben tárolt ún. félig nyitott kapcsolatok egy idő után, ha addig nem érkezik rájuk ACK, törlődnek; ám ha a támadó csomagok gyorsan jönnek, akkor elárasztják a tárolót, és az áldozat nem lesz képes a legtöbb legitim kliens kérésének feldolgozására.
Egy válasz a zh-ból (betű szerint):
A TCP-kapcsolat felépítésének normális menete a SYN - SYN ACK - ACK protokollfutam.
Ezzel kétféleképp lehet visszaélni:
a) Az ún 'SYN flood' támadás során a támadó nagymennyiségű SYN csomaggal árasztja el a célpontot (noha valódi kapcsolatfelépítési szándéka feltehetőleg nincs). Ez a célpontot beéníthatja, lefagyaszthatja, túlterhelheti (különösen az egyszerűbb hálózati eszközöket). DoS támadás.
b) Egyszerű tűzfalak (amelyek nem állapotkövetőek, és csak a felépítést szolgáló csomagokra tartalmaznak szűrőfeltételeket) "becsaphatók", ha - nemlétező - sequence numberrel ellátott ACK csomagot küld a támadó.
A syncookies ezeket a támadásokat hatékonyan képes elhárítani.
Kommentár: a válasz 4 pontot ér. A kérdés az "a)" részben leírt támadásra vonatkozott, ennek az ismertetése azonban nem elég részletes; nem derül ki, mi okozza a túlterhelést, melyik erőforrás fogy el (hogyan különbözik egy SYN-elárasztás egy tetszőleges más csomagfajtával való elárasztástól?).
A "b)" jelű támadásról nem derül ki, mitől "SYN támadás", és hogy mit nyer a támadó azzal, ha egy "nem létező" szekenciaszámú ACK csomaggal "becsapja a tűzfalat", de még az sem, mit jelent az, hogy egy szekvenciaszám nem létezik, vagy hogy az ACK csomag saját szekvenciaszámáról, vagy az általa nyugtázott szekvenciaszámról van szó.
Akárhogy is, a syncookies mechanizmus csak az első ellen "véd" (a szó egy tág értelmében), a második ellen nem.

  • Mire való a PMTU discovery és hogyan működik? (7 pont)
Helyes válasz:
L. az IP-alapok szócikkben; felesleges itt megismételni.
Egy válasz a zh-ból (betű szerint):
Path Maximum Transfer Unit Discovery: A maximális csomagmeret meghatározásár a szolgáló algoritmus, amely arra szolgál, hogy a kapcsolat kezdeményezője megállapítsa, hogy mekkora csomagokat küldhet az adott címzett felé.
A kapcs. kezdeményező fél először egy előre beallított MTU meretű csomagot küld a címzettnek. Ezután a ????visszafelvő???? üzenetek (ICMP) alapján ????binalis???? kereséssel megkeressük az MTU-t. Ha a csomag átmegy, akkor megduplázzuk az előző méretet, ha viszont egy router "Fragmentation needed" üzenetet küld vissza, akkor az (MTUi+MTUi-1)/2-vel próbálkozunk és így tovább ????binalis???? kereséssel. Az IP elvi határa 64k az MTU-ra, de a különböző adatkapcsolati és fizikai rétegek általában 1-2MB (sic!) közötti értékeket engednek meg.
Kommentár: A válasz a sok pontatlanság és egy darab, a Richter-skálán mérhető tévedés miatt csak 3 pontot ér.
1. A PMTU discovery egy TCP-kapcsolat mindkét viszonylatában lejátszható, nemcsak a kliens->szerver irányban.
2. A válasz nem tér ki a DF (Don't Fragment) bit szerepére.
3. A csomagméret megduplázása problémás lehet, hiszen ezzel a saját interfészünk MTU-jánál nagyobb csomagméret is előállhat.
4. Ha a saját interfészünk MTU-ja túl nagy, és ICMP fragmentation needed üzenetet kapunk, nincs í mínusz egyedik MTU, amit felhasználhatnánk, hiszen ez volt az első csomag - akkor viszont hogyan számítsuk ki a másodiknak a méretét?
5. Ha valahogyan eljutottunk a 800 byte-hoz (mondjuk a 400 duplázásával), és a 800 túl nagy, akkor a leírt algoritmus szerint először a 600-zal kellene próbálkoznunk, de ha az még átfér, akkor ezt duplázva, 1200-zal - az viszont több, mint 800, és már a 800 se fért át.
6. Az az 1-2MB nagyon sok lesz...

  • Mire való az ARP? Milyen üzenetei vannak? (5 pont)
Helyes válasz:
Az ARP arra való, hogy az egy fizikai hálózaton levő IP-vel kommunikálni vágyó számítógépek fel tudják deríteni egymás fizikai rétegbeli címét. Etherneten és az Ethernetre elegendően hasonlító hálózatokban használják.
Két üzenete van: a kérdés ("who-has 1.2.3.4 tell 1.2.3.1") és a válasz ("1.2.3.4 is-at 00:f0:0d:d0:0d:00").
(Az ARP nem IP-csomag, így nincs IP-fejléce.)
Adatkapcsolati szinten a kérdés forráscíme annak az állomásnak a címe, amely a választ tudni szeretné; a célcím a broadcast.
A válasz forráscíme annak az állomásnak a címe, amely tudja a választ (ez nem feltétlenül a keresett állomás); a célcím a kérdező állomás címe.
(Az ARP-kérdést elvileg nem muszáj broadcastként feltenni, de ez a leggyakoribb.)
(Az ARP elvileg többféle Ethernet felett szállítható protokollt is támogat.)
(Az ARP-kérdés a payloadban is tartalmazza a kérdező hardvercímét, és igazából erre a címre kell küldeni a választ.)
(Elvileg a RARP-üzenetek is az ARP-hez tartoznak, de nemigen használ ilyet senki.)
(A "who-has" és az "is-at" nevek a tcpdumphoz fűződnek, a szabvány kevésbé szemléletes neveken emlegeti az üzeneteket.)
Egy válasz a zh-ból (betű szerint):
ARP
IP->MAC megfeleltetés
who-has src mac: saját valós mac
dst mac: FF:FF:FF:FF:FF:FF
src IP: saját IP
dst IP: a cél IP-je
is-at src mac: cél saját mac-je
dst mac: saját mac
src IP: cél IP-je
dst IP: saját IP
Kommentár: a válasz 2,5 pontot ér. Súlyos hiba, hogy az ARP-t IP fölötti protokollként képzeli el. Nem írja le továbbá a who-has és az is-at üzenetek tartalmát.

[szerkesztés] 3 DNS

  • Mekkora legyen egy rekord TTL-je? Miért éppen annyi? Miért baj, ha túl nagy, és miért baj, ha túl kicsi? (6 pont)
Helyes válasz:
Egy rekord TTL-je akkora legyen, hogy lehetőleg éppen a következő tervezett módosítás előtt járjon le. Ha nem tudjuk, mikor lesz a következő módosítás, akkor akkora legyen, amennyi ideig vállalható, hogy kliensek esetleg még a rekord régi tartalma szerint járnak el; tehát pl. az előző címén keresik a webszervert.
Ökölszabály: várhatóan lassan változó rekordok TTL-je legyen néhány órás (vagy akár néhány napos). Nem valószínű, hogy gyakran változna pl. egy domain MX-ének a neve, tehát az MX rekord TTL-je akár egy hét is lehet; a névhez tartozó IP viszont talán megváltozhat "váratlanul" (pl. mert átmegyünk másik hosting-szolgáltatóhoz, vagy mert DNS alapú failovert valósítottunk meg), így ennek a TTL-je lehet jóval alacsonyabb (néhány óra, vagy failover esetén akár csak öt perc).
Ha a TTL túl nagy, az azért baj, mert akkor a rekord tartalmának megváltoztatása után sokáig lehetnek olyan kliensek, akik a rekordnak még az előző tartalmát ismerik, és rossz gépnek próbálják kézbesíteni a leveleinket, rossz DNS-szervertől érdeklődnek a hosztneveinkről s.í.t. Ha az ebből adódó problémákat el akarjuk kerülni, akkor a rekord megváltoztatása után még legalább a TTL-ben megadott ideig mindkét címen (az újon és a régin) nyújtanunk kell a szolgáltatást.
Ha a TTL túl kicsi, az azért baj, mert a kliensek csak rövid ideig cache-elik a rekordok tartalmát és feleslegesen gyakran fordulnak a DNS-szerverünkhöz; így a szervernek megnő a terhelése, a kliensek pedig lelassulhatnak (mert pl. két kattintás között megint le kell kérdezni a webszerver IP-címét).
(Van olyan DNS-szerver, pl. a djbdns, amely tervezett DNS-változtatásokat úgy támogat, hogy a megváltoztatandó rekord TTL-jét minden válaszhoz egyenként kiszámítja úgy, hogy éppen a tervezett változtatás pillanatában járjon le.)
Egy válasz a zh-ból (betű szerint):
Általában néhány óra legyen egy DNS rekord TTL-je, ez szabja meg, hogy a kliens ezt meddit cachelheti. Ha túl nagy a TTL azzal az a baj, hogy lehet, hogy a rekord valami miatt megváltozott és erről csak nagyon későn a TTL lejárta után értesül a kliens. Ezért szolgáltatás kiesést okozhat.
Ha túl kicsi azzal az a baj, hogy folyton lekérdezi a kliens ezzel megterheli a DNS szervereket. Ezért optimális a pár óra. Ha nagyobb karbantartást szeretnénk végezni, akkor ????érdemes???? a TTL-t ????1-re 1s-ra???? ????állítani????, megvárni míg a régi ????lejár???? majd ????utána???? elvégezni a karbantartást, ezzel gyakorlatilag 0 lesz a szolgáltatás kiesés.
Kommentár: A válasz 4 pontot ér.
Alapvetően jó, de nem írja le, hogy a néhány óra egy olyan ökölszabály, amitől bizonyos esetekben érdemes eltérni (l. a DNS-alapú failover, ill. a szinte biztosan hosszú életű rekordok esetét).
Csak az ökölszabályt írja le, nem próbálja mérnöki módon megfogalmazni, mi alapján döntsük el, mennyi legyen a TTL.
Nem említi, hogy a szolgáltatáskiesés elkerülhető, ha a régi címen is fenntartjuk a szolgáltatást a TTL lejártáig (ami persze költséges és/vagy lehetetlen lehet).
Nem említi, hogy kliensoldalon is eredményezhet lassulást a túl kicsi TTL.

  • Mi a baj a glueless delegációkkal? Ugyanakkor mi a jó bennük? (10 pont)
Helyes válasz:
Egy delegáció kétféleképpen lehet glueless. Az egyik eset az, amikor olyan szervernek delegálunk egy domaint, amelynek a nevére nem vagyunk autoritatívak (pl. mondjuk a .hu-ra autoritatív szerver az ns.foo.com-nak delegálja a foo.hu domaint). Ebben az esetben a delegáló szerver nem adhatja át glue-ként a delegációban szereplő szerver A rekordját a kliensnek, mivel nincs joga nyilatkozni arról a domainről, amelybe az a szerver tartozik; a .hu nameserver nem autoritatív a .com-ra (elvileg lehetne autoritatív arra is, de ez most mindegy).
A másik eset az, amikor a delegáló szerver adatbázisába szándékosan nem vesszük fel az alárendelt szerver A rekordját annak ellenére, hogy megtehetnénk (mert autoritatívak vagyunk a nevére). Ez elég nagy marhaságnak tűnik; remélem, a valóságban senki nem csinál ilyet.
A baj a glueless delegációval az, hogy a kliensnek újra kell kezdenie a feloldást, esetleg a gyökértől, hogy megtudja a megkérdezendő szerver IP-címét; ha közben újabb glueless delegációval találkozik, akkor megint, s.í.t. Így nem biztos, hogy adható felső korlát egy feloldás memóriaigényére. Ráadásul a glueless-ség körbeérhet, és ebben az esetben a körben szereplő egyetlen domain sem oldható fel.
Pl. ha az a.com nameservere az ns.b.net, a b.net-é az ns.c.hu, a c.hu-é az ns.d.org, a d.org-é pedig az ns.a.com, akkor egyik domain sem elérhető, ha a delegációk glueless-ek, de mire ez kiderül, a rekurzív rezolver elpazarolt egy csomó időt és memóriát.
A glueless delegáció egyetlen "előnye" az, hogy a delegációval kapcsolatos NS rekordot tartalmazó zónába nem kell felvenni a delegációt kapó DNS-szerver A rekordját, így ezt a rekordot karbantartani sem kell, ha megváltozna (ehhez persze viszonylag bonyolult üzleti folyamatok tartozhatnak, úgyhogy nem feltétlenül egyszerű). Ez roppant kényelmes a delegációt végző szerver üzemeltetője számára, csak mindenki mással kitolás.
Hogy a mi domainjeinkre mutató delegációk gluelessek-e, azt mi magunk is befolyásolhatjuk; ha ügyelünk arra, hogy a domainre autoritatív szerver neve a domainen belül legyen (tehát a foo.com nameserverének neve a foo.com domainbe tartozzon, mondjuk ns.foo.com vagy a.ns.foo.com), akkor adunk esélyt a glue-nak.
Egy válasz a zh-ból (betű szerint):
A DNS-szerver a kérdezőnek delegáláskor a glue-ban átadhatja egy a keresett domainnel azonos zónába eső nameserver IP címét. Ez jó, mert így nem kell külön feloldani a nameserver ????nevét????, növekszik a DNS hatékonysága. Pl. a pool-foobar.axelero.hu megkérdezi a .hu zónáért felelős nameservert, hogy mi a www.bme.hu IP címe. A szerver válaszol, hogy ő nem authoritatív, de kérdezze meg az ns.bme.hu-t. Ilyenkor a kliensnek fel kellene oldani az ns.bme.hu-t is, ezt azonban a nameserver átadhatja glue-ban. Ha pl. a www.bme.edu-t próbáljuk meg feloldani, és a .hu-ért felelős name servertől kapjuk meg az ns.bme.edu IP címét, azt nem hisszük el neki.
A glueless delegációk rontják a DNS hatékonyságát, jó bennük, hogy pl. minden további nélkül megváltoztathatjuk a name serverünk IP címért, mert mindig feloldásra kerül a neve. Ez az előny elenyésző, érdemes használni a glue-t.
Kommentár: a válasz 9 pontot ér. Hibák:
A .hu zónáért felelős szerver természetesen autoritatív minden, a .hu alatti névre, így a www.bme.hu-ra is. Nem azt válaszolja az erre vonatkozó kérdésre, hogy nem autoritatív (ilyen válasz a DNS-ben gyakorlatilag nem is lehetséges), hanem egy delegációt tartalmazó választ küld.
Ha a .hu zónáért felelős szerver egy másik, .hu alatti szervernek delegál egy zónát, akkor annak a címét illik is átadnia glue-ban, hiszen kitől kérdezhetnénk meg, ha a .hu szerverek nem mondják meg?
A válasz első fele részben redundáns volt; nem volt kérdés, mi a glue (de persze inkább írjatok többet, mint kevesebbet). Ugyanakkor ez a bevezető győzött meg arról, hogy a hallgató valószínűleg érti, miért rontja a glueless delegáció a DNS hatékonyságát, mert azt külön nem írta le.

  • Mi a DNS poisoning, hogyan működik és hogyan lehet ellene védekezni? (12 pont)
Helyes válasz:
Legalább háromféle DNS poisoningot érdemes megkülönböztetni. Az egyik esetben a támadó az általunk megszólított nameserver helyett válaszol, és a válasz tartalmával mérgezi meg a cache-ünket (ezt akkor tudja könnyen megtenni, ha le tudja hallgatni a kérdést). Ezt a támadást DNS spoofingnak vagy DNS forgerynek (hamisításnak) is nevezik.
A másik eset az, amikor a támadónak van egy domainje, mondjuk a foo.bar, és ennek valamelyik szubdomainjét delegálja - mondjuk - az ns.bme.hu-nak: ize.foo.bar. NS ns.bme.hu., és ehhez a válaszhoz glue-ként mellékeli az ns.bme.hu állítólagos IP-címét, de nem a valódit, hanem egy olyan címet, amit ő ellenőriz: ns.bme.hu. A 1.2.3.4 , ahol 1.2.3.4 a támadó szervere. Ha a kliensszoftver hibás, és elfogadja a glue-t annak ellenére, hogy a foo.bar nameserver nem autoritatív a bme.hu domainre (és így nincs joga nyilatkozni az ns.bme.hu IP-jéről), akkor a cache-ben a támadó által megadott - vélhetően jó hosszú - TTL lejártáig tárolja, hogy az ns.bme.hu IP-je 1.2.3.4. Ezután, ha ez a kliens olyan domainhez tartozó nevet próbálna feloldani, amire az ns.bme.hu autoritatív, az 1.2.3.4-hez fog fordulni, hiszen "tudja", hogy az ns.bme.hu IP-je ez. Így a támadó az ilyen módon "eltérített" szervernek szóló összes kérdésre adott választ ellenőrizheti, átírhatja.
Ennek a támadásnak egy variánsa az, ha irreleváns Authority sectiont rak a támadó a válaszába, amiben pl. azt mondja, hogy bme.hu. NS ns.foo.bar., holott ezt nem is kérdezte senki.
A harmadik fajta támadás lényege az, hogy a támadó man-in-the-middle támadást valósít meg egy primary és egy secondary nameserver között (vagy egyéb módon eléri, hogy a secondary őhozzá forduljon AXFR-rel a primary helyett), és az AXFR-rel átvitt adatokat "röptükben" átírja. Így a secondaryn a támadó által elhelyezett hamis adatok lesznek, úgyhogy azok a kliensek, akik a secondaryhoz fordulnak, a hamis válaszokat fogják megkapni. Ha a primaryt a támadó valahogyan ki tudja vonni a forgalomból, akkor minden kliens a hamis adatokat látja.
Az első támadás ellen gyakorlatilag nem lehet védekezni (nem húzhatunk ki VPN-t a világ összes DNS-szerveréhez). A saját ellenőrzésünk alá tartozó szakaszon megnehezíthetjük a lehallgatást, de nagyjából ennyi. A lehallgatásra amúgy azért van szükség, mert minden DNS-kérés tartalmaz egy 16 bites azonosítót (amit amúgy nonce-nak hívnak), aminek a válaszban is szerepelnie kell; ha a támadó nem tudja, milyen nonce-szal ment ki a kérés, meg kell tippelnie, mit írjon a válaszba. Ha emellett a kérés forrásportja is véletlenszerű, összesen kb. 31-32 bitnyi véletlenséget (azért nem 32, mert jó pár forrásport kizárható) tudunk vinni a kérésbe, úgyhogy a vak tippelés nehézzé válik.
A második támadás ellen úgy védekezhetünk, hogy nem fogadunk el olyan glue-t, amelyre az a szerver, amelytől kapjuk, nem autoritatív; a példában az ns.bme.hu IP-címét a gyökértől indulva külön meg kellett volna keresni a "hagyományos" feloldóalgoritmussal.
Természetesen abban az esetben, ha a támadó szervere valóban autoritatív a .hu-ra vagy a .bme.hu-ra, azt hazudik az ns.bme.hu IP-jével kapcsolatban, amit csak akar; sőt, mivel ő mondja, definíció szerint az lesz az igazság. Ezért veszélyes, ha egy a DNS-hierarchiában "magas szinten álló" nameserverre betör valaki. Ez ellen max. úgy védekezhetünk, ha megpróbáljuk az összes választ az összes, rá autoritatív szervertől beszerezni, aztán pl. többségi szavazással eldöntjük, melyik választ fogadjuk el. Ez elég lassú lenne.
A harmadik támadás ellen a saját szervereink esetében úgy védekezhetünk, hogy biztonságos VPN-t húzunk ki a két szerver között, és efölött AXFR-ezünk, vagy AXFR helyett valamilyen más, biztonságos, hitelesített átviteli módszert alkalmazunk (pl. rsync over ssh). Más szerveren így elhelyezett hamis adatok ellen gyakorlatilag nem tudunk védekezni (max. a többségi szavazásos módszerrel, de az nem hatékony és nem is 100%-os).
Érdemes a Wikipédián utánaolvasni, ha nem lenne világos.
(Ennyire részletesen nem kell leírni a válaszban.)
Egy válasz a zh-ból (betű szerint):
Ha egy támadó man-in-the-middle szerűen ???? be tud épülni egy kliens és egy DNS szerver közé, vagy két DNS szerver közé, akkor elcsípheti a DNS lekérdezéseket és meghamisíthatja a válaszokat.
De ha például a támadó egy kiszemelt áldozatnál azt szeretné elérni, hogy a www.otp.hu címre az ő "phishing" oldala jöjjön be, akkor ezt sokszor beékelődés nélkül is megteheti, hiszen a DNS protokoll a kapcsolatnélküli UDP felett működik, tehát a támadónak csak folyamatosan, rövid időközönként "DNS Reply" válaszüzeneteket kell küldenie az áldozatnak, az eredeti DNS szerver nevében. Ezt UDP esetén egyszerűen megteheti, hiszen csak az IP fejléc source IP mezejét kell a DNS szerver IP-jére állítania, és a DNS üzenet tartalma pedig az, hogy a www.otp.hu IP címe a támadó szerverére mutat. Ilyenkor ha az áldozat egy DNS lekérdezést küld az otp.hu-ra és a támadó válaszcsomagja érkezik meg hamarabb, ????akkor???? ????máris???? sikeres volta "poisoning".
Védekezési lehetőségek:
* Minden lekérdezés tartalmaz egy nounce-ot, ami a válaszban is szerepel.
* DNS protokoll TCP feletti használata
* A DNS szerver hitelesíti magát, pl. PKI kiépítése esetén a válaszokban a kliens által küldött nounce a szerver titkos kulcsával le van titkosítva. (Nyilv. kulcsú digitális aláírás)
Kommentár: a válasz 6 pontot ér.
Nem tér ki a poisoning többféle módozatára, főleg a glue-alapú módszer leírása hiányzik.
Nem magyarázza el, mi a nonce (nem nounce, de ez mindegy), és hogyan segít a védekezésben.
Nem derül ki, miért jó a támadónak, ha két DNS-szerver közé épül be, mint man in the middle (talán az AXFR-es támadásra gondolt a hallgató, de nem írta le).

  • Mit jelent a DNS-ben a "classless" delegáció? Mikor használnak classless ("de Groot") delegációt és miért? Mutasson példát classless delegációra! (10 pont)
Helyes válasz:
A reverse DNS (PTR rekordok rendszere) megalkotásakor A, B és C osztályú címekben gondolkodtak.
A reverse DNS gyökere az in-addr.arpa. zóna; ennek a szubdomainjeit delegálják az egyes IP-tartományok üzemeltetőinek, hogy reverse DNS szolgáltatást tudjanak nyújtani. A DNS-ben a pont karakter választja el egymástól a hierarchia szintjeit; ha tehát pl. a 4.0.0.0/8-as hálózat teljes reverse-tartományát akarjuk delegálni egy nameservernek, akkor valami ilyesmit írhatunk az in-addr.arpa. zónafájljába: "4.in-addr.arpa. NS ns1.Level3.net.". Hasonlóképpen, ha a 152.66.0.0/16-os hálózat reverse delegációját nézzük, akkor egy "66.152.in-addr.arpa. NS ns.bme.hu." jellegű bejegyzést találunk majd a 152.in-addr.arpa. zónafájljában.
Látható, hogy a reverse delegációnak ez a módja csak nyolccal osztható netmaskú alhálózatra működik; ha pl. a 172.16.0.0/12-höz tartozó reverse domaint akarnánk delegálni, gondban lennénk, mert a 16.172.in-addr.arpa túl szűk (a /12-es tartomány a 172.16.0.0-172.31.255.255 címeket foglalja magában), a 172.in-addr.arpa pedig túl tág.
Kézenfekvő, hogy delegáljuk egyenként az összes reverse zónát 16.172.in-addr.arpától 31.172.in-addr.arpáig; ez 16 db NS rekord felvételét jelentené. A módszer kifogástalanul működik is, csak az a hátránya, hogy ha a delegációt fogadó, tehát a reverse zonáért ténylegesen felelős szerveren BIND, vagy egy BINDra hasonlítani vágyó program fut, akkor mind a 16 "önálló" domainhez külön zónafájlt kell létrehozni, és ezekre hivatkozni kell a konfigurációs fájlban. Ez nyilván olyan rettenetes megterhelést jelent, hogy mindenáron el kell kerülni; ezt a célt szolgálja a "classless" delegáció (amúgy érdemes elolvasni az RFC2317-et, akad benne marhaság a javából).
(A BIND-felhasználók számára amúgy még rosszabb lenne a helyzet, ha pl. egy /25-ös hálózat reverse zónáját delegálná nekik valaki 128 darab NS rekorddal: 128 darab zónafájlt kellene létrehozniuk. Természetes, hogy egy szoftver tervezési hibáit egy új szabvánnyal kell maszkolni; ezért született meg az RFC2317. Nyilván a véletlen műve, hogy az RFC egyik szerzője az a Paul Vixie, aki a BIND-ot jegyző Internet Software Consortium munkatársa is. A sorok között ne olvassatok, oda nem írtam semmit! :)
Példa. Tegyük fel, hogy az 1.2.3.4-es és az 1.2.3.5-ös IP-cím a miénk; a nameserverünk az ns.foo.com. Ez ugye az 1.2.3.4/31-es hálózat. "Classful" delegáció esetén két darab NS rekorddal intéznénk el a delegációt: "4.3.2.1.in-addr.arpa. NS ns.foo.com." ill. "5.3.2.1.in-addr.arpa. NS ns.foo.com." (sőt, még jobb lenne, ha nem glueless delegáció lenne, tehát mondjuk az ns.4.3.2.1.in-addr.arpa nevet viselné a nameserverünk). Classless delegáció esetén ugyanez úgy történik, hogy felveszünk egy darab NS-rekordot így: "4/31.3.2.1.in-addr.arpa. NS ns.foo.com." - ezzel létrehoztunk egy új zónát, a 4/31.3.2.1.in-addr.arpa nevűt; most már csak az a dolgunk, hogy az egyes PTR-rekordokat áthelyezzük ebbe a zónába, ezt pedig két CNAME rekorddal lehet megtenni: "4.3.2.1.in-addr.arpa. CNAME 4.4/31.3.2.1.in-addr.arpa." és "5.3.2.1.in-addr.arpa. CNAME 5.4/31.3.2.1.in-addr.arpa.".
Így a DNS-kliens, amikor a 4.3.2.1 PTR rekordját keresi, megtalálja a 4.4/31.3.2.1.in-addr.arpára mutató CNAME-et, úgyhogy megpróbálja a 4.4/31.3.2.1.in-addr.arpa PTR rekordját feloldani, erre pedig a delegációt fogadó szerver a keresett PTR rekorddal válaszol. Egyszerű, nem?
Note to self: Erre kéne részletes példa, mindkét szerver zónafájljával, a DNS-ről szóló oldalra. Bár az RFC-ben is megvan és DJB oldalán is.
Egy válasz a zh-ból (betű szerint):
Classless delegáció: ha egy DNS nem egy teljes A, B vagy C tartományt fed le.
Delegáció: ha az adott DNS szerver "tudja" a választ, de a klienst továbbirányítja a megfelelő authoritatív szerverhez.
Kommentár: a válasz sajnos csak 1 pontot ér. Csak a kérdés legegyszerűbb részére válaszolt ("mit jelent a classless delegáció?"), és arra is pontatlanul. Mit jelent az, hogy "egy DNS nem egy teljes A, B vagy C tartományt fed le"? Több DNS van? És pl. a BME nameserverei "nem fednek le" egy teljes A-osztályú hálózatot - akkor ez most classless delegáció? Ha igen, akkor ki delegál kinek mit classless módon? Ha nem, miért nem? Nyilván nem kellett volna ugyanolyan részletesen leírni, mint ahogy én leírtam feljebb, de annak ki kellett volna derülnie, hogy mi a "trükk" benne (a CNAME és a példában a 4/31.3.2.1.in-addr.arpa zóna).

  • Mit jelent a DNS-ben a delegáció? Mondjon példát! (6 pont)
Helyes válasz:
Delegációnak - a kliens szemszögéből - azt nevezzük, amikor egy DNS-szerver válaszában nincs Answer section, csak olyan Authority section, amiben a kérdésben szereplő domainre, vagy egy a hierarchiában fölötte álló domainre vonatkozó NS rekordok vannak. Ebből a kliens megtudja, hogy melyik másik DNS-szervernek kell feltennie a kérdést a végső válasz reményében.
A DNS-adminisztrátor szemszögéből a delegáció az, amikor szubdomaint definiál, és ennek a karbantartását rábízza valaki másra: a szubdomainhez tartozó NS-rekordban megadott szerver üzemeltetőjére.
A DNS egésze szempontjából a delegáció az, ami hierarchikussá teszi a DNS-t, mint adatbázist. Elvileg a root szerverek minden névre autoritatívak, a valóságban azonban - a delegációnak köszönhetően - nem kell minden név összes rekordját ismerniük, hanem elegendő azt tudniuk, hogy a TLD-kre (top-level domainekre) mely szerverek autoritatívak; vagyis a TLD-ket delegálják azoknak a szervereknek, amelyek az egyes TLD-kre autoritatívak, s.í.t.
Példa: Ha valamelyik, a hu. zónára autoritatív szervertől megkérdezzük, mi a www.bme.hu domain A rekordja (ne feledjük, a DNS-ben minden domain, a "hosztnevek" is), a válaszban nem lesz Answer section (vagyis a keresett A rekord), de lesz olyan Authority section, amiben fel vannak sorolva a bme.hu domainre autoritatív szerverek (és lehet egy Additional section, amelyben ezek közül azoknak, amik szintén .hu alattiak - vagyis, véletlenül, az összesnek - benne lesz az A és/vagy AAAA rekordja is - ez a glue).
Példa az adminisztrátor szemszögéből: ha a hu. zónafájlba azt írjuk, hogy "bme.hu. NS ns.bme.hu.", akkor az ns.bme.hu-nak delegáltuk a bme.hu domainnel kapcsolatos kérdéseket.
Egy válasz a zh-ból (betű szerint):
Delegáció: ha csak azt mondjuk meg egy DNS-kérésre, hogy merre kell tovább tapogatózni a cím után.
Pl. ha a www.bme.hu keresésekor a *.hu-ra autoritatív DNS szerver címét kapjuk válaszként
Kommentár: a válasz 5 pontot ér. A delegációt, mint láttuk, többféle szemszögből meg lehet közelíteni; a hallgató a kliens oldaláról közelítette meg, és a lényeges információk többségét leírta. Hiányzik azonban, hogy kitől (ti. a root szervertől) fogjuk megkapni a .hu-ra autoritatív szerverek nevét (a címüket csak glue-ként). Ezek a pontatlanságok önmagukban nem súlyosak, de összesen indokolják egy pont levonását.

  • Mit jelent a DNS-ben az NXDOMAIN? (5 pont)
Helyes válasz:
Ha egy kérdésre azt a választ kapjuk, hogy NXDOMAIN, az azt jelenti, hogy sem a keresett domainnek, sem egyetlen aldomainjének sem a keresett típusú, sem más típusú rekordja nincs. Csak olyan szerver jogosult NXDOMAINnel válaszolni, amely autoritatív a keresett domainre; ha egy szerver csak autoritatív kérdésekre válaszol, és olyan domainről kérdezik, amire nem autoritatív, akkor nem válaszolhat NXDOMAINt, hanem csöndben kell maradnia (mert a DNS-ben nincs olyan válasz, hogy "nem vagyok autoritatív").
Egy válasz a zh-ból (betű szerint):
A DNS szerver biztosan állítja, hogy sem a kérdezett domén, sem annak aldoméne nem létezik. Ez nem egyenértékű azzal, hogy a DNS szerver nem ismeri a domént vagy nem autoritatív rá.
Kommentár: a válasz 4 pontot ér, mert nem fejti ki, mit jelent az, hogy egy domain "nem létezik" (ti. azt, hogy semmilyen típusú rekordja nincs), pedig igazából éppen ez volt a kérdés; az, hogy az NXDOMAIN a "domain" és a "(does) not exist" szavak valamilyen permutációjának rövidítése, elég evidens.

  • Mit jelent az, hogy egy DNS-szerver autoritatív egy domainre? Mitől válik azzá? Ki határoz erről? (10 pont)
Helyes válasz:
Elsősorban azt jelenti, hogy a mindenki által ismert gyökérszerverektől elindulva az adott domainre vonatkozó kérdések feloldása során delegációs lánc vezet az adott DNS-szerverhez. Például az ns.bme.hu azért autoritatív a bme.hu domainre, mert azok a szerverek, amelyek a root szerverek szerint autoritatívak a .hu-ra, a .bme.hu-t (többek között) az ns.bme.hu-nak delegálják. Vagyis az autoritativitás "felülről érkezik", a DNS-hierarchiában "feljebb található" szerver nyilatkozata alapján válik egy "alacsonyabb szintű" szerver autoritatívvá egy domainre. Úgy is mondhatnánk, hogy a root szerverek a saját autoritativitásuk egyes partícióit adják tovább a delegációk mentén a TLD-kre autoritatív szervereknek, és így tovább. Egy domainre csak olyan szerver tehet minket autoritatívvá, amely maga is autoritatív rá (pl. a tmit.bme.hu-t csak a .bme.hu-ra, a .hu-ra vagy a gyökérzónára autoritatív szerver delegálhatja az alpha.tmit.bme.hu-nak, az ns.whitehouse.gov nem).
Azt is fontos érteni, hogy a root szerverek minden domainre autoritatívak; a .hu szerverek (pl. az ns.nic.hu) minden, a .hu alatti domainre autoritatívak (tehát pl. az alpha.tmit.bme.hu vagy az ez.egy.nagyon.mely.domain.nev.hu IP-címéről is jogosultak nyilatkozni); a delegált domain nem kerül ki abból a halmazból, amelyre a szerver autoritatív. Tehát attól, hogy az ns.nic.hu az ns.bme.hu-t egy delegáció révén autoritatívvá teszi a .bme.hu-ra, még ő maga is autoritatív marad a .bme.hu domainre.
Az autoritativitás másik kritériuma az, hogy a szerver saját magát autoritatívnak gondolja egy domainre; ez beállítás kérdése.
A "ki határoz erről?" kérdésre a technikai válasz az, hogy a hierarchiában feljebb található domain karbantartója; jogilag természetesen ennél bonyolultabb a helyzet, de ez most nem érdekel minket.
A "mit jelent?" kérdésre adott válaszhoz hozzá tartozik az is, hogy az adott szerver a saját adatbázisából képes megválaszolni minden, az adott domaint érintő kérdést (és ebből a szempontból a delegáció is válasznak számít); jogosult pl. úgy nyilatkozni, hogy a keresett név nem létezik (NXDOMAIN, l. feljebb).
Egy válasz a zh-ból (betű szerint):
Az autoritatív DNS-szerver azt jelenti, hogy az adott domainre vonatkozó név-IPcím megfeleltetésekért ő a "felelős": a kérések feloldásában az autoritatív DNS-szerver a hiteles válaszadó.
A hierarchikus DNS-szervezésben alapvetően két fontos tényező tesz autoritatívvá egy DNS-szervert:
a) "Gondolja magát annak" (Elvileg nem célszerű - nem szép dolog - olyan kéréseket kiszolgálni, amire nézve nem autoritatív a szerver)
b) A hierarchiában felette álló szerverek delegálják az adott szervernek a vonatkozó domainre vonatkozó jogokat
Kommentár: a válasz csak 8,5 pontot ér a következő két hiba miatt:
1. A DNS-ben nemcsak név-IPcím-párokat tárolunk, vannak más rekordtípusok is (-0,5 pont).
2. Dehogynem célszerű olyan kéréseket kiszolgálni, amire nézve nem autoritatív a szerver - éppen ezt csinálja a rekurzív rezolver, amit sok implementáció egybeépít az autoritatív szerverrel. Ami nem szép dolog, az az, ha úgy állítjuk be a szervert, hogy olyan domainre is autoritatívnak gondolja magát, amit nem delegálnak neki a felsőbb szintű szerverek. (-1 pont)

[szerkesztés] 4 Az SMTP működése

  • Írja le egy tipikus SMTP-tranzakció menetét! Milyen üzeneteket küld a kliens, és milyeneket a szerver? (6 pont)
Helyes válasz:
Itt említeni kellett a HELO, a MAIL FROM, az RCPT TO, a DATA és a QUIT parancsot; röviden a levél felépítését (fejléc; üres sor; törzs; egyetlen pontból álló sor); és a szerver válaszainak felépítését (háromjegyű szám, az első jegy jelentése stb.). Bővebben l. Az SMTP működése c. szócikkben.
Egy válasz a zh-ból (betű szerint):
C->S: HELO valaki.valahol.hu.
* ESMTP esetén HELO helyett EHLO
* A kliens megadja a domain nevét
S-C: HELO [...]
A szerver köszönti a klienst, és megadja a paramétereit (név, típus, verzió, használható parancsok, stb...)
C->S: MAIL FROM küldő@e-mail.címe.hu
* A kliens megadja a küldő e-mail címét
* Az "e-mail.címe.hu" jóesetben "valaki.valahol.hu"
C->S: MAIL TO címzett@e-mail.címe.hu
C->S: DATA [...]
Itt küldi el a tényleges üzenetet.
Az üzenet végét a ".<ENTER>" jelzi.
S->C: Message received üzenet, ha minden rendben.
Kommentár: a válasz csak 2 pontot ér, mert nem a valóságban használt SMTP-t, hanem egy hasonló, de alternatív protokollt ír le.
Az első üzenetet a szerver küldi, egy SMTP banner formájában: pl. 220 chardonnay.math.bme.hu ESMTP.
A kliens a HELO-üzenetben nem a domain-nevét adja meg, hanem a hosztnevét, vagy, ha ilyen nincs neki, akkor az IP címét.
A szerver sosem küld HELO üzenetet a kliensnek; mindig három számból álló kódokkal jelzi, hogy az előző parancsról mi a véleménye.
A szerver általában nem árulja el a saját verziószámát, nehogy ezzel feleslegesen megkönnyítse egy esetleges támadó dolgát.
A MAIL FROM-ban megadott feladó domainje semmilyen kapcsolatban nem kell, hogy álljon a HELO-üzenetben megadott hosztnévvel - pl. a saját másodlagos MX-ünk nyilván a saját nevén mutatkozik majd be, viszont az eredeti feladó nevében küldi nekünk a levelet.
MAIL TO helyett RCPT TO van az SMTP-ben.
A tényleges üzenet felépítése hiányzik.
A QUIT parancs hiányzik.

  • Milyen módokon (nem mi alapján!) utasíthat el egy SMTP-szerver egy nem kívánt levelet? Melyiknek mi a következménye a levél későbbi sorsát illetően? (6 pont)
Helyes válasz:
- A TCP-kapcsolat elutasításával. A kliens (ha valódi levelezőszerver) egy darabig újra meg fogja kísérelni kézbesíteni a levelet; ha hosszú ideig nem sikerül, feladja, és hibaüzenetet küld a feladónak.
- A TCP-kapcsolat bontásával az SMTP-kapcsolat során, a levél átvétele előtt. A kliens (ha valódi levelezőszerver) egy darabig újra meg fogja kísérelni kézbesíteni a levelet; ha hosszú ideig nem sikerül, feladja, és hibaüzenetet küld a feladónak.
- Kliensoldali időtúllépés szándékos kiprovokálásával az SMTP-kapcsolat során (egyszerűen sokáig nem válaszol). A kliens (ha valódi levelezőszerver) egy darabig újra meg fogja kísérelni kézbesíteni a levelet; ha hosszú ideig nem sikerül, feladja, és hibaüzenetet küld a feladónak.
- Az SMTP-tranzakció során, "ideiglenes" (négyessel kezdődő) válaszkóddal. A kliens (ha valódi levelezőszerver) egy darabig újra meg fogja kísérelni kézbesíteni a levelet; ha hosszú ideig nem sikerül, feladja, és hibaüzenetet küld a feladónak.
- Az SMTP-tranzakció során, "végleges" (ötössel kezdődő) válaszkóddal. A kliens a levelet kézbesíthetetlennek tekinti és hibaüznetet küld a feladónak.
- (Minden ettől eltérő viselkedés - pl. ha átveszi a levelet, de nem kézbesíti, csak hibaüzenetet küld a feladónak, vagy még azt sem - a levél elfogadásának minősül az SMTP kontextusában.)
Egy válasz a zh-ból (betű szerint):
- nem épít fel a másik szerverrel TCP kapcsolatot
következmény: ha spammer vsz. nem próbálkozik újra ma már ez sajnos nem igaz.
ha nem spammer akkor újra próbálkozik kicsit lelassítja a valódi leevlek kézbesítését
- átmenetileg vissza utasítja
köv: 4xy hibakóddal átmenetileg visszautasitja a levél átvételét, késöbb lehet próbálkozni
- permanensen visszautasítja
köv: 5xy hibakóddal végérvényesen visszautasítja a levél átvételét, nem érdemes többszörprobálkozni
Kommentár: a válasz 6 pontot ér annak ellenére, hogy keveri a következményeket a módszerrekkel (jóra gondolt, de rosszat írt). Felsorolta a három legelterjedtebb módszert. Kicsit hiányzik, hogy mi történik, ha a későbbi próbálkozások is hasonlóan sikertelenek, de az SMTP-hibaüzenetek annyira közismertek, hogy bizonyára mindenki tudja, hogy ilyenkor hibaüzenet jön.

  • Mit tehetünk a spam elleni védekezés érdekében a HELO sor átvételekor? Az egyes módszereket értékelje is! (10 pont)
Helyes válasz:
Lásd az SMTP működéséről szóló szócikkben.
Egy válasz a zh-ból (betű szerint):
- megvizsgálhatjuk, létező hostnév-e a HELO argumentuma tehát van-e ennek megfelelő A vagy MX rekord
+ elvileg jogos, SMTP, ESMTP-ben szerepel, hogy saját hosztnév adandó meg
- gyakorlatilag sok MTA nem ezt küldi el HELO-ként
- önmagában nem nyújt védelmet (könnyű létező hosztnevet megadni)
- megnézhetjük a HELO argumentum létező hosztnév-e, és ennek reverse DNS-e megegyezik-e a TCP kapcsolat végpontjának IP címével (vagy legalább a DNS feloldásával megkapjuk-e az adott IP-t)
- nem jogos feltételezés, bár nem okoz nehézséget így konfigurálni egy ????szervert????
- nem biztos, hogy törődnek a reverse DNS-sel (nem spammerek)
- valamiféle heurisztika/feketelista alapján megnézhetjük, "spammer-gyanús"-e a HELO argumentuma
- nem túl megbízható/kérdéses a megbízhatóság
Kommentár: a válasz 9 pontot ér. Nem említi meg, hogy a HELO-ellenőrzés eredményét is figyelembe lehet venni a levél pontozásakor. A HELO-feketelista megbízhatósága amúgy igen nagy lehet; pl. nem túl valószínű, hogy legitim távoli kliens "localhost"-ként mutatkozna be.

[szerkesztés] 5 A HTTP működése

  • Milyen problémával kell számolnunk, ha HTTP virtualhostingot használunk, és SSL-re is szükségünk van? Milyen megoldásai vannak a problémának? (6 pont)
Helyes válasz:
A HTTP virtualhosting működési elve az, hogy a HTTP Host: fejlécében megmondjuk a webszervernek, melyik virtuális szerver címterében kell értelmezni a kérésben átadott elérési utat és fájlnevet. Ha HTTPS-t használunk, akkor a böngésző és a webszerver közötti TCP-kapcsolat felépülése után először az SSL handshake-re kerül sor; ennek során a böngésző hitelesíti a szervert, és többek között azt is ellenőrzi, hogy a szerver tanúsítványa az URL-ben szereplő hosztnévre szól-e.
A gond az, hogy a szerver ebben a pillanatban még nem tudja, hogy mi az URL, így, ha virtuális hosztokat használunk, nem tudja eldönteni, melyik virtuális hoszt tanúsítványával hitelesítse magát a böngésző felé; ha nem a jót választja, a böngésző hibaüzenetet ad (vagy az alkalmazásszintű proxy megtagadja a webszájthoz való hozzáférést). A szerver csak az SSL-handshake után, a HTTP-tranzakcióból tudja meg (a Host: fejlécből), hogy melyik virtualhostra kíváncsi a kliens.
Négy megoldás van: az egyik az, hogy minden HTTPS-es virtualhostot külön IP:port páron futtatunk, így a szerver abból, hogy melyik IP:portra jött kapcsolatkérés, meg tudja állapítani, melyik virtualhostot szólítják meg éppen, és így azt is tudja, melyik tanúsítvánnyal kell hitelesítenie magát.
A második megoldást csak akkor használhatjuk, ha minden virtualhostunk ugyanabba a domainbe tartozik, és a hitelesítő hatóságunk (CA, certificate authority) hajlandó nekünk joker-tanúsítványt (wildcard cert) adni erre a domainre; ekkor a tanúsítvány elvileg minden, az adott domainbe tartozó konkrét hosztnévre érvényes lesz, tehát a szerver nem kerül olyan dilemma elé, hogy több tanúsítvány közül kellene választania, vagyis az összes virtualhost maradhat ugyanazon az IP:port páron, hanem hitelesítheti magát azzal az eggyel, ami van. A módszer hátránya, hogy nem minden CA ad ki joker-tanúsítványt, és elképzelhető, hogy nem minden böngésző fogadja el.
A harmadik megoldás az SNI, aminek az a lényege, hogy a kliens még az SSL handshake előtt jelzi a szervernek, milyen névre szóló tanúsítványt vár.
A negyedik megoldás az, hogy a tanúsítványban a Subject Alternative Name mezőben felsoroljuk az összes virtualhost nevét (a felsorolás tartalmazhat jokereket is).
Egy válasz a zh-ból (betű szerint):
Az a probléma, hogy a kliens elküldi a HTTP-kérésben a kért weboldal címét, ekkorra azonban már túl kéne esni az SSL-handshake-en, amihez kéne tudni a kért oldal címét (hogy melyikhez tartozó tanúsítványt használjuk). Megoldás:
1. különböző portra kell tenni a virtuális HTTPS-szervereket és port alapján tudjuk, melyik cert. kell
2. a domain-re vonatkozó cert. kell
Kommentár: A válasz 3 pontot ér. Fél pont levonás jár azért, mert nem fejti ki, mi "a domain" (impliciten feltételezi, hogy minden virtualhost ugyanabba a domainbe tartozik), és másik fél pont levonás azért, mert nemcsak külön portra, hanem külön IP-re is tehetjük az egyes virtuális szervereket; az IP:port párnak kell különböznie. Két pont levonás jár az SNI kihagyásáért.

[szerkesztés] 6 Filerendszerek

  • Fájlrendszerek kontextusában mi a fork? (3 pont)
Helyes válasz:
Ha a fájlrendszer lehetővé teszi, hogy egyetlen fájlhoz több, a fájlrendszer szintjén elkülönülő adatterület is tartozzon, akkor ezen adatterületek mindegyike egy-egy fork.
(A fájl érdemi tartalma a default fork; egyéb forkok tartalmazhatnak pl. bővített attribútumokat, POSIX ACL-eket, kép thumbnailjét, dokumentum meta-adatait stb.)
Egy válasz a zh-ból (betű szerint):
egy file-t több részletben több helyen tárolunk az FS-en ilyen lehet pl. mp3 ID3 tagjei, képek tumbnailjei, extended attribute-umok
Kommentár: A válasz 2,5 pontot ér, mert sajnos pontatlanul fogalmaz. Mit jelent az, hogy "több részletben, több helyen tároljuk"? A fragmentálódott fájlokat is több részletben, több helyen tároljuk, mégsem állnak szükségszerűen több forkból. A válasz igazából nem definiálta a forkot, csak mondott rá példát, amiből az ID3 tag ráadásul nem is jó példa, mert az szükségszerűen a fájl elsődleges adatterületének a része (persze az ID3 tagben található adatokat tarthatnánk egy - vagy egy-egy - forkban).

  • Mire jó a pivot_root? Mit csinál? (6 pont)
Helyes válasz:
A pivot_root parancs és az ugyanilyen nevű rendszerhívás "elforgatja" az egy-gyökerű könyvtárfát úgy, hogy egy jelenleg valamelyik alkönyvtárba mountolt fájlrendszer legyen az új gyökér-fájlrendszer, az eddigi gyökér pedig ennek valamelyik alkönyvtárába legyen bemountolva. Ez azért jó, mert így később umountolhatjuk a root fájlrendszert, amit egyébként sehogyan sem tudnánk megtenni.
A leggyakrabban initrd-s (inital ramdiskes) bootolás egyik lépéseként használják: a működő rendszer alatt a ramdiskről a tényleges (a diszken levő) root fájlrendszert mountolják be / directoryként. De használhatjuk kézzel is, ha pl. távolról alakítunk át egy gépet LVM-esre/raidesre, és reboot nélkül akarunk áttérni az újonnan elkészített root fájlrendszerre.
Egy válasz a zh-ból (betű szerint):
A pivot_root a root fs mount pontját változtatja meg.
Kommentár: a válasz 1 pontot ér, mert valóban "ilyesmiről" van szó, de ebből nem derül ki, hogy az fstabot írja át úgy, hogy legközelebb máshová mountolódjon az, ami most a root, vagy ...?

  • Mire jók a POSIX ACL-ek? Mondjon példát olyan jogosultságrendszerre, ami POSIX ACL-ekkel leírható, de hagyományos unixos jogosultságokkal nem! Bónuszkérdés: olyanra is tud példát, ami POSIX ACL-ekkel sem írható le? (10 pont)
Helyes válasz:
A POSIX ACL-ek egy olyan DAC (discretionary access control) mechanizmust valósítanak meg, amelynek segítségével csoportokra és felhasználókra lebontva megadhatjuk, hogy az adott inode-ra (fájlra, könyvtárra) kinek legyen olvasási, írási ill. végrehajtási joga. Az ún. "effective rights mask" segítségével valamennyi ACL-ből kimaszkolhatók bizonyos bitek (tehát pl. egyszerűen megvonható mindenkitől az írási jog akkor is, ha a saját ACL-je szerint van neki, és a mask újbóli módosításával visszaadható).
(Az ACL az Access Control List rövidítése.)
Mivel a POSIX ACL DAC-mechanizmus, nem pedig MAC (mandatory access control), a felhasználók a saját fájljaikon átállíthatják az ACL-eket, vagyis rendszerszinten sajnos nem kényszeríthetők ki vele bizonyos jogok bizonyos könyvtárakon.
Példa olyan esetre, ami unixos jogosultságokkal nem írható le: olyan fájl, amire az olvasó csoportnak olvasási joga van, az író csoportnak írási és olvasási joga is van, és azoknak a felhasználóknak, akik nem tagjai ezek közül egyik csoportnak sem, sem írási, sem olvasási joga nincs. (POSIX ACL-ekkel ezt egyébként így adhatjuk meg: setfacl -m g:olvaso:r--,g:iro:rw-,o:--- file)
Példa olyan esetre, ami POSIX ACL-ekkel sem írható le: egy végrehajtható fájl az egyik csoport számára legyen setuid root, a másik csoport számára egyszerűen végrehajtható, de ne setuidos. Ez azért nem oldható meg POSIX ACL-ekkel, mert a POSIX ACL-ek csak az rwx biteket tartalmazzák, a setuid, setgid, sticky biteket nem.
Egy válasz a zh-ból (betű szerint):
ACL Access Control List. Bonyolultabb hozzáférési jogrendszer mint a hagyományos unixos jogosultságok.
Pl. AFS-nél az hogy az AFS szerver gyökerén belül van egy Private mappa, Public és Public_html. Itt azt kell megoldani, hogy a public_html-t csak a webszerver olvassa viszont a publicot ne. Tehát ha egy szolgáltatásnak akarunk jogokat megadni azt ACL-el érdemes megtenni.
Kommentár: A válasz 2 pontot ér. A leírt példa lehet jó is, de a válaszból ez sajnos nem derül ki, mivel sajnos érthetetlen - mit jelent az, hogy "a publicot ne"? A publicot ne olvashassa a webszerver, vagy a publicot ne csak a webszerver olvashassa? És milyen jogok legyenek a Private könyvtáron? A POSIX ACL-ek szemantikájáról sajnos egy szó sem esik.

[szerkesztés] 7 Software RAID Linux alatt

  • Mi a write-behind a linuxos softraid kontextusában, és mire jó? (5 pont)
Helyes válasz:
RAID1 esetén a tömb egyes elemein beállítható külön a write-mostly flag, és az egész tömbre a write-behind működés. A write-mostly hatása az, hogy az adott eszközről az md driver próbál nem olvasni, csak szinkronizálni rá; ez akkor jó, ha az adott eszköz lassú, mert így akkor csak az írást lassítja le, az olvasást nem. A write-behind-ot akkor tudjuk bekapcsolni, ha a tömbnek van write-intent bitmapje, és az a hatása, hogy a write-mostly-nak jelölt eszközökre aszinkron módon fog írni az md driver; ennek az az értelme, hogy ha pl. egy viszonylag lassú hálózati diszk is része a tömbnek, akkor annak a sebessége nem fogja vissza nagyon a teljes tömb írási teljesítőképességét, a tartalma mégis nagyjából szinkronban marad a tömb többi részével.

(Erre a kérdésre az a hallgató, akinek a zh-jában szerepelt, nem válaszolt.)


  • Mit jelent a linuxos softraid terminológiájában a RAID10? Mik a jellemzői? Mik a paraméterei? (8 pont)
Helyes válasz:
Itt elsősorban azt kell leírni, hogy:
- egy absztrakciós szinten megvalósított csíkozás és tükrözés (több diszken is tárolja ugyanannak az adatnak egy-egy példányát, és az egészet csíkozza);
- így pl. csinálhatunk páratlan számú diszkből is csíkozott-tükrözött tömböt (RAID0+1 és RAID1+0 csak páros számú diszkkel megy);
- lehet hozzá hotspare-t adni;
- paraméter: hány éles diszk (ami nem spare), hány near másolat, hány far másolat, hány spare.
(A near/far magyarázatát l. a Software RAID Linux alatt szócikkben.)
Egy válasz a zh-ból (betű szerint):
Egy absztrakciós szinten megvalósított RAID1 és RAID0. A tömb hotspare-t tartalmaz, bővíthető, a hibajavítást megvalósító paritás "tömbök" nem külön meghajtón helyezkednek el, ezek ugyanúgy az adatok mellett szerepelnek. A RAID0 a diszkek csíkozását, a RAID1 a diszkek tükrözését jelent. Csíkozás alatt az értendő, hogy a több diszkből álló tömb teljes területét felhasználjuk, ellenben nem törekedünk az adatok biztonságos tárolására. A csíkok szélesíthetőek több diszk beiktatásával, sekélyíthetőek, mélyíthetőek a csíkonkénti blokkszám csökkentésével, növelésével.
A tükrözés a redundancia növelését szolgálja, az adatok pontos másolata található meg az összes diszken. RAID10 esetén a hotspare bármelyik kiesett lemezt pótolhatja, online képes szinkronizálni. Maximum 2 winchester kiesését képes túlélni.
Kommentár: a válasz sajnos csak 2 pontot ér, mert számos hibát tartalmaz:
- A RAID10 egyelőre nem bővíthető.
- Nincs benne paritás (súlyos hiba paritást emlegetni); a redundanciát másolatok tárolásával biztosítja, mint a RAID1.
- A csíkozás nem azt jelenti, hogy a teljes területet felhasználjuk, hanem azt, hogy a logikailag folytonos adatterületek több diszkre vannak elosztva annak érdekében, hogy a folytonos írás/olvasás több diszket is érintsen, és így ezek a műveletek gyorsuljanak.
- A csíkozás nem zárja ki, hogy a biztonságra is figyeljünk: a RAID5 is csíkoz, csak számol paritást is.
- A RAID10 annyi diszk kiesését képes elviselni, ahány redundáns másolatot tartalmaz, ez viszont egy olyan paraméter, amit a tömb létrehozásakor állíthatunk be.
- Nem tér ki a near és far másolatokra.

[szerkesztés] 8 Logikai kötetkezelés

  • Hogyan lehet az LVM segítségével online mentést nem támogató adatbázisról mégis kvázi-online mentést készíteni? (10 pont)
Helyes válasz:
1. Leállítjuk az adatbázist – ami az adatokat egy logikai köteten tárolja –, vagy legalábbis befagyasztjuk a tranzakciókat, hogy rövid időre konzisztens állapotban maradjanak a diszken tárolt adatok.
2. Készítünk egy pillanatfelvételt (LVM snapshotot) az adott logikai kötetről. Ez nagyon gyorsan megvan.
3. Az adatbázist újraindítjuk, vagy újra engedélyezzük az író tranzakciókat.
4. Ezután az LVM a snapshot copy-on-write szemantikájának köszönhetően, ha az adatbázisszoftver ír az eredeti kötetre, akkor a módosítandó adatoknak egy másolatát elhelyezi a snapshot-köteten, így biztosítva, hogy onnan mindig kiolvasható legyen a snapshot készítésekori állapot.
5. Ezután a kedvenc offline backup-megoldásunkkal lementjük a snapshot-kötetről az adatbázist.
Így megoldható, hogy az adatbázist ne kelljen annyi időre leállítani, ameddig az offline mentés tart, csak a snapshot létrehozásának idejére, ami max. tizedmásodperces nagyságrendű idő.
Egy válasz a zh-ból (betű szerint):
LVM-el lehet snapshotot ???? ez csak a ????változtatásokat???? ???? le mivel ez kell vissza????.
Kommentár: a válasz az olvashatatlan szavaktól függetlenül 1 pontot ér, mivel csak a snapshotot, mint kulcsfogalmat említi, a módszer részleteiről egyáltalán nem emlékezik meg.

  • Legfeljebb mennyi helyet érdemes allokálni egy snapshot LV számára? Miért? (3 pont)
Helyes válasz:
Legfeljebb annyit, amekkora az eredeti LV (aminek a snapshotja), mégpedig azért, mert a snapshot csak azokat az adatokat tárolja, amelyek az eredeti köteten megváltoztak, ennek a tárolásához pedig nem lehet szükség több helyre, mint amekkora az eredeti kötet (x MiB-nyi adatban legfeljebb x MiB-nyi változás tud bekövetkezni).
Egy válasz a zh-ból (betű szerint):
Legfeljebb annyit, amekkora a volume, amiről a snapshot készül, ez általában sok, helyette elég annyi, amennyi változásra számítunk az adott volume-on a snapshot használata alatt (a volume változhat, de a snapshot megőrzi a volume eredeti állapotát, ha a snapshotot nem változtatjuk, így az eredeti állapotnak változáskor ki kell íródnia a snapshotra).
Kommentár: a válasz 3 pontot ér; az indoklás ugyan nem szabatos, de a kérdés szempontjából irreleváns kiegészítésből kiderül, hogy a hallgató érti, miről van szó.

  • Miért jó logikai kötetkezelést használni? Mondjon alkalmazási példákat is! (5 pont)
Helyes válasz:
Itt aztán lehet mesélni. :) Amit meg lehet említeni: rugalmasság (nem kell előre eldönteni, minek mennyi hely kell; utólag betolt diszk kapacitása hozzáadható a filerendszerhez; akár működés közben kicserélhető az összes diszk a rendszer alatt); snapshot lehetősége (és erre néhány alkalmazási példa, nem részletesen). Biztos olyasmit is fel lehet sorolni, ami nekem most nem is jutott eszembe.
Egy válasz a zh-ból (betű szerint):
Mert relatíve "eszközfüggetlen". A hardver és szoftver (=fájlrendszer) közé épül be, a fizikai eszközök cserélhetőek a fájlrendszer alatt, a fájlrendszer ill. az alatta levő LVM kötet pedig növelhető, zsugorítható (fájlrendszerfüggő).
A logikai kötetkezelés linux alatt (RAID) ill. LVM formájában jelenik meg. Az LVM felett tetszőleges fájlrendszer elhelyezhető. SunOS-nél az újonnan megjelent ZFS az LVM-et a fájlrendszerekkel ötvözi, a SPARC architektúrát nagyon specifikusan lekezelve.
Felhasználás: érdemes lehet nem a teljes LVM-et "kimérni" a partíciók számára, így igény szerinti automatizált partíciónövelő és egyben rendszergazdát e-mailben/SMS-ben/... értesítő scriptet/programokat írni kritikus alkalmazásokat futtató szerveren. (PL.: a /VAR/LOG partíció nem telet be (nem blokkolódhatnak a programok), de a logokat hosszú, előre megadott ideig őrizni kell (EU-s távközlési szolgáltatók?)
Ilyenkor a teljes LVM betelése előtt növelhetőek az LVM kapacitása. (VG)
Kommentár: a válasz 5 pontot ér annak ellenére, hogy nem minden mondat értelmes. :) Hogy mit jelenthet a SPARC architektúra "specifikus lekezelése", az pl. rejtély, de valamit hagyni kell az utókornak is.

  • Milyen elvárásaink legyenek azokkal a filerendszerekkel szemben, amelyeket logikai kötetekben akarunk használni? (6 pont)
Helyes válasz:
Az alábbiakat kell megemlíteni, rövid indoklással:
- gyors, online növelhetőség
- zsugoríthatóság előny, de kevés fs tudja, főleg online
- snapshot támogatása (a snapshot lehetőleg legyen konzisztens)
Egy válasz a zh-ból (betű szerint):
Logikai kötetekben olyan fájlrendszereket érdemes használni amikkel teljes mértékben ki tudjuk használni a logikai kötetek által nyújtott előnyöket. Elsősorban az online átméretezés ami itt szóba jöhet. LVM-nél pl. hiába változtat juk meg a PV-k méretét a rajta levő fs-t vagy csak offline tudjuk átméretezni, vagy online de csak növelni, vagy egyik sem. Online jól méretezhető az xfs pl. ext2/ext3 esetén offline, vagy patch-el online is.
Fontos még a naplózás is. ext3 fizikai naplót vezet. Az xfs pedig logikai naplót. Ez utóbbi fontos előnye hogy csak a metaadatokat írja a log file-ba, ezáltal gyorsabban ????visszaállítható????, mint pl. az ext3.
Ha nem linuxban gondolkodunk jó megoldás pl a SUN-os zfs ahol a logikai kötetek és a filerendszer "összenőtt". Az átméretezés online így nem gond. (plane hogy a használt ufs fs már régóta tudja ezt)
Kommentár: A válasz 4 pontot ér. Egyrészt nem említi a snapshot-támogatást, másrészt az LVM pont a fizikai kötetek méretét nem tudja megváltoztatni (megj.: ez azóta változott, most már van pvresize is). A logikai és a fizikai napló nem abban különbözik, hogy csak a meta-adatokat naplózza, vagy az összeset, hanem abban, hogy milyen módon teszi ezt. A fizikai naplóban a diszkre kiírandó szektorok vannak benne, egy az egyben, míg a logikai naplóban az elvégzendő módosítások magas szintű leírása, ami sokkal kompaktabb.

[szerkesztés] 9 A bootfolyamat

  • Miért nehéz a System V init használata esetén egy daemonizált szolgáltatás megbízható, robusztus leállítása? Milyen módszerrel próbálják enyhíteni a problémát, és mi a baj ezzel a módszerrel? Hogyan foglalná össze, mi a probléma gyökere? (12 pont)
Helyes válasz:
A daemonizálódás azt jelenti, hogy amikor a szolgáltatás elindul, az első processz szinte azonnal indít egy gyermekfolyamatot (ami nyit egy új process groupot), majd kilép. A szolgáltatás nyújtásáért a továbbiakban ez a gyermekfolyamat, ill. az ő esetleges gyemekfolyamatai a felelősek; az első processzt indító folyamatnak a továbbiakban nincs kapcsolata a szolgáltatásért felelős processzekkel, mert az ő gyermekfolyamata szinte azonnal kilépett, és a Unixban minden processz csak a közvetlen gyermekfolyamatai állapotáról értesül hivatalból.
Ennek a következménye az, hogy nincs pontos képünk arról, melyik PID-hoz tartozó processz a felelős egy szolgáltatásért.
A szolgáltatás leállítása általában abból áll, hogy a megfelelő processznek küldünk egy bizonyos signalt. A gond az, hogy nem tudjuk, melyik a megfelelő processz. Többféle heurisztikával meg lehet próbálni megkeresni, de mindegyikkel van valami baj.
- Kereshetünk adott nevű processzt. Ez nem jó, mert a szolgáltatásunkkal nem összefüggő processznek is lehet olyan neve, ill. elképzelhető, hogy a szolgáltatásunkkal kapcsolatos processz neve nem kiszámítható (mert mondjuk átírja aszerint, hogy éppen mit csinál).
- Hálózati szolgáltatás esetén megnézhetjük, melyik processz figyel az adott szolgáltatás portján. Ezzel az a baj, hogy elképzelhető, hogy több olyan szolgáltatásunk is van, ami az adott porton figyelni tud; lehet, hogy hol az egyik fut, hol a másik, vagy az egyiket nem is használjuk szolgáltatásként, csak valamilyen segédprogramja miatt van fent, stb. stb.; ilyenkor, ha egy nem futó szolgáltatást próbálunk leállítani, lehet, hogy a futót állítjuk le véletlenül.
- A legelterjedtebb módszer a pidfile használata. Ez egy szövegfile, amibe a szolgáltatás (vagy az őt indító és daemonizáló segédprogram, pl. a start-stop-daemon) beleírja a PID-ját. Ezzel több baj is van:
- Ha a szolgáltatás több processzből áll, és ezeknek van életciklusa (tehát egy idő után kilépnek, és új veszi át a helyüket), akkor elképzelhető, hogy a pidfile már nem a megfelelő PID-t tartalmazza.
- Ha a pidfile helyét egy konfigurációs fájlban kell megadni, elképzelhető, hogy a szolgáltatás elindítása óta megváltozott a konfiguráció, és nem arra a pidfile-ra hivatkozik, amit a futó szolgáltatáspéldány használ.
- Összetett struktúrájú konfigurációs fájl esetén esetleg nem triviális megtalálni benne az érvényes pidfile-beállítást (pl. apache2).
- Így aztán, ha a pidfile-ból ki is olvasunk egy természetes számot, valahogyan meg kell(ene) győződni arról, hogy az valóban a leállítandó szolgáltatáshoz tartozó processz PID-ja-e; itt viszont versenyhelyzetbe kerülhetünk, mert a szám kiolvasása, hitelesítése és a signal elküldése között megeshet, hogy az eredeti processz kilép, és másik kapja meg a PID-ját, így annak a másiknak küldjük a signalt is.
- Még akkor is fennáll ugyanez a versenyhelyzet, ha nem hitelesítjük a PID-t sehogy (ez amúgy rossz ötlet), mert a kiolvasás-signalküldés nem atomi művelet (a kiolvasás után - de a signalküldés előtt - eltűnhet az eredeti processz és létrejöhet ugyanolyan PID-val másik, de még az is baj, csak kisebb, ha csak az eredeti tűnik el).
A probléma gyökere az, hogy az első processzt hagytuk kilépni. Ha ő még futna, akkor a szülője tudná a PID-ját, és így tudna neki signalokat küldeni PID-lottó nélkül is.
Egy válasz a zh-ból (betű szerint):
A System V init a processzeket a process ID-jük (PID) alapján állítja le. Ez azért lehet problémás, mert a PID fájlba bárki beleírhat, más processzt állíthatunk le. Ha a processz kilépett és más kapta meg a PID-jet, akkor is mást állíthatunk le. System V init esetén a démonizált szolgáltatások indítása a következőképpen történik: a processz elindul, háttérbe kerül, majd minden hívásra gyermekfolyamatokat hoz létre. Ez azért nem jó mert nem tudjuk ekzaktul (sic) meghatározni a keresett gyermekfolyamat PID-jét. Ez kiküszöbölhető úgy, hogy a szülő folyamat nem kerül háttérbe és ő foglalkozik minden kéréssel. Ezt valósítja meg a runit és a daemontools.
Kommentár: a válasz sajnos csak 3 pontot ér; nagyon zavaros, sokminden keveredik benne (úgy látszik, ezt rosszul mondtam el az előadáson). Konkrét hibák:
- Ha signalt kell küldeni egy szolgáltatás leállításához, akkor szükségszerűen tudnunk kell egy PID-t, aminek a signalt küldhetjük. Ez nem egyedül a System V initre jellemző.
- Nem derül ki, mi a pidfile.
- Az nem igaz, hogy a pidfile-t bárki felülírhatná; nem szükséges, hogy mindenki számára írható legyen.
- A daemonizálás folyamatának leírása nem jó; nem az a processz kerül a háttérbe, amit közvetlenül elindítunk (pl. a parancssorból), hanem annak a gyermekfolyamata. Amit elindítunk, az a gyermekfolyamat létrehozása után ki is lép.
- Nem szükségszerű, hogy egy daemon egyáltalán foglalkozzon "kérésekkel" (gondoljunk pl. az auto-nice-daemonra). Az pedig pláne nem szükségszerű, hogy minden kérés kiszolgálásához új processz induljon.
- Nem az a baj, hogy az egyes kérésekkel foglalkozó gyermekfolyamatok PID-ját nem tudjuk, hanem az, hogy egyáltalán semmilyen, a szolgáltatáshoz köthető PID-t nem tudunk, legalábbis nem megbízható módon.
- Teljesen független egymástól az, hogy egy szolgáltatás daemonizálódik-e, és hogy külön gyermekfolyamatot indít-e minden kérés kiszolgálásához. Emiatt a leírt, a runitnak tulajdonított megoldás sajnos szintén nem jó.

  • Milyen problémák vannak a System V inittel? (8 pont)
Helyes válasz:
A következő problémákat lehet említeni:
- komplexitás
- rugalmatlanság (ha egy szolgáltatás respawnol, akkor nem lehet lelőni)
- robusztusság hiánya (szolgáltatások leállítása nem egyszerű)
- runlevel-rendszer szűkössége (lényegében csak 4 lehet, és nem adhatunk nekik nevet)
- lassúság (párhuzamosság hiánya ott, ahol pedig lehetne)
- jogok delegálásának viszonylagos összetettsége (sudo vagy ilyesmi kell)
- az initscriptek induláskori állapottere nem konzisztens: vagy a shelltől, vagy az inittől öröklik; emiatt nehéz initscriptet tesztelni
Egy válasz a zh-ból (betű szerint):
- Nem követi azt a UNIX filozófiát, hogy egy időben csak 1 processzel foglalkozik. Az rcS-ben külön oldalakat fordítanak arra az "optimalizációra", hogy egy "szám osztállyal" jelölt rc scriptek egyszerre fussanak le. pl. S20, S30
- inittabban elavult feature-ök vannak, pl ups management
- respawn-nál nem lehet megmondani, hogy melyik processel szeretnénk "foglalkozni", gyakorlatilag runlevel váltással állítható csak le egy program, ami respawnol.
- nincs monitorozás, központi logolás, erőforrások korlátozása
- erre nyújthat megoldást (részben, vagy teljesen) a runit
Runittal külön ????futtatunk???? minden programhoz monitorozó logoló service-t, és mivel a runit egy processzének gyereke a service az adott futtatási környezet átadható és az erőforrás használat egyedileg módosítható.
Ugyanakkor a runit hátulütője lehet az eléggé szétszórt, de azért átláthatóan struktúrált configdir/configfile architektúra.
Kommentár: A válasz 6 pontot ér. A központi logolás és az erőforrások korlátozása nem az init feladata. A válasz nem említi a szolgáltatások leállítása körüli problémákat. A UNIX-filozófia nem ír elő olyat, hogy "egy időben csak 1 processzel szabad foglalkozni". A runit előnyeinek indoklása rossz, de szerencsére nem vonatkozott rá a kérdés.

[szerkesztés] 10 A runit működése

  • Mi alapján tudja az svlogd szétválogatni a naplóüzeneteket?
Helyes válasz:
Egy a reguláris kifejezésekénél gyengébb kifejezőerejű, de gyorsabban illeszthető wildcard-rendszer segítségével minden megadott naplókönyvtárban "halmazműveletekkel" megadhatjuk azon üzenetek halmazát, amelyeket az adott könyvtárba naplózni szeretnénk. Ha az adott könyvtár konfigurációja UDP-s naplózást (is) előír, akkor a kiválasztott üzeneteket az svlogd az UDP-s naplószervernek is elküldi. Az, hogy "halmazműveletekkel", azt jelenti, hogy a +/- operátorokkal szukcesszíven bővíthetjük/szűkíthetjük a kiválasztott üzenetek halmazát, amíg tényleg pontosan azok vannak benne, amikre kíváncsiak vagyunk.
Bizonyos értelemben "szétválogatás" az is, hogy külön meghatározhatjuk, melyik üzeneteket írja ki a standard errorra is (a logfájlon és/vagy az UDP-n kívül).
(A wildcard-rendszerben amúgy két metakarakter van, a plusz és a csillag; ezeknek a jelentése benne van a dokumentációban, nem kell fejből tudni.)
Egy válasz a zh-ból (betű szerint):
SVLOGD képes illeszteni mintákat, ill. pipe-onként is képes szétválogatni az üzeneteket (Minden pipe egy futó szolgáltatás, processz).
Kommentár: A válasz sajnos csak 1 pontot ér. Nem derül ki, hogy a szétválogatás azt jelenti, hogy minden egyes megadott könyvtárban egyenként kell rendelkezni az oda naplózandó vagy nem naplózandó üzenetek halmazáról (tehát igazából nem szétválogatásról van szó, mert az az lenne, hogy "ezt ide, azt meg amoda" - ehelyett viszont olyat tudunk mondani, hogy "ide semmit, kivéve ezt, de abból emezt mégse, kivéve amazt, mert azt mégis", és ezt minden könyvtárban egyenként).
A válasz második része pedig sajnos teljes tévedés; amit a hallgató leírt, az a socklog-ng-re jellemző, nem az svlogdre. Az svlogd pontosan egy "pipe-ból" (a standard inputról) olvas; ha több (a standard outputra logoló) szolgáltatást akarunk naplóztatni, akkor több svlogd-t kell futtatnunk (mindegyikhez egyet). Ebből a szempontból a socklog, ami a syslogd-t helyettesíti, egyetlen szolgáltatásnak minősül, kvázi "koncentrálja" a többi program által generált syslog-üzeneteket. A szétválogatásnak is főleg a socklog esetén van értelme, bár természetesen máskor is vehetjük hasznát.

  • Mik az előnyei a runitnak a hagyományos System V inittel szemben? Mik a hátrányai? (12 pont)
Helyes válasz:
Előnyök:
A runit több egyszerű (ezért kisebb erőforrásigényű és várhatóan kevesebb hibát tartalmazó) programmal oldja meg azt, amit az init egy bonyolult programmal.
A bootfolyamat jelentősen felgyorsulhat, mert a szolgáltatások egyszerre indulnak el.
A szolgáltatások közötti függőségek kezelése kézimunkával megoldható (System V init esetén csak kísérletezni lehet vele, robusztus megoldás nincs).
Menedzseli is a szolgáltatásokat, nemcsak elindítja: újraindítja őket, ha leállnak; küldhetünk nekik signalokat; kapunk hozzájuk robusztus naplózást.
(Az inittel is lehet újrainduló szolgáltatást csinálni - "respawn" -, de az nem állítható le szelektíven.)
A szolgáltatások leállításához nem szükséges PID-k megtippelése; mivel a szolgáltatás szülőfolyamata nem lép ki, mindig tudjuk, milyen PID tartozik a szolgáltatáshoz.
A szolgáltatások egységes környezettel indulnak, attól függetlenül, hogy parancssorból indítjuk őket vagy a bootfolyamat során indulnak el.
Tetszőleges számú és nevű runlevelünk lehet.
Ezeket az előnyöket a felhasználók saját szolgáltatásainak is tudjuk biztosítani.
A szolgáltatásokat, ha akarjuk, kiemelt csoportok vagy felhasználók root jogok nélkül is menedzselhetik, akkor is, ha a szolgáltatás root jogokkal fut; ehhez csak a szolgáltatáshoz tartozó runsv-parancs-FIFO-t kell a megfelelő csoport vagy felhasználó számára írhatóvá tenni.
Könnyen le tudjuk kérdezni a szolgáltatásaink státuszát (fut, nem fut, próbál elindulni, mióta fut, mióta nem fut stb.).
Kapunk olyan eszközöket (elsősorban a chpst-t), amikkel egyszerűen beállíthatjuk a futtatandó szolgáltatás unixos állapotterét az elindítása előtt.
Runlevel-váltáskor (a previous symlink segítségével) könnyen nyomon követhetjük, leálltak-e már az előző runlevelben igen, de az újban nem futó szolgáltatások.
Hátrányok:
Általában nem kapunk kész run-scripteket a szolgáltatásokhoz, ezeket nekünk kell megírnunk; így valamivel több munka van a runittal, mint az inittel.
Nincs egységes mechanizmus a szolgáltatások konfigurációjának újraolvastatására (ami az initben a /etc/init.d/szolgáltatásneve reload).
Ha blokkolódik a naplózás, blokkolódik a szolgáltatás (ez elkerülhető, de akkor viszont lehet, hogy elvesznek naplóüzenetek).
Általában nem a runit az alapértelmezés, így a rá való áttéréshez szükséges idő hozzáadódik a telepítés időigényéhez.
Az initet lehet arra kérni, hogy töltse rá magát saját magára (hajtson végre egy execve("/sbin/init") jellegű rendszerhívást); a runit ilyet nem tud. pivot_root után lehet rá igény.
(Feltehetően lehet még találni előnyöket és hátrányokat is.)
Egy válasz a zh-ból (betű szerint):
runit előnyei:
- egy dologgal foglalkozik egy parancs, nem sokkal mint az init
- ha egy folyamat megáll újra tudja indítani
- nem probléma, hogy melyik processznek kell a signált küldeni
- kicsik a programok
- gyors
runit hátrányai:
- nem használják a distribek default az init helyett
- a szolgáltatások elindításához nekünk kell megírni a scripteket
- kicsit trükközni kell a runlevel váltásnál, bár a runlevel váltást nem nagyon használják
Kommentár: a válasz 7 pontot ér, mivel hét valódi előnyt/hátrányt említ. A runlevel-váltásra vonatkozó megjegyzés nem állja meg a helyét; nem kell "trükközni" runlevel-váltáshoz, egyszerűen más a mechanizmus, mint az initben. Egy (opcionálisan két) symlink átállítása nem "trükközés" ahhoz képest, hogy kiadjuk a "telinit x" parancsot. A previous symlink nem szükséges a runlevel-váltáshoz a runitban, csak nekünk segítség, hogy nyomon követhessük az előző runlevelben igen, de az újban nem futó szolgáltatásaink státuszát. Az initben ezzel ekvivalens mechanizmus amúgy egyáltalán nincs.

  • Milyen esetekben rotálja a logokat az svlogd? (6 pont)
Helyes válasz:
A következő három esetben:
1. a log mérete elérte a configban megadott számú bájtot.
2. a log életkora elérte a configban megadott számú másodpercet, és a log nem üres.
3. az svlogd processz ALRM signalt kap, és a log nem üres.
Egy válasz a zh-ból (betű szerint):
svlogd: - ha a megadott paramétereket elérték a logok (méret, darabszám, időtartam stb.); a konfigfile-ban állítható be
- ha manuálisan megkérjük erre szignállal
Kommentár: a válasz 4 pontot ér, mivel gondolkodáshiányra utaló dolgot tartalmaz: a logok darabszáma nem lehet a rotálás feltétele, hiszen a rotálás megnöveli a logok számát, tehát ha egyszer túlléptük a küszöböt, akkor folyamatosan rotálnunk kellene. Másfelől viszont a logok darabszámát csak a rotálás növeli meg, tehát ha csak darabszám alapján akarnánk rotálni, akkor sosem rotálnánk. Emellett a válasz nem tér ki arra, hogy üres logot az svlogd semmiképpen nem rotál.

  • Milyen feltételeket kell teljesítenie egy minimális runitos run-scriptnek ahhoz, hogy a runit előnyei érvényre jussanak? (5 pont)
Helyes válasz:
- Nem szabad kilépnie, amíg a hozzá tartozó szolgáltatás ki nem lép.
- Lehetőleg azonnal ki kell lépnie, amikor a hozzá tartozó szolgáltatás kilép.
- exec-kel kell elindítania a szolgáltatást, hogy a runsv által a gyermekfolyamatának küldött signalokat a szolgáltatás kapja meg, és ne a run script; ha ez valami miatt nem megoldható, akkor gondoskodni kell a signalok továbbküldéséről, de ez már eléggé elbonyolítja a scriptet (a kill signalt pedig úgysem tudjuk továbbküldeni).
- A standard errort át kell irányítani a standard outputra ("exec 2>&1"), hogy az is megjelenjen a szolgáltatáshoz tartozó svlogd-példány standard inputján.
- Meg kell próbálnia (pl. az sv start parancs segítségével) elindítani azokat a szolgáltatásokat, amiktől az aktuális szolgáltatás függ; ha nem sikerül, ki kell lépnie (a runit később újrapróbálja).

(Erre a kérdésre az a hallgató, akinek a zh-jában szerepelt, nem válaszolt.)


  • Milyen lehetőségeket biztosít az svlogd a logok által elfoglalt hely mennyiségének menedzselésére? (6 pont)
Helyes válasz:
- Megadhatjuk, mekkora méret elérése esetén rotálja a logot (vagyis kezdjen újat);
- megadhatjuk, hány régi logot tartson meg (ha ennél több van, rotációkor törli a legrégebbit, amikor újat kezd);
- megadhatjuk, legalább hány régi logot tartson meg (ha ennél több van, és elfogy a hely, törli a legrégebbit);
- a posztprocesszorunk rotációkor betömörítheti a régi logot.
Egy válasz a zh-ból (betű szerint):
A log könyvtárában a config fájlban megadható, hogy méret szerint rotáljon, tehát ha elér a logfájl egy méretet, akkor rotálja azt. További beállításokkal meg lehet adni, hogy max n, ill. min n' logfile legyen. Ha max van, akkor ha túllépi a létszámot, törli a legrégebbit. Ha min van, és elfogy a hely, és több, mint n' logfile van, szintén törli a legrégebbit.
Ezen kívül megadhatunk utófeldolgozást, ami pl. tömörítéssel csökkentheti a logfile-ok méretét.
Kommentár: kisebb fogalmazászavartól eltekintve tökéletes válasz, 6 pont.

[szerkesztés] 11 Naplózás

  • Mik az előnyei/hátrányai az UDP ill. a TCP fölötti hálózati naplózásnak? (4 pont)
Helyes válasz:
Mivel a TCP kapcsolatorientált, a naplózószerver elvileg túlterhelhető kapcsolatokkal.
Mivel a TCP-ben van torlódásvezérlés, elképzelhető, hogy a naplóüzeneteket író programok blokkolódnak arra várva, hogy a helyi üzenetpuffert hálózaton át üríteni lehessen; ha ezt nem várják meg, egy idő után megtelik a memória a pufferrel vagy elküldés nélkül el kell dobni üzeneteket.
A TCP-kapcsolatok szemantikájának megvalósítása növeli a naplózókliens összetettségét; kezelni kell pl., ha megszakad a hálózati kapcsolat.
Az UDP kapcsolatmentes, "fire and forget" jellegű. Emiatt kliensoldalon nem feltétlenül van visszajelzés arról, hogy egy üzenet elküldése sikeres volt-e, vagyis hálózati problémák esetén naplóüzenetek elveszhetnek útközben.
A torlódásvezérlés hiánya miatt elvileg előfordulhat, hogy az UDP-s naplóüzenetek telítik a hálózatot.
A TCP-kapcsolatok viszonylag egyszerűen biztonságossá tehetők SSL/TLS használatával; elterjedt, szabványos UDP fölötti alkalmazási rétegbeli titkosító-hitelesítő technológia nincs.
UDP-csomagot könnyebb hamisítani, mint TCP-t (amihez session spoofing is kell), így könnyebb hamis naplóüzenetet küldeni UDP-n.

(Erre a kérdésre az a hallgató, akinek a zh-jában szerepelt, nem válaszolt.)

[szerkesztés] 12 Tűzfalak

  • Hasonlítsa össze a DROP és a REJECT iptables-akció működését! Mikor és miért érdemes vagy lehet az egyiket vagy a másikat használni? Mondjon példákat! (6 pont)
Helyes válasz:
A DROP eldobja a csomagot; a REJECT szintén eldobja a csomagot, de erről értesíti a csomag feladóját egy ICMP-üzenettel vagy (választhatóan) egy TCP RST csomaggal. TCP RST csomagot értelemszerűen csak TCP-csomagra tudunk válaszolni; ICMP-csomagra pedig egyáltalán nem válaszolhatunk, nehogy visszacsatolt hurok képződhessen.
A REJECT előnye az, hogy a távoli gép megtudja, hogy a csomagja nem elveszett, hanem nem kértük, így esetleg nem próbálkozik feleslegesen újra (meg újra meg újra). Jó példa arra, amikor ez nekünk (a tűzfal üzemeltetőjének) is jó, az auth (ident) szolgáltatás; ha a 113-as TCP portra jövő kapcsolatokat egyszerűen eldobjuk, akkor olyankor, amikor bizonyos szolgáltatásokat (pl. IRC) próbálunk igénybe venni, ki kell várnunk, amíg a távoli szerver feladja a próbálkozásokat az auth porton; míg ha azonnal küldünk TCP RST-t, akkor az első próbálkozás után rájön, hogy nincs azon a porton semmi, és nem kell kivárnunk a félperces nagyságrendű timeoutot.
A REJECT hátránya ugyanakkor, hogy egyrészt elárulja, hogy az adott címen egyáltalán van számítógép, másrészt pedig forgalmat generál, ami felhasználható lehet DoS-támadáshoz. Ezért ügyelni kell arra, hogy a REJECT szabályaink által generált forgalmat rátalimitáljuk.
Másfelől viszont előnye a REJECTnek, hogy nem árulja el, hogy tűzfal védi a gépet: a védett szolgáltatások portjai úgy viselkednek, mintha csakugyan nem futna ott szolgáltatás, nem pedig úgy, mintha szűrnénk az oda irányuló forgalmat. Emellett "jólneveltség" is jelezni, hogy egy szolgáltatás nem elérhető.
A DROPot akkor célszerű használni, ha a bejövő csomag nyilvánvalóan ártó szándékú; pl. egy ismert spammer IP-jéről jövő SMTP-kapcsolatfelvétel. Ha küldünk neki TCP RST-t, azzal megkönnyítjük a dolgát, ha viszont csak eldobjuk a csomagját, akkor egy darabig még lekötjük az idejét, mert esetleg párszor még megpróbálja.
Egy válasz a zh-ból (betű szerint):
DROP: a csomagot eldobjuk, a küldőt nem értesítjük erről. Pl. támadások érzékelésekor célszerű (portscan esetében lelassítja a támadást, ki kell várnia a timeoutot a támadónak).
REJECT: a csomagot eldobjuk, a küldő kap erről értesítést. Ha szeretnénk, hogy a küldő értesüljön a csomag eldobásáról, ez a célszerű.
Kommentár: a válasz csak 3 pontot ér, mivel:
- nem írja le, milyen értesítést küld a REJECT a feladónak;
- nem mond konkrét példát arra, hogy mikor jó REJECT-et használni;
- nem szükségszerűen igaz, hogy portscan esetén a támadónak bármit is ki kellene várnia: megteheti, hogy minden vizsgált portra párhuzamosan elküld egy, majd röviddel később még egy, majd még egy csomagot, aztán csak a beérkező válaszokkal foglalkozik; ahonnan nem jön válasz, arról pedig feltételezi, hogy nem elérhető/szűrve van.

  • Mikor lehet értelme az olyan NAT-nak, ami egy címteret egy vele azonos méretű másikra képez le? Mondjon példát! (3 pont)
Helyes válasz:
Elsősorban akkor van értelme, ha két olyan magánhálózatot kapcsolunk össze, amelyek ugyanazt az IP-tartományt használják (pl. 192.168.0.0/24), és nem akarjuk egyik oldalon sem újraosztani a címeket.
Másodsorban akkor használhatunk ilyet, ha a belső hálózatunkat "implicit tűzfallal" szeretnénk megvédeni. Pl. van egy /16-os, publikus cím-blokk, de a belső gépeknek nem ebből, hanem egy /16-os privát blokkból osztunk címeket, aztán 1:1 megfeleltetéssel a publikus blokk címeire képezzük le ezeket, amikor az Internethez fordulnak (tehát pl. a 192.168.83.144-es gép összes kimenő kapcsolata a 152.66.83.144-ről látszik jönni). Ennek ugyan borzasztóan sok értelme nincs, de annyit mégis elérünk vele, hogy egyrészt a belső gépeket az Internet felől nem lehet közvetlenül megcímezni (tehát ebben az értelemben védettek, bár ez nem sokat jelent manapság), másrészt pedig egyszerűsíti a NAT-oló eszköz dolgát, kevesebb állapot-információt kell tárolnia (a {belső cím,protokoll,port} hármashoz algoritmikusan generálható a {külső cím,protokoll,port} hármas, vagyis ezt nem kell külön nyilvántartani).
Egy válasz a zh-ból (betű szerint):
Így a belső hálózatot kívülről nem lehet megcímezni, mégis egyértelmű a külső IP címe (is). Pl. ha van egy 152.66.0/24 címtartományunk, annak megfeleltethetünk egy 192.168.0/24 címtartományt.
Kommentár: a válasz 1,5 pontot ér, mert a két legfontosabb alkalmazási lehetőség közül csak az egyiket említi.

  • Milyen esetekben érdemes a Netfilterben rátalimitet használni és miért? Mondjon néhány példát! (6 pont)
Helyes válasz:
Rátalimitet elsősorban kimenő vagy átmenő forgalomra érdemes beállítani; arra is akkor, ha van olyan forgalom, ami nem létfontosságú, de hasznos, viszont nem szeretnénk, ha megtölthetné a kimenő vonalunkat (vagy a tűzfalunk mögött található hálózat bejövő vonalát). Ilyen például a REJECT szabályaink által generált TCP RST csomagok által képviselt forgalom; vagy a hasznosnak ítélt ICMP-típusok (azt nem szertnénk, ha egy bejövő Echo Request floodra kimenő Echo Reply flooddal válaszolnánk, de az azért nem árt, ha a ping amúgy működik). Ezen túlmenően célszerű rátalimitálni a naplózást végző szabályokat is, hogy ha valaki tudja, milyen csomagokat naplózunk, akkor se legyen képes arra, hogy tetszőleges mennyiségű naplóüzenetet gyártasson velünk rövid idő alatt.
Egy válasz a zh-ból (betű szerint):
Bizonyos DoS támadások elkerülése érdekében.
Vagy bizonyos mennyiségű csomag utáni naplózás bekapcsolásáért. -> PL: 100/s beérkező csomag a 113-as porton még nem érdekes, de utána már gyanús.
A DoS támadások egy része "elárasztáson" alapul: érdemes pl. a beengedett echo-request-eket valami a szerver feladatkörének megfelelően x/s-ra állítani. X eleme [0, végtelen[ (0 pl. otthon router, 100/s pl. BME levelezőszerver).
Ugyanígy minden portra lehet limiteket beállítani.
Kommentár: A válasz 4 pontot ér.
Alapvetően igaz, hogy bizonyos DoS támadások elkerülése érdekében célszerű rátalimiteket alkalmazni, de a konkrétumok sajnos nem teljesen jók.
Érdekes ötlet, hogy naplózzuk, ha 100/s-nál több csomag érkezik valamelyik portunkra; nekem nem jutott volna eszembe erre használni a rátalimitet, de végülis megoldható (egy olyan szabállyal, ami 100/s-os rátával naplózás nélkül elfogadja a csomagokat, aztán a következő szabály valami rátalimittel naplóz, az ezutáni pedig válogatás nélkül elfogad).
A beengedett echo-requestek rátájának korlátozása közvetve éri el ugyanazt a hatást, amit közvetlenül a kimenő echo reply csomagok rátájának korlátozásával érhetnénk el; erre viszont úgyis szükség van, úgyhogy a bejövő rátalimit majdnem felesleges (azért csak majdnem, mert ha a bejövő csomagokat rátalimitáljuk, akkor létre sem jön a hozzájuk tartozó, a kimenő rátalimit miatt úgyis halva születő válaszcsomag, és ezzel megspórolunk egy kis processzoridőt).
Az "ugyanígy minden portra lehet limiteket beállítani" vélhetően azt jelenti, hogy lehet a portra érkező kapcsolatok rátáját korlátozni. Ezt viszont ész nélkül nem érdemes megtenni, mert így azzal szúrunk ki a legkevésbé, aki a legtöbb kapcsolatot próbálja felépíteni, mivel az ő csomagjaiból van a legtöbb, és így neki van a legtöbb esélye mégis átcsúszni a korláton. Amúgy is kérdéses, miért akarnánk egy népszerű szolgáltatásra érkező kapcsolatkérések rátáját korlátozni; a számukat akarhatjuk (ha a szerverünk legfeljebb x darab klienst tud megfelelő minőségben párhuzamosan kiszolgálni), de a rátájukat miért?
Kifejezetten hiányzik a válaszból, hogy a naplózó szabályokat a naplóüzenet-dömping elkerülése végett lényegében minden esetben rátalimitálni kell.

  • Milyen fogalmakkal dolgozik a Linux Netfilter alrendszere? Magyarázza el, melyik micsoda! (8 pont).
Helyes válasz:
Elsősorban a tábla, a lánc, a szabály, az illesztés (match) és az akció (target) fogalmát kell elmagyarázni. Ezt valószínűleg lehetetlen megtenni a csomag útjának valamilyen mélységű ismertetése nélkül. Nem ismétlem meg itt azt, amit a Tűzfalak szócikkben leírtam.
Egy válasz a zh-ból (betű szerint):
Tábla: 3 táblát használ alapesetben a netfilter, filter, nat és mangle. A filter táblába kerülnek a hagyományos értelemben vett csomagszűrőszabályok (a 2.4-es kernel előtti netfilterben tekinthető úgy, hogy csak ez az egy tábla volt), azaz a node számára kimenő, bejövő és áthaladó csomagokra adhatunk meg szabályokat. A nat táblába a hálózati címfordítással kapcsolatos szabályok kerülnek, a mangle táblába pedig a csomagok jelölésével kapcsolatos szabályok.
Lánc: A a táblákban a szabályok láncokra tagolódnak. A beépített láncoknak előre meghatározott szerepük van, a felhasználók által létrehozott láncok általában valamelyik beépített láncra hivatkoznak. Pl. a filter tábla beépített láncai az INPUT, az OUTPUT és a FORWARD, amik rendre a bejövő, a kimenő, és a node-on áthaladó csomagokra hivatkoznak.
cél (target): a csomagok célja, azaz megadja, hogy az adott szabályra illeszkedő csomaggal mit csináljon a kernel. Ilyen pl. a DROP, ami az adott csomagot eldobja, vagy az ACCEPT, ami elfogadja.
Egy szabály megadása az iptables program segítségével kb. így néz ki: iptables -t tábla -A/D/valami lánc (-p protokoll, -s IP, stb.) -j (vagy -g vagy valami) cél
Kommentár: a válasz 6 pontot ér, mivel nem tér ki a match (illesztés) fogalmára, és kicsit zavarosan ismerteti a láncokat - mit jelent az, hogy "a felhasználók által létrehozott láncok valamelyik beépített láncra hivatkoznak"? Éppen fordítva van: a beépített láncokból fogunk elágazni a saját láncainkra, vagyis a beépítettek "hivatkoznak" a felhasználó által definiált láncokra. Van még néhány kisebb pontatlanság (pl. a mangle táblában módosíthatjuk is a csomagokat, nemcsak megjelölhetjük őket; újabban van egy raw tábla is; stb.), de ezek nem fontosak.

[szerkesztés] 13 A munin működése

  • Milyen esetben érdemes COUNTER helyett DERIVE típusúnak definiálni egy adatot a munin (rrdtool) számára, és miért? (4 pont)
Helyes válasz:
(A COUNTER típusú adatról az rrdtool feltételezi, hogy monoton nő; tehát ha az értéke csökkent, akkor egy 32 vagy egy 64 bites számláló túlcsordult.)
(A DERIVE típusú adatnál az rrdtool nem feltételez monotonitást; a kiolvasott érték idő szerinti deriváltját ábrázolja, ami lehet negatív is.)
Akkor érdemes DERIVE-ot használni COUNTER helyett, ha:
- olyan számlálónk van, ami viszonylag gyakran resetelődik anélkül is, hogy túlcsordulna (pl. reboot miatt); de
- nem tudunk vagy akarunk konkrét felső korlátot megadni az általa számolt mennyiség növekedési rátájára (amit az rrdtool a grafikonban ábrázol); viszont
- zavarnak a grafikonokban az rrdtool által feltételezett túlcsordulásokat korrigálni hivatott 232-es tüskék; de
- az nem zavar minket, ha a számláló valódi túlcsordulásakor a grafikonban nem látunk semmit.
A DERIVE-hoz ilyenkor elég egy univerzális, 0-ás alsó korlátot megadni; a COUNTERnek viszont (ha pl. hálózati forgalomról van szó) minden gépen kellhet más korlát (pl. mert mindenhol más az upstream sávszélesség).
Egy válasz a zh-ból (betű szerint):
Ez két különböző számláló típus, akkor érdemes COUNTER helyett DERIVE-ot használni, ha a számláló nem növekedhet minden határon túl.
Kommentár: a válasz sajnos csak 1 pontot ér, mivel nem indokolja meg, hogy miért érdemes DERIVE-ot használni akkor, ha a "a számláló nem növekedhet minden határon túl", ez viszont önmagában érthetetlen. Az egy pont azért jár, mert a hallgató látszólag tisztában van azzal, hogy egyáltalán micsoda a COUNTER és a DERIVE.

  • rrdtoollal (ezt használja a munin) szeretnénk grafikont rajzolni arról, hogy hány kérést szolgál ki a webszerverünk. A webszervertől meg tudjuk kérdezni, hány kérés érkezett az elindítása óta összesen. Milyen típusú adatként vegyük fel ezt az adatforrást az rrdtoolban? COUNTER, GAUGE, DERIVE vagy ABSOLUTE? Miért? (4 pont)
Helyes válasz:
Vegyük sorra, melyik jön számításba. Ugyebár egy olyan adatot fogunk kiolvasni, ami normális körülmények között monoton nő (a webszerver elindítása óta kiszolgált kérések száma nem tud csökkenni), viszont a webszerver újraindítása esetén nullázódik.
COUNTER: ez jó; az egyetlen baj vele az, hogy a webszerver újraindulásakor túlcsordulást fog feltételezni, és 232-t hozzá fog adni a kiolvasott értékhez, vagyis minden restart után lesz egy nagy tüskénk a grafikonban. Ezeknek a kiszűrése viszonylag nehéz, mert nem biztos, hogy könnyen meg tudjuk mondani, hány kérés/s-nak látszó forgalom esetén kell érvénytelenként eldobni az adatot. Persze nem valószínű, hogy 106-nál több kérést tudnánk kiszolgálni másodpercenként, úgyhogy ha ezt adjuk meg maximumnak, azzal már jók vagyunk (ha ennél nagyobb értéket vél olvasni az rrdtool, akkor UNKNOWN értéket ír be az adatbázisába).
GAUGE: ez nem lesz jó; a grafikonon is azt látnánk, hogy elindulása óta hány kérést szolgált ki a webszerver, mi viszont általában valamilyen időegységre vetítve szeretnénk ezt tudni (mondjuk percenként vagy másodpercenként).
DERIVE: ez szintén jó, mivel a kiolvasott érték idő szerinti deriváltját fogja ábrázolni, ami éppen az, ami nekünk kell (ha 5 perc után 100 kérés, és 10 perc után is 100 kérés, akkor a derivált nulla, és ez összevág azzal, hogy ez alatt az öt perc alatt egyetlen kérést sem szolgáltunk ki). A webszerver újraindítása után a derivált negatív lesz, úgyhogy negatív tüskét láthatunk a grafikonon; ezt könnyű kiküszöbölni úgy, hogy nullára állítjuk az adat értékkészletének alsó határát.
ABSOLUTE: ez szintén rossz választás, mivel a feladat szövege szerint a számláló nem nullázódik kiolvasáskor, az rrdtool viszont ezt feltételezné; így a monoton növekvő számlálóból arra következtetne, hogy minden periódusban egyre több és több kérést szolgáltunk ki. Az előző számokkal számolva az első öt perc alatt is százat és a második alatt is százat. Ha 15 perc után 150-en állna a webszerver számlálója (vagyis a harmadik ötperces periódusban 50 kérést dolgozott fel), akkor ABSOLUTE adattípus esetén az rrdtool azt gondolná, hogy minde a 150 kérés az utolsó öt percben keletkezett.
Összefoglalva: a COUNTER vagy a DERIVE lehet jó választás. A COUNTER esetében a maximumot, a DERIVE esetében a minimumot kell helyesen beállítani ahhoz, hogy a nem kívánt tüskéket elkerüljük. Mindkét esetben torzítani fogja az értékeket, ha a webszerver újraindulása utáni következő kiolvasáskor a számláló már elérte a legutolsó kiolvasáskori értéket; az rrdtool nem fogja észrevenni, hogy újraindítás történt, és úgy fog számolni, mintha nem lett volna restart. Ezen sajnos nem lehet segíteni.
Egy válasz a zh-ból (betű szerint):
ABSOLUTE-ot használnék, mert ebben az esetben a számláló minden kiolvasáskor RESET-et kap, így nem csordulhat túl."
Kommentár: a válasz sajnos nem ér pontot. Az rrdtool nem tudja befolyásolni, hogy a számláló rendelkezik-e azzal a tulajdonsággal, hogy kiolvasáskor nullázódik; ez a kérdéses számlálónak egy adottnak vehető tulajdonsága, amihez nekünk alkalmazkodnunk kell, amikor az rrdtoolt beállítjuk. Az sem igaz, hogy a kiolvasáskor nullázódó számlálók nem csordulhatnak túl; értelemszerűen ha két kiolvasás között többel nő meg az értékük, mint amennyit tárolni tudnak, akkor ugyanúgy túlcsordulnak, mintha nem nullázná őket a kiolvasás. (Az azért igaz, hogy "ritkábban" vagy "kisebb valószínűséggel" csordulnak túl, mint nem nullázódó társaik.)

[szerkesztés] 14 A Nagios működése

  • Mi a Nagios terminológiájában a "puha" ill. a "kemény" állapot? (3 pont)
Helyes válasz:
A Nagios puhának nevezi egy szolgáltatás állapotát, ha volt olyan próbálkozás, amikor a szolgáltatás elérhetetlennek bizonyult, de még meg kell próbálnia néhányszor (recheck), hogy biztos legyen a dolgában. Az állapot akkor kemény, ha egy "próbálkozáshullám" (check+recheck-ek) összes próbálkozását elbukta a szolgáltatás.
Egy válasz a zh-ból (betű szerint):
"puha": az állapotváltás létrejött a "check" (első vizsgálat) szerint, de ez még nem tekinthető véglegesnek, mert további vizsgálatok ("recheck") vannak folyamatban
"kemény": az állapotváltás véglegesnek tekinthető, nem lesz erre nézve több vizsgálat (nem jelenti, hogy nem fog később megváltozni, csak azt, hogy a nagios ezt az állapotváltást figyelembe vette.)
Kommentár: tökéletes válasz, 3 pont.

  • Mire és miért kell odafigyelni sok szolgáltatás monitorozása esetén (pl. ötpercenként meg akarunk győződni arról, hogy megy mind a 3000 webszerverünk)? Hogyan oldja meg ezt a problémát a Nagios? (5 pont)
Helyes válasz:
Nagyszámú szolgáltatás monitorozásakor több fontos problémával is szembe kell nézni. Ezek közül az egyik az, hogy a monitorozással kapcsolatos terhelés minél egyenletesebben jelentkezzen, tehát ne egyszerre akarjuk a 3000 webszerver állapotát lekérdezni, mert az ennyire szélsőségesen börsztös forgalmat esetleg nem bírja jól a hálózatunk, és a mérési eredményeket is eltorzíthatja; pl. lehet, hogy azért mérünk rossz reakcióidőt, mert elfogyott a sávszélesség, és nem azért, mert lassú a webszerver. Ezt a problémát a Nagios "okos" ütemezője megpróbálja megoldani, és aránylag egyenletesen elosztani a próbálkozásokat úgy, hogy mindegyikre az előírt gyakorisággal kerüljön sor, de lehetőleg minél kevesebb történjen egyszerre. NEMHASZ: Ne Monitorozzuk Halálra A Szolgáltatásokat.
Egy másik gond forrása az lehet, ha a monitorozó gép hálózati kapcsolata megszakad; ebben az esetben mind a 3000 webszervert egyszerre látja meghibásodni, és mindegyikről szépen küld is emailt, vagy rosszabb esetben sms-t. Ezt a Nagios-szal úgy kerülhetjük el, ha függőségeket vezetünk be a szolgáltatások között; így pl. ha nem látjuk a default gatewayt, csak erről az eseményről kapunk értesítést, és az ettől függő további tesztekre (a 3000 db webszerver ellenőrzésére) sor sem kerül.
Egy válasz a zh-ból (betű szerint):
A monitorozás szempontjából lényeges, hogy ne arról szóljon üzenet a monitorozónak, hogy egy adott szolgáltatás nem megy, hanem arról, hogy működik. A jelzés elmaradása esetén pedig riasszon minket. Fontos szempont, hogy a monitorozás ne vegyen nagyobb mennyiségű erőforrást igénybe,mint az adott szolgáltatás / ez akkor fontos, ha a monitorozás és a szolgáltatás ugyanazokat az erőforrásokat használja / Így a példában szereplő 3000 webszerver egyszerre történő állapotlekérdezése a hálózaton csúcsterhelést jelent. A lekérdezéseket osszuk szét az ötperces időtartamban, így elkerülhetjük a túlterhelést. Ha egyszerre vizsgálnánk az összeset, akkor elképzelhető lenne, hogy a webszerverek pingelése akkora terhelést okozna hálózatunkon, hogy a webszerverek nem lennének elérhetőek.
Kommentár: a válasz 3,5 pontot ér. A két probléma közül az egyiket jól leírja, és a kérdés sajnos olyan értelemben félrevezető volt, hogy egyes számban beszélt "a problémáról", azt a látszatot keltve, hogy csak egy van. A válasz ugyanakkor nem említi, hogy a Nagios képes lenne arra, hogy egyenletesen ossza el a próbálkozásokat; ezen túlmenően webszerverek elérhetőségét nem pingeléssel vizsgáljuk, hanem HTTP-kéréssel (hiába ping, ha az apache lerohadt, ill. a webszerver működhet akkor is, ha pingelni nem tudjuk).

[szerkesztés] 15 Gyakorlati rész

[szerkesztés] 15.1 Routingos feladat 2006.

  • Adott a következő routing-tábla, és a 127.128.129.130-as géppel szeretnénk kommunikálni. A tábla mely sorai milyen sorrendben jutnak szerephez? Milyen üzeneteket kell kiküldenünk, ha feltételezzük, hogy az ARP-táblánk üres? Töltse ki a táblázat üresen hagyott mezőit (a megadott adatokból a hiányzó adatok kikövetkeztethetők)! Rajzolja le a routing-tábla által leírt hálózat topológiáját! (20 pont)

Helyes válasz:

A táblázatban vastagon írtam a többi adatból kitalálandó adatokat.

Sorszám Destination Gateway Netmask Interface
1 2.0.0.0 0.0.0.0 255.0.0.0 eth0
2 2.3.0.0 0.0.0.0 255.255.0.0 eth1
3 2.3.4.0 0.0.0.0 255.255.255.0 eth2
4 127.128.129.0 2.3.4.5 255.255.255.128 eth2
5 127.128.129.0 2.3.4.6 255.255.255.0 eth2
6 127.128.129.192 2.3.4.5 255.255.255.248 eth2
7 127.128.129.208 0.0.0.0 255.255.255.248 eth3
8 3.4.5.6 127.128.129.210 255.255.255.255 eth3
9 0.0.0.0 2.0.0.254 0.0.0.0 eth0

A lerajzolandó topológia ilyesmi:

20061114-zh-routing.png

A routing során először az 5. sor jut szerephez; ez tartalmazza a keresett 127.128.129.130-as cím felé mutató route-ot (ennek a leghosszabb a netmaskja a célra illeszkedő route-ok közül). A következő sor, amit felhasználunk, a 2.3.4.6-os router elérhetőségét tartalmazó sor, ami a 3. Ezután küldünk egy ARP who-has 2.3.4.6 üzenetet az eth2-es interfészünkön. Ha megkapjuk a választ, akkor elküldjük a 127.128.129.130-as gépnek szánt csomagot úgy, hogy IP-szinten neki, Ethernet-szinten pedig a 2.3.4.6-nak címezzük.

Azzal nem kell törődni, hogy bizonyos állomások látszólag több helyen is vannak; a routing mindenképpen a legspecifikusabb route-ot választja.

[szerkesztés] 15.2 Scriptes feladat 2006.

  • Keressen versenyhelyzeteket, logikai vagy biztonsági hibákat az alábbi scriptben! Hogyen lehetne ezeket elkerülni? Amúgy mit próbál csinálni a script? (20 pont)
 1. #!/bin/sh
 2. mkdir $(date)
 3. DEBUGFILE=/tmp/$1.$$
 4. ps axfu >$DEBUGFILE
 5. cd $(date)
 6. PID=$(pidof ntpd)
 7. echo PID of ntpd was $PID >log
 8. kill $PID
 9. tail -f log &
10. TAILPID=$(pidof tail)
11. while [ -d /proc/$PID ]; do
12.	echo waiting for ntpd to die... >>log
13.	ps axfu >$DEBUGFILE
14.	sleep 1
15. done
16. kill $TAILPID
17. /etc/init.d/ntpd start &

Helyes válasz:

A script alapvetően az ntpd-t (időszinkronizáló daemon) próbálja lelőni, majd újraindítani. Miközben vár arra, hogy leálljon, a "DEBUGFILE"-ban folyamatosan frissíti a futó processzek listáját.

Hibák:

  1. A date outputja tartalmaz szóközt, nem szerencsés idézőjel nélkül felhasználni.
  2. A date kimenetében a területi beállítások (LC_TIME) függvényében esetleg előfordulhat a "/" karakter, ami azt eredményezheti, hogy a 2. sorban csődöt mond az mkdir (mert többszintű könyvtárhierarchiát csak a -p kapcsolóval hoz létre, ez viszont nem biztos, hogy hordozható). Az LC_TIME változót a szülőjétől (a shelltől) örökli a script. Az ebből eredő bizonytalanság miatt vagy elő kellett volna írni a date-nek, hogy milyen formában írja ki a dátumot, vagy POSIX-ra (vagy más kívánt locale-re) kellett volna állítani az LC_TIME-ot. Erről nem volt szó az órán, úgyhogy nem volt muszáj tudni (de mostantól az :).
  3. A directoryt az aktuális munkakönyvtárban hozzuk létre; nincs hibakezelés, egyszerűen feltételezzük, hogy sikerült, pedig az aktuális könyvtár lehet akár readonly fájlrendszeren is.
  4. A DEBUGFILE létrehozásakor felhasználjuk a parancssorban átadott első paramétert, ami szintén tartalmazhat szóközt vagy egyéb speciális karaktert, ezért szintén idézőjelbe kellett volna tenni.
  5. A DEBUGFILE mindenki által írható könyvtárban jön létre; ilyen esetben pl. a tempfile(1) programot érdemes használni, ami megpróbálja az összes versenyhelyzetet elkerülni. Az sajnos nem elég, ha a fájl létrehozása előtt megvizsgáljuk, nem létezik-e, mert aközött a két pillanat között, hogy megvizsgáljuk, és utána létrehozzuk, valaki más létrehozhat a helyén egy a /etc/shadow-ra mutató symlinket. Mindenképpen atomi művelettel kell létrehozni úgy, hogy hibát kapjunk, ha már létezett.
  6. A 2. és az 5. sor között megváltozhat (sőt, jó eséllyel meg is változik) a date kimenete, tehát nem abba a directoryba váltunk, amit az előbb létrehoztunk. Hibakezelés nincs, feltételezzük, hogy sikerült beleváltani.
  7. A 6. sorban ntpd néven futó processzeket keresünk. Ilyenből lehet nulla, egy, vagy több, de nem vizsgáljuk meg, hány van.
  8. A 7. sorban a log nevű fájl létrehozása szintén biztonsági rés, mert lehet, hogy a könyvtárat már valaki más létrehozta nekünk, mielőtt beleváltottunk, és van benne egy log nevű symlink, ami a /etc/shadow-ra mutat. Itt is egy még garantáltan nem létező fájlba kellene logolni (tempfile), de még jobb lenne olyan könyvtárat használni, ahova biztosan csak nekünk van írási jogunk; ott nyugodtan hozhatnánk létre akár log nevű fájlt is.
  9. A 8. sorban TERM signalt küldünk az összes processznek, aminek a PID-ját a 6. sorban megkaptuk; azóta azonban eltelt valamennyi idő, úgyhogy ezek a processzek kiléphettek, és az ő PID-jukkal indulhattak újak. Tehát nem biztos, hogy azoknak küldjük a signalt, amelyeknek akartuk. Ráadásul ntpd néven akárki futtathat processzt, és nem biztos, hogy az összeset ki akarjuk lőni. Mivel azonban ez nem volt specifikálva, itt csak találgatunk.
  10. A 11. sorral több gond is van. Egyrészt feltételezi, hogy $PID pontosan egy PID-t tartalmaz; valójában azonban lehet nulla vagy több is.
    • Ha nulla, akkor a /proc könyvtár megszűnésére várunk, ami várhatóan végtelen ciklus lesz.
    • Ha egy, akkor versenyhelyzetben vagyunk; lehet, hogy a TERM signal hatására az adott processz (ami, mint az előbb tisztáztuk, nem biztos, hogy még mindig egy ntpd volt) kilépett, de azóta indult helyette másik ezzel a PID-val. Ekkor egy újabb processz kilépésére várunk, ez a processze azonban nem biztos, hogy valaha ki fog lépni, vagyis lehet, hogy megint végtelen ciklusba kerülünk.
    • Ha több, akkor shellfüggő, hogy a [ -d mihez kezd az egynél több argumentummal. Bizonyos shellek csak az elsőt veszik figyelembe (ez nekünk mázli), mások hibaüzenetet adnak. (Ezt nyilván nem kellett volna fejből tudni.)
  11. A 12. sor minden iterációban új sort fűz a loghoz. Mivel a ciklus lehet végtelen, a log nőhet végtelen nagyra.
  12. A 13. sor újra létrehozza a DEBUGFILE-t, ennek minden problémájával (mivel a /tmp alatt van, a tmpreaper letörölhette az előzőt, a támadó pedig létrehozhatott a helyén symlinket, stb.)
  13. A 16. sorban feltételezzük, hogy csak az általunk az imént indított tail PID-ja lesz benne a TAILPID változóban, holott valójában az összes, a 10. sor futásakor futó tail nevű folyamat PID-ja benne lesz - a mi tailünké viszont lehet, hogy nem, mert a 9. sor egy új shellt indít, ami majd elindítja a tail programot, de itt versenyhelyzet van a pidof lefutása és a háttérben indított tail elindulása között. Általában a tail fog nyerni, úgyhogy a pidof megtalálja, de garancia nincs erre.
  14. További probléma a 16. sorral, hogy itt is lőhetünk ki ártatlan processzt, pl. ha a mi tailünk bármi (mondjuk I/O error) miatt kilépett, és az ő PID-jával indult valami más, akkor ezt a mást lőjük ki még akkor is, ha a 10 sorban a mi tailünk PID-ja (és csak az) került a TAILPID változóba.

[szerkesztés] 15.3 Scriptes feladat 2007.

Keressen versenyhelyzeteket, logikai vagy biztonsági hibákat az alábbi scriptben! Melyiknek mi a (nem kívánt) hatása? Melyik mennyire súlyos, ha a scriptet a rendszergazda futtatja? És ha egy normális felhasználó? Hogyan lehetne ezeket a hibákat elkerülni? Amúgy mit próbál csinálni a script? (20 pont)

 1. #!/bin/sh
 2. # redirect stdout to log
 3. exec >foobar.$$.log
 4. # copy log to stderr
 5. tail -f foobar.$$.log >&2 &
 6. pidof tail >/tmp/tail.pid
 7. mkdir $(date)
 8. for i in $(find $@ -name *.mp3); do ln $i $(date)/; done
 9. touch $(date)/*
10. find / -name *.mp3 >/tmp/mp3.log &
11. FINDPID1=$(pidof find)
12. find . –atime +30d | xargs echo rm –rf 2>/tmp/delete.log >/tmp/deleteold.sh &
13. FINDPID2=$(pidof find)
14. echo "Operation in progress... please wait."
15. sleep 5
16. cd $(date)
17. echo Start:
18. ls
19. for i in *; do
20.   mv $i $(echo $i | sed 's/.*-/$ARTIST-/')
21. done
22. echo End:
23. ls
24. echo Waiting for find to exit.
25. for i in $FINDPID1 $FINDPID2; do
26.   while [ -d /proc/$i ]; do
27.      echo Waiting...
28.      sleep 5
29.   done
30. done
31. # don't need this anymore, clean it up
32. kill $(cat /tmp/tail.pid)
33. rm –rf /tmp/tail.pid

Erről a feladatról rengeteget lehet írni, mert nagyon sok a hiba a scriptben. A 20 pontért nem kellett mindet megtalálni.

Hibák (az abszolút teljesség igénye nélkül):

  1. A 3. sorban megjósolható nevű ideiglenes fájlt hozunk létre; ha a scriptet egy támadó számára is írható könyvtárban futtatjuk, a támadó előre elhelyezhet foobar.{2-32767}.log nevű, valamilyen számára nem írható fájlra mutató symlinkeket ebben a könyvtárban; a script indításakor a kérdéses fájl a futtató felhasználó jogait használva íródik felül. Ha a scriptet a rendszergazda futtatja, szinte bármelyik fájl felülírható. A hibát úgy lehetett volna elkerülni, ha egyrészt nem abban a könyvtárban hozzuk létre az ideiglenes fájlt, amelyben éppen vagyunk (és amelyről nem tudunk semmit), hanem egy csak számunkra írható, konkrétan megadott könyvtárban; másrészt pedig ha a - például az mktemp(1) segítségével - biztonságos módon, mindenképpen egyedi néven hozzuk létre a fájlt.
  2. A 3. sorban megkísérlünk létrehozni egy fájlt, ám nincs hibakezelés; egyszerűen feltételezzük, hogy sikerült, és az 5. sorban megpróbálunk olvasni a fájlból. Ha egy támadó olyan fájlra mutató symlinket helyezett el a mi fájlunk helyén, amelyet írni nem tudunk, csak olvasni, akkor a 3. sor nem írta felül, az 5. sor viszont kiírja az utolsó tíz sort és a később keletkezőket a képernyőre. Ha a fájl érzékeny adatokat tartlamaz, akkor ez nem kívánatos. A jó megoldás ugyanaz, mint az előbb. (Hasonló, de nehezebben kivitelezhető támadás, ha az aktuális könyvtár nem sticky és a támadónak is van rá írási joga: a támadó a 3. sor végrehajtása után, de az 5. végrehajtása előtt az érzékeny adatokat tartalmazó fájlra mutató symlinkkel helyettesíti a 3. sorban létrehozott fájlt. Így az sem akadály, ha a scriptet futtató usernek van rá írási joga; nem fog felülíródni, csak kilistázódni.)
  3. A 6. sorban lekérdezzük azoknak a processzeknek a PID-ját, amelyeknek a neve tail, és az eredményt egy megjósolható nevű, mindenki által írható könyvtárban található fájlba írjuk. Ugyanazok a megfontolások érvényesek, mint a harmadik sornál.
  4. A 6. sorban található pidof találhat nulla, egy vagy több futó tail nevű processzt; a script később (a 32. sorban) feltételezi, hogy egy volt, mégpedig az, amit az 5. sorban indítottunk. Ez több okból sem biztos. Egyrészt, az 5. és a 6. sor között versenyhelyzet van, mivel az 5. sort a háttérben indítjuk ("&" van a végén); elképzelhető, hogy amíg a háttérben indított shell a tail-t próbálja exec()-elni, addig nekünk lefut a 6. sorunk, és így még nincs is meg az tail, amit az 5. sorban indítottunk. Az is lehet, hogy mire a 6. sor lefut, az 5. sorban indított tail már kilépett (pl. mert a 3. sorban nem sikerült létrehozni a fájlt, mert nincs írási jogunk az aktuális könyvtárra, így az 5. sornál nem is létezik a fájl, vagyis a tail hibaüzenetet ad és "azonnal" kilép), és egy a rendszerben valaki más által futtatott tail nevű folyamatot találunk (vagyis még az sem garancia arra, hogy jó PID-t tárolunk, ha csak egy tail nevű folyamatot talál a pidof).
  5. A 7. sorban a date kimenetét közvetlenül átadjuk az mkdirnek, idézőjel nélkül. A date kimenete viszont a területi beállításoktól függően mindenféle karaktert tartalmazhat: szóközt, / karaktert stb. Ha szóközt tartalmaz, és a /bin/sh nem a zsh, akkor az mkdir minden bizonnyal több argumentumot fog látni, vagyis több alkönyvtárat hoz létre; ha /-t, akkor pedig egy könyvtár-részfát próbál létrehozni, de hacsak nem létezik az összes könyvtár az utolsót leszámítva, a -p kapcsoló nélkül hibát ad. Az lett volna a helyes, ha a date-nek előírjuk, hogy milyen formátumban szeretnénk a dátumot (pl. date +%Y%m%d).
  6. A 8. sor egy ciklus: for i in $(find $@ -name *.mp3); do ln $i $(date)/; done. Több gond is van vele.
    • Egyrészt, ha az argumentumlistánk szóközöket is tartalmazó könyvtárneveket is tartalmazott, akkor a find ezeket esetleg a szóközök mentén szétdarabolva kapja meg, vagyis nem mindegyikben fog keresni.
    • Másrészt, mivel a *.mp3-at nem tettük idézőjelbe, nem védtük meg a shelltől a csillagot, úgyhogy meg fogja próbálni az aktuális könyvtárban glob patternként értelmezni, vagyis behelyettesíteni a *.mp3 helyére az összes, az aktuális könyvtárban található .mp3 kiterjesztésű fájl nevét.
      • Ha nincs ilyen, akkor bizonyos shellek (pl. a zsh) hibaüzenetet adnak; mások (pl. a bash) meghagyják a parancssorban a *.mp3 sztringet betű szerint.
      • Ha pontosan egy .mp3 kiterjesztésű fájl van, akkor vele megegyező nevűeket keres a ciklus a megadott könyvtárak közül azokban, amelyeknek a nevében nem volt szóköz vagy egyéb szóhatároló karakter.
      • Ha egynél több .mp3 kiterjesztésű fájl van, a find nem tudja feldolgozni a parancssorát és hibaüzenetet ad.
    • Mindenesetre a ciklus csak abban az esetben keresi meg a megadott könyvtárakban található valamennyi mp3-at, ha az aktuális könyvtárban nincs mp3 kiterjesztésű fájl, a /bin/sh a bash, és a megadott könyvtárak nevében nincs szóhatároló karakter.
    • Ha a find kimenete szóhatárolót tartalmazó fájlneveket vagy könyvtárneveket is tartalmaz, a shell ezeket esetleg feldarabolja, mielőtt a ciklusváltozóval végigiterálna rajtuk. Így végül a ciklusváltozó nem a find által megtalált fájlok nevein megy végig, hanem azokon a szavakon, amelyekből ezek a nevek állnak.
    • A ciklusmagban minden iterációban újra lekérdezzük a dátumot, és feltételezzük, hogy az éppen lekérdezett dátumról elnevezett alkönyvtár létezik, és bele tudjuk hardlinkelni a megtalált fájlt. A dátum megváltozhatott azóta, hogy a könyvtárat létrehoztuk a 7. sorban (ha ugyan akkor sikerült egyáltalán létrehozni, ugye). A helyes megoldás az lett volna, hogy a dátumot egyszer kérdezzük le, egy változóban tároljuk, és mindig a változó értékét használjuk a $(date) helyett.
    • Az ln hardlinket hozna létre, de ez csak akkor lehetséges, ha a régi és az új fájlnév is ugyanazon a fájlrendszeren található; tehát a ciklus csak azokat a fájlokat fogja tudni odahardlinkelni a dátumról elnevezett, esetleg nem is létező könyvtárba, amelyek ugyanazon a fájlrendszeren vannak, mint ez a könyvtár. A többi fájl esetében hibaüzenetet kapunk (alighanem az "invalid cross-device link" szövegűt).
    • Több felsorolt könyvtárban is lehet azonos nevű mp3-fájl; ezeket mind ugyanabba a könyvtárba próbáljuk hardlinkelni (leszámítva a dátumváltozásokat :), így tehát fájlnév-ütközéskor nem jön létre a hardlink és hibaüzenetet kapunk.
  7. A 9. sorban megint gondjaink lesznek a szóhatároló karaktert tartalmazó fájlnevekkel, mert ezeket minden védelem nélkül adjuk át a touch-nak. zsh-ban ezt megtehetjük így (hogy glob expansion eredménye a szóközt tartalmazó fájlnév), de más shellben nem feltétlenül, úgyhogy oda kell rá figyelni.
  8. A 10. sorban korábbi hibák ismétlődnek: *.mp3, /tmp alatt létrehozott ismert nevű fájl stb.
  9. A 11. sor a 6. sor pepitában (nem biztos hogy pont annak a findnak a PID-ját találjuk meg, amit az előbb indítottunk stb.)
  10. A 12. sorban (find . –atime +30d | xargs echo rm –rf 2>/tmp/delete.log >/tmp/deleteold.sh &) elvileg a 30 napnál régebben megnyitott fájlokat keressük az aktuális munkakönyvtár alatt, és olyan parancssorokat építünk, amelyek ezeket a fájlokat letörlik. Itt is gond van a szóhatárolókat tartalmazó fájl- és könyvtárnevekkel; ezeknek a szavait egyenként fogjuk megpróbálni törölni. Két ismert nevű /tmp alatti fájlt is írunk, ami a korábban részletezett okok miatt veszélyes.
  11. A 13. sorban a script megint azt feltételezi, hogy csak az imént indított find PID-ját fogja megtalálni a pidof, pedig legalábbis a 10. sorban indított előző find is jó eséllyel fut még.
  12. A 14. és a 15. sorban nincs hiba(!) :).
  13. A 16. sorban az aktuális dátumról elnevezett könyvtárba próbálunk váltani, és itt is minden ezzel kapcsolatos eddig említett probléma érint minket (szóközök; változott a dátum a róla elnevezett könyvtár létrehozása óta; stb.). Nincs hibakezelés; ha itt nem sikerült könyvtárat váltani, a későbbi műveleteket akkor is végrehajtjuk, csak nem azokon a fájlokon, amiken szerettük volna.
  14. Nézzük a 19-21. sorban látható ciklust: for i in *; do mv $i $(echo $i | sed 's/.*-/$ARTIST-/'); done.
    • $i-re idézőjel nélkül hivatkozunk, így esetleg több szóra darabolódik.
    • A sed által előállított új fájlnév szintén tartalmazhat szóközt; azt is idézőjelbe kellett volna tenni.
    • $ARTIST sehol sem kapott értéket a scriptben; feltehetően a szülőfolyamattól kellene örökölni, de nem ellenőrizzük, hogy nem üres-e.
    • A sed-script aposztrófok között van, nem macskakörmök között, így $ARTIST értéke akkor sem helyettesítődik be, ha amúgy van neki; helyette szó szerint az fog szerepelni az új fájlnévben, hogy $ARTIST.
    • Ezzel a sed-scripttel több különböző fájlnevet is leképezhetünk ugyanarra a fájlnévre, vagyis az mv-vel kombinálva felülírhatunk fájlokat egymással.
  15. A 25-30. sorban levő ciklus arra próbál várni, hogy a findok kilépjenek. Gyakorlatilag mostanra simán elavulhattak azok a PID-k, amiket akkor találtunk, ha ugyan találtunk; úgyhogy ki tudja, hány és milyen processz kilépésére várunk, ráadásul sorban. Amíg arra várunk, hogy az első kilépjen, a többinek a PID-ja még akkor is érvénytelenné válhat (megkaphatja más processz), ha a külső ciklus elején még érvényes volt. Jó eséllyel lesznek olyan processzek, amelyeknek a kilépésére kétszer is várunk (mert mindkét változó tartalmazza a PID-jukat); ez tovább növeli annak az esélyét, hogy más processz kapja meg közben valamelyik PID-t, és esetleg sosem lép ki, vagyis az idők végezetéig csak várunk. Emellett persze nem biztos (csak nagyon valószínű), hogy be van mountolva a /proc fájlrendszer.
  16. A 32. sorban feltételezzük, hogy:
    1. A /tmp/tail.pid fájl az, amit mi hoztunk létre;
    2. hogy csak az általunk indított tail PID-ját tartalmazza, de azt igen;
    3. hogy az általunk indított tail még mindig fut;
    4. vagy ha esetleg nem futna, akkor a PID-ját még nem kapta meg más folyamat;
    5. hogy a 6. sorban megtalált tail processzek közül egyikért sem kár;
    6. hogy ezek a processzek sem léptek még ki;
    7. vagy ha igen, akkor nem kapta meg más processz egyiknek a PID-ját sem;
    8. vagy ha mégis, akkor ezekért az új processzekért, amelyek a hajdani tail-ek PID-jaival futnak, szintén nem kár.
  17. A 33. sorban, ha a /tmp/tail.pid egy könyvtár volt (ami már létezett, mielőtt mi megpróbáltunk ezen a néven fájlt létrehozni), akkor most a teljes tartalmával együtt letöröljük.

[szerkesztés] 15.4 Tűzfalas feladat 2006.

  • Írjon iptables tűzfalscriptet. Topológia és követelmények:
    • eth0 interface: az Internet felé néz;
    • eth1: a DMZ felé néz;
    • eth2: a belső hálózat felé néz;
    • tap0 egy magánhálózati címeket használó VPN felé néz;
    • a DMZ nem kezdeményezhet kapcsolatot;
    • az Internet felől érkező új kapcsolatok csak a DMZ-be irányulhatnak;
    • a belső hálózat korlátlanul kommunikálhat;
    • a VPN-ről csak a DMZ érhető el, az Internet nem.

A konkrét szintaktika nem fontos; ha nem ismeri az iptables parancssorát, írhat pszeudokódban. A Netfilter felépítését azonban ismerni kell! Minden pszeudokódban leírt sor egyértelműen meg kell, hogy feleljen egy iptables-parancssornak. (20 pont)

Helyes válasz:

Egy lehetséges minimális megoldás a következő:

# töröljük a szabályokat:
iptables -F
iptables -X
# a nemúj kapcsolatok mehetnek:
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
# az új Internet -> DMZ kapcsolatok mehetnek:
iptables -A FORWARD -i eth0 -o eth1 -m state --state NEW -j ACCEPT
# a belső hálózatból érkező új kapcsolatok mehetnek:
iptables -A FORWARD -i eth2 -m state --state NEW -j ACCEPT
# a VPN-ről elérhető a DMZ:
iptables -A FORWARD -i tap0 -o eth1 -m state --state NEW -j ACCEPT
# minden mást tiltunk:
iptables -A FORWARD -j DROP

A VPN számára nem kell NATolni, mert:

  • a mi tűzfalunk kell, hogy a DMZ default gatewaye legyen (különben a DMZ szerverei nem tudnának válaszolni az Internetről érkező csomagokra, mert nem tudnák, merre küldjék a választ);
  • a mi tűzfalunkon van route a VPN-ben levő magánhálózati IP-k felé;
  • tehát a DMZ-s gépek, ha a magánhálózati IP-kről szólítják meg őket, a mi tűzfalunkon keresztül route-olják a választ (mert az a default gateway), ő pedig szépen kiküldi a tap0 interface-en, mert az adott címek az ő routing-táblája szerint arra vannak.

A fenti script működésének a lényege az, hogy mindent engedélyez, amit a specifikáció szerint engedélyezni kell, és minden mást tilt. "A DMZ nem kezdeményezhet kapcsolatot" előírást így automatikusan teljesítjük, nincs szükség külön szabályra hozzá. Az elején azért engedélyezünk válogatás nélkül minden már felépített kapcsolatot, mert az, hogy fel van épülve, azt jelenti, hogy korábban, amikor még új volt, akkor engedélyeztük. Ennek az az előnye is megvan, hogy azokat a csomagokat viszont, amelyek nem tartoznak sem új, sem már felépült kapcsolathoz, eldobjuk; ha nem írunk elő state matchet, akkor ezeket esetleg véletlenül átengedjük.

A scriptet sokféleképpen lehet szépíteni; egyrészt megtehetjük, hogy a szűrt forgalmat nem eldobjuk, hanem REJECT-eljük, és mondjuk ICMP Communication Administratively Prohibited üzenetet küldünk a feladónak (ennek alkalmazásszinten "No route to host" üzenet lesz a hatása). A kimenő ICMP-ket azonban rátalimitálni kell, nehogy "mandínerből" megfloodoljunk valakit.

Ez mondjuk nézhet ki így:

[...]
# minden mást tiltunk:
iptables -A FORWARD -m limit --limit 10/s -j REJECT --reject-with admin-prohib
# ha betelt a limit, akkor a fenti szabály nem illeszkedett, tehát még megvan a csomag, dobjuk el:
iptables -A FORWARD -j DROP

Egy másik lehetséges módszer erre az, hogy a FORWARD végén csak REJECT van (limit nélkül), és az OUTPUT láncban felveszünk egy olyan szabályt, ami a kifelé menő ICMP-t rátalimitálj.

További szépítési lehetőség, hogy naplózzuk, mit dobunk el; ebben az esetben a naplózó szabályt is rátalimitálni kell, nehogy megegye a diszket a napló egy flood során.

[...]
# minden mást tiltunk, de előtte naplózzuk:
iptables -A FORWARD -m limit --limit 10/minute -j LOG --log-prefix "FW: FORWARD catch-all: "
iptables -A FORWARD -m limit --limit 10/s -j REJECT --reject-with admin-prohib
# ha betelt a limit, akkor a fenti szabály nem illeszkedett, tehát még megvan a csomag, dobjuk el:
iptables -A FORWARD -j DROP

További csinosítás lehet, ha ezeket a limiteket nem drótozzuk bele a scriptbe, hanem az elején változókban konfigurálhatóvá tesszük; ugyanezt megtehetjük az interfészek neveivel is.

A specifikáció nem mondott semmit arról, hogy a tűzfal maga hogyan kommunikálhat (bár, ha szigorúan vesszük, akkor az, hogy "a DMZ nem kezdeményezhet kapcsolatot", jelentheti azt, hogy a tűzfal a DMZ-ből érkező kapcsolatkéréseket nem fogadhatja); ha ebben teljesen meg szeretnénk akadályozni (amit megtehetünk), azt elérhetjük pl. így:

iptables -P INPUT DROP
iptables -P OUTPUT DROP

Ezután a gép persze csak konzolról menedzselhető. Nyilván ennél kifinomultabb szabályokat is ki lehet találni, de mivel nem volt része a feladatnak, itt nem foglalkozom vele részletesen.

Általában célszerű arra törekedni, hogy egy tűzfalban az "amit nem szabad, azt tilos" elv jusson érvényre, nem pedig az "amit nem tilos, azt szabad"; a kettőt keverni pedig nagyon nem szerencsés (átláthatatlanná válik).

[szerkesztés] 15.5 2009/1. feladat

Egy linuxos rendszeren a rendszergazda beállított ütemezett feladatként egy olyan shellscriptet, amely óránként lefut rendszergazdai jogkörrel, és a következő két sorból áll:

#!/bin/bash
chmod 777 /tmp/*

A /tmp könyvtár jogosultságbeállításai a szokásosak. Ha valamilyen nem-root felhasználóként be tudunk jelentkezni erre a rendszerre, hogyan tudunk a fentiek ismeretében rendszergazdai jogosultságokat szerezni (vagyis tetszőleges parancsot rootként lefuttatni)? Legalább két módszert találjon ki! (8 pont)

Megoldás: A chmod, ha nem mondunk neki mást, ész nélkül követi a symlinkeket és azon az inode-on állítja be a jogokat, amelyikre a symlink végeredményben mutat. A /tmp könyvtár mindenki számára írható, vagyis bárki bármilyen symlinket elhelyezhet itt. Az okos script gondoskodik arról, hogy a symlinkek célpontjai mindenki számára írhatóvá, olvashatóvá és végrehajthatóvá váljanak.

Ezután csak az a kérdés, mire mutasson az általunk létrehozott symlink. Nagyon sok jó megoldás van, ezért először kiemelnék két rosszat. Volt, aki úgy gondolta, hogy egy setuid root binárisra mutató symlinket hoz létre, amit majd felülír, ha megkapja rá a jogot, és utána a setuid bit miatt rendszergazdai jogosultságokkal futtatja. Ez azért nem működik, mert a chmod 777 törli a setuid-bitet (ugye a jogosultságbitek igazából 4x3 bitből állnak, úgyhogy a 777 voltaképpen 0777-et jelent; az első 3 bit a setuid, a setgid és a sticky bit, amelyeket a 0777-es chmoddal nullázunk).

Egy másik rossz megoldás egy "foo; parancs;" nevű symlink létrehozása abban a reményben, hogy a shell, amikor a chmod 777 * parancssorát előállítja, a pontosvessző utáni részt új parancsként építi be a parancssorba. A shell ennyire szerencsére nem buta; a globbing nem parancssort, csak argumentumlistát állít elő.

Jó megoldások pl., a teljesség igénye nélkül:

  • ln -s /etc/passwd /tmp, majd a saját userünk uid-ját átírjuk nullára; a legközelebbi belépésünkkor rendszergazdai jogaink lesznek.
  • ln -s /etc/shadow /tmp, majd a root user jelszó-hash-ét átírjuk egy olyanra, amelyhez ismerjük a plaintextet.
  • ln -s /az/adott/ütemezett/script /tmp (vagyis a fenti két sorból álló konkrét script); amikor írási jogot kapunk rá, hozzáfűzünk tetszőleges parancsokat a végéhez.
  • ln -s /etc/crontab /tmp, és új ütemezett feladatot veszünk fel, amely rootként fut majd.
  • ln -s /bin/bash /tmp; tudjuk, hogy a rendszergazdai jogokkal futó ütemezett script parancsértelmezője a bash, úgyhogy ha a basht felülírjuk, az általunk kívánt kód fog lefutni helyette, szintén rootként.
  • ln -s /dev/sda /tmp; ezután megkerssük az sda-n található fájlrendszerben egy saját binárisunk inode-ját, átállítjuk a tulajdonosát rootra, majd beállítjuk rajta a setuid bitet is.
  • ln -s /lib/libc.so.6 /tmp; majd a libc-t felülírva majdnem minden program a mi kódunkat is tartalmazni fogja, hiszen betöltik a libc6-ot.
  • ln -s /etc /tmp; így az összes konfigurációs fájlt törölhetjük, majd sajátot hozhatunk létre a helyén, tetszőleges tartalommal. Ez a fenti támadások egy részének az általánosítása.
  • ln -s / /tmp/gyökér; így végeredményben minden könyvtárat és fájlt helyettesíthetünk sajáttal, mert a gyökérkönyvtár összes elemét jogunk van átnevezni (a mountpointokat kivéve), és az eredeti néven saját fájlt vagy könyvtárat elhelyezni ott. Ez egy további általánosítás.

Sok-sok jó megoldás van még. Ezek közül kellett legalább kettőt leírni a maximális pontszámért.

[szerkesztés] 15.6 2009/2. feladat

Egy kollégánk több szerveren szeretne egymás után bizonyos parancsokat lefut­tatni. A parancsokat minden szerveren elhelyezte a /root/bin/somescript nevű fájlban. A szerverek nevét a servers.txt fájl tartalmazza, soronként egyet. Kollégánk a következő egysoros scriptet írta a feladat megoldására:

while read server; do ssh $server /root/bin/somescript; done <servers.txt

Meglepve tapasztalja, hogy annak ellenére, hogy a servers.txt fájl számos sorból áll, a /root/bin/somescript csak az első szerveren hajtódik végre, a többin nem. A szerverek minden lényeges szempontból teljesen megegyeznek egymással. Ha a listán egy másikat rak az első helyre, akkor csak azon hajtódik végre a /root/bin/somescript. Mi lehet a probléma oka? (4 pont)

Segítségképpen felírtam a táblára, hogy a következő parancssor működik, és azt csinálja, amit gondolunk:

echo foo | ssh user@bar cat \>baz

Megoldás: itt arra kellett rájönni, hogy az ssh a kliensprogram standard inputját elérhetővé teszi a szerveren az ssh sessionön át elindított program számára. Azt kellett még végiggondolni, mi történik a fájldeszkriptorokkal az egysoros script végrehajtása során:

  1. A shell indít egy gyermekfolyamatot.
  2. Ez a gyermekfolyamat lezárja a standard inputját, majd megnyitja helyette a servers.txt-t (tehát minden input ebből a fájlból jön majd a továbbiakban).
  3. A gyermekfolyamat megkezdi az egysoros script feldolgozását. Lefut a read parancs, amely az input első sorát beolvassa a $server változóba.
  4. Az stdin fájldeszkriptorban ekkor az offszet (vagyis a következő olvasás pozíciójára mutató "pointer") a servers.txt fájl második sorának elejére mutat.
  5. Elindul az első ssh processz, amelynek az összes fájldeszkriptort, így az stdin-t is, örökli az őt indító shelltől. Vagyis az ő stdin-je is a servers.txt fájl, és az offszet továbbra is a második sor eleje.
  6. Felépül az ssh-kapcsolat, és a szerveren elindul a /root/bin/somescript, amely az sshd-től örökli az stdin FD-t. Az sshd (az ssh-szerverprogram) úgy indítja el a somescriptet, hogy egy-egy olyan pipe-ot örököl stdin-ként, stdout-ként és stderr-ként, amelyeknek a másik vége az sshd processzé, és a titkosított hálózati kapcsolaton keresztül az ssh kliensprogrammal van összekapcsolva. Tehát amit az ssh-szerveren futó program kiír, azt az ssh-kliens megkapja és kiírja a helyi konzolon, és fordítva, amit a szerveren futó program olvas, azt az ssh-kliens standard inputjáról olvassa (ezt tette egyértelművé a táblára felírt segítség).
  7. Ha minden a terv szerint menne, a somescript kilépne, erre az ssh is kilépne, a read beolvasná a következő sort, és mennénk tovább a következő szerverre.

Ehelyett azonban az történik, hogy a read a második alkalommal már nem olvas semmit, és emiatt sikertelen visszatérési értékkel tér vissza; emiatt pedig véget ér a while-ciklus (mert a ciklusban maradás feltétele az, hogy a read sikerüljön). Ezzel az egysoros scriptnek is vége.

Ha az lenne a baj, hogy nem érjük el a további szervereket, akkor az ssh hibaüzenetet adna és sokáig kellene várnunk, amíg próbál kapcsolódni. Ez aligha lepné meg annyira a kollégát, hogy tanácsot kellene kérnie tőlünk, és fontos körülményként biztosan megemlítené, ha mégis így lenne. Arra sem ad magyarázatot ez az elmélet, miért mindig a lista legelső elemén fut le a script, a többin sosem.

A valódi magyarázat az, hogy a somescript tartalmaz valami olyat, ami végigolvassa a standard inputot. Ezt az ssh-kapcsolaton keresztül megteheti; végeredményben elolvassa a servers.txt további sorait a későbbi read parancsok elől. Nem azért, mert ugyanabból a fájlból olvasnak (természetesen megnyithatja két egymástól független processz ugyanazt a fájlt és mindketten végigolvashatják), hanem azért, mert ugyanazon a fájldeszkriptoron keresztül olvassák a fájlt, így az offszet-mezőn is osztoznak.

Erre csak két hallgató jött rá; ők 4-4 pontot kaptak. Részpontszámot lehetett kapni olyan elméletekért, amelyek viszonylag plauzibilisek voltak, de a valóságban nem pont a leírt (és elmondott) viselkedést eredményezték volna. Nem zárom ki, hogy létezik az itt helyesként bemutatottól eltérő másik helyes válasz is, de a ZH-ban senki nem írt ilyet.

[szerkesztés] 15.7 2009/3. feladat

Legalább hány 1TB kapacitású diszkre van szükségünk ahhoz, hogy olyan linuxos soft-RAID-tömböt szervezzünk belőlük, amely legalább 2TB nettó kapacitással rendelkezik, bármely két diszk kiesését az esetleges bithibáktól eltekintve biztosan túléli, hozzáadható egy diszk melegtartalékként és van esély arra, hogy egy harmadik diszk kiesését is túlélje? Hogyan szerveződjön a tömb? Ha több minimális számú diszket igénylő értelmes megoldás is van, mindegyiket írja le! (8 pont)

Megoldás: először gondoljuk végig, hány diszk kell mindenképpen. 1, 2 és 3 triviálisan kevés, hiszen nem élheti túl három diszk kiesését. Négy szintén kevés, mert három kiesése esetén csak egy maradna, azon pedig nem fér el a kívánt 2TB nettó kapacitás. Tehát biztosan kell legalább öt diszk. Kérdés, elég-e öt?

Igen, elég. Több jó megoldás is van (az ezekkel izomorf megoldásokat nem tekintjük különbözőnek):

  • raid5(raid1(sda, sdb), raid1(sdc, sdd), sde) (erre volt, aki rájött)
  • raid6(sda, sdb, sdc, raid1(sdd, sde)) (erre is volt, aki rájött)
  • raid4(raid1(sda, sdb), raid1(sdc, sdd), sde) (az sde a paritásdiszk; erre is volt, aki rájött) (ugyanez működhetne raid4 helyett raid3-mal is)
  • Daraboljuk fel mind az öt diszket 2-2 500G-s partícióra, majd: raid5(raid1(sda1, sde2), raid1(sdb1, sda2), raid1(sdc1, sdb2), raid1(sdd1, sdc2), raid1(sde1, sdd2)) – így homogén tömböt kapunk, ami ráadásul garantáltan túléli három diszk kiesését. Erre nem jött rá senki.

Hat diszk evidens, hogy elég; nagyon sok jó megoldás van hat diszkkel (pl. RAID10, mindenből három példányt tárolva). Aki szerint hatnál több diszk a minimum, nem kapott sok pontot.

Aki a fenti jó megoldások közül legalább kettőt megemlített (és a leírás nem tartalmazott súlyos hibát), a nyolcból legalább hét pontot megkapott.

Személyes eszközök