Ugnezdeni spletni strežnik
Tokrat bomo razrešili dilemo, kako lahko izdelamo ugnezden spletni strežnik, ki brez diska v celoti teče iz hitrega pomnilnika, za streženje spleta pa namesto diska uporablja ključek USB.
Januarja 2005 sem pod naslovom "Filtriranje spletne vsebine z ugnezdenim Linuxom" predstavil izdelavo odprtokodnega usmerjevalnika s tremi omrežnimi segmenti, požarno pregrado in filtrom spletne vsebine. Tokrat se lotimo spletnega strežnika, ki za disk uporablja kar ključek USB. Prednost te rešitve je, da nima mehanskih delov, ki bi se lahko pokvarili, rešitev pa je ob odsotnosti diska in hladilnih ventilatorjev neslišna, energijsko varčna in v smislu omrežne varnosti bolj odporna proti hekerjem.
Uporabljeni material
Ko sem se loteval tega pingvina v nizkem letu, me je mikalo, da bi spletni strežnik postavil na strojni podlagi PC Engines WRAP (malce poguglajte), ker je fizično še manjša, vendar nima podpore USB. Zato bi moral oboje, operacijski sistem in datotečni sistem za streženje spleta, postaviti na isto kartico CF neposredno v reži na tiskanini. Danes sicer PC Engines ima ploščico ALIX3d, ki ima takt 500 MHz, 256 MB pomnilnika, 2 priključka USB in meri 10 x 16 cm, a takrat je še ni bilo. Zato je v danih razmerah zmagal razum in izbral sem raje malce večjo ploščico Soekris net4801 s podporo USB. Tako sem ločil ugnezdeno, pretežno nespremenljivo programsko kodo od spremenljivega podatkovnega dela. Ključek z vsebino spletnega strežnika je brez vpliva na sistem vselej mogoče zamenjati za večjega. Za operacijski sistem sem izbral mikrodistribucijo Linuxa, LEAF uClibc Bering [2], ker ga dobro poznam, ker v celoti deluje z navideznega diska v hitrem pomnilniku (RAM disk), ker ga ni težko prenesti v pomnilniško kartico in ker ima bogato podporo že prevedenih aplikacijskih paketov. Seznam mojih potrebščin je tako bil:
Poleg tega sem uporabil navodila in izkušnje drugih uporabnikov Beringa in Soekrisa, ki sem jih našel v omrežju [3], [4], čeprav sem nekatere stvari naredil po svoje.
Postavljanje Beringa na kartico CF sem opisal že leta 2005. A Bering je od takrat napredoval in tudi v tem prispevku prikazana postavitev bo drugačna in naprednejša od takratne.
Potrebščine za izdelavo nove igrače
V članku so omenjeni avtorjevi starejši članki, ki jih je napisal za Monitor. Najdete jih na spletu:
http://www.monitor.si/clanek/filtriranje-spletne-vsebine-z-ugnezdenim-linuxom/
http://www.monitor.si/clanek/solide-state-linux-1-del/
http://www.monitor.si/clanek/usmerjevalnik-z-ugnezdenim-linuxom-2-del/
Priprava pomnilniške kartice
Na delovni postaji z Linuxom sem pomnilniško kartico vstavil v bralnik kartic. Pomnilniške kartice v bralnikih USB se preslikujejo v navidezne diske SCSI, zato sem kartico videl kot /dev/sda, čeprav jo bo net4801 kasneje videl kot /dev/hda . S programom fdisk sem za vsak primer najprej pregledal podatke o izvirni navidezni topologiji, če bi se mi kaj sfižilo. Na moji kartici so bili taki podatki, na vaši pa bodo lahko drugačni:
Disk /dev/sda: 64 MB, 64225280 bytes
16 heads, 63 sectors/track, 124 cylinders
Units = cylinders of 1008 * 512 = 516096 bytes
Na začetku je treba malce sistemskega znanja o zaganjanju operacijskih sistemov. Če hočemo operacijski sistem naložiti s pomnilniške kartice, je za to potrebna zagonska koda. Ker kartice CF ponavadi uporabljamo v fotoaparatih za shranjevanje slik, ne pa za zaganjanje operacijskih sistemov, na njih ni take kode. Je pa priložena paketu Syslinux. Jaz sem uporabil različico 2.07, ker mi je preizkušeno delovala že prej, so pa tudi novejše različice. Iz paketa sem vzel datoteko mbr.bin (mbr pomeni "master boot record") z vsebino zagonskega sektorja in jo zapisal v prvi sektor na prvem navideznem cilindru pomnilniške kartice. To sem naredil z ukazom dd takole:
# dd if=mbr.bin of=/dev/sda bs=512 count=1
S tem je na kartici koda za prvo fazo zbujanja operacijskega sistema, ni pa še razdelkov niti operacijskega sistema.
Vsebino izvirne tabele razdelkov, kot jo naredijo v tovarni kartic, sem s programom fdisk zbrisal in jo ustvaril na novo. Naredil sem dva razdelka za datotečni sistem FAT-16, vsakemu sem namenil polovico kartice. Prvi razdelek sem označil kot aktivni in ustvarjeno zapisal na kartico. Navodila za uporabo programa fdisk so v omrežju, zato tega postopka ne opisujem.
Rezultat posega je bil:
Disk /dev/sda: 64 MB, 64225280 bytes
16 heads, 63 sectors/track, 124 cylinders
Units = cylinders of 1008 * 512 = 516096 bytes
Device Boot Start End Blocks Id System
/dev/sda1 * 1 62 31216+ 6 FAT16
/dev/sda2 63 124 31248 6 FAT16
Nato sem v obeh razdelkih ustvaril datotečna sistema:
# mkdosfs -n BERING /dev/sda1
# mkdosfs -n CONFIG /dev/sda2
Zakaj sem naredil dva razdelka, bomo videli kasneje.
Nato je bilo treba v aktivni razdelek zapisati drugi del zagonske kode. Ker sem za aktivni razdelek izbral logični disk /dev/sda1 z oznako BERING, sem s programom syslinux to naredil takole:
# syslinux -s /dev/sda1
Slednji korak je v datotečni sistem prvega razdelka zapisal datoteko ldlinux.sys. Zagonska koda jo bo našla z uporabo kazalca, ki kaže točno na sektor kartice, kamor jo je zapisal syslinux. Zato moramo paziti, da datoteke ldlinux.sys kasneje ne bi še enkrat zapisali na kartico, ker bi se lahko zapisala v kak drug sektor, to pa bi onemogočilo nalaganje Linuxa.
S tem je kartica v bralniku preorana in pripravljena za setev.
Ploščico net4801 so ravno nehali izdelovati, ker je AMD opustil proizvodnjo procesorjev. V omrežju je najti manjše in zmogljivejše. Tale meri 13,2 x 14,5 cm, ima procesor AMD Geode SC1100 s taktom 266 MHz in 128 MB pomnilnika. Ima tri ethernetne vmesnike 100 Mb/s, vrata RS-232 in priključek USB. Rabi pa borih 7 W moči. Seveda nima ventilatorja, niti pasivnega hladilnika. VIA je še nedavno tega izdelovala ploščice s taktom okrog 1 GHz. Še korak naprej na tem področju so naredili procesorji Atom. Prve ploščice s porabo 4 W in taktom 1,2 GHz so že dobavljive.
Križno konfiguriranje pomnilniške kartice za ploščico net4801
To, kar izvirna zagonska disketa uClibc Bering 3.1 naredi v disketnem pogonu namiznega računalnika, torej postavi Bering v navidezni disk v pomnilniku in ga od tam požene, hočemo, da naredi kartica CF na ploščici net4801. Za to pa je treba narediti nekaj popravkov izvirne konfiguracije.
Net4801 je za povprečnega uporabnika Oken videti nenavaden računalnik: nima niti grafičnega vmesnika za monitor niti priključka za tipkovnico. Ima pa zaporedna vrata ("serial port"). Kakor se "pravim programerjem" [1], ki smo si dopisovali še v HEX kodah in govorili samo v VELIKIH ČRKAH, zdi to nekaj samoumevnega, pa verjamem, da kateri mlajši kolegi ne vedo več, kaj je neumen terminal in za kaj bi bil koristen. No, namesto terminala danes uporabimo namizni računalnik s programom, ki posnema delovanje terminala (v Linuxu Minicom, v Oknih pa npr. HyperTerminal), in ga z zaporednim kablom priključimo na net4801. Slednji bo, ko bo oživel, sprva navzven komuniciral le prek zaporednega kabla.
Izvirna disketa z uClibc Beringom je konfigurirana tako, da sporočila ob nalaganju operacijskega sistema pošilja na grafični vmesnik. Ker ga na net4801 ni, moramo sporočila preusmeriti na zaporedna vrata, da jih bomo lahko prestregli na terminalu. Zato moramo popraviti nekaj Linuxovega drobovja.
Spodnje spremembe je priporočljivo delati na delovni postaji z Linuxom. Spreminjali bomo besedilne konfiguracijske datoteke. Urejevalniki besedil v okolju Windows ob zlomu vrstice vrinejo v besedilo dva znaka: <CR> in <LF>. Linux za to uporablja samo en znak: <LF>. Če syslinuxu podtaknemo konfiguracijsko datoteko z napačno prelomljenimi vrsticami, Linuxa ne bomo zagnali. Syslinux mešanja s krvno skupino Microsoft ne prenese najbolje.
Zato vsebino zagonske diskete prepišemo na neki imenik na namiznem stroju z Linuxom, npr. na /delovni. V tem imeniku bomo pripravili vsebino in jo nato zapisali na pomnilniško kartico. Najprej popravimo konfiguracijsko datoteko za syslinux, tako da se bodo sporočila med zaganjanjem izpisovala na zaporedna vrata namesto na kartico VGA. Hitrost izpisa določimo z ukazom serial, ki ga moramo dodati na začetku datoteke syslinux.cfg, in z ukazom append console, kjer usmerimo izpisovanje na zaporedna vrata ttyS0.
Zdaj bom izdal, zakaj sem naredil na pomnilniški kartici dva razdelka. V prvem bo Bering. V drugem pa bodo vse konfiguracijske informacije o Beringu in aplikacijskih paketih. Ko bo prišel novejši Bering, bom vsebino prvega razdelka "povozil" z novejšo kodo, konfiguracija v drugem razdelku pa bo ostala nespremenjena. Na ta način se bom izognil velikemu obsegu nepotrebnega dela ob nadgrajevanju sistema. uClibc Beringu s parametrom LEAFCFG= povemo, kam naj shranjuje konfiguracijske podatke. V mojem primeru sem ga usmeril na drugi razdelek pomnilniške kartice, to je na /dev/hda2.
Pazljivi bralec bo opazil, da sem višje zgoraj omenjal /dev/sda2, zdajle pa /dev/hda2. CF kartica, ki jo imamo tale trenutek v bralniku kartic, se delovni postaji predstavlja kot /dev/sda. V net4801, ko bo pripeta na vmesnik CF in preslikana v disk IDE, pa bo nosila oznako /dev/hda. V konfiguracijo moramo zapisati ciljno, ne trenutno stanje. Zato tudi izraz "križno" konfiguriranje.
Moj syslinux.cfg je bil po popravkih takle:
serial 0 19200
display syslinux.dpy
timeout 0
append reboot=bios
default linux initrd=initrd.lrp init=/linuxrc rw root=/dev/ram0 LEAFCFG=/dev/hda2:msdos
append console=ttyS0,19200 nodma=hda ide=nodma
Izvirna datoteka syslinux.dpy v Beringu vsebuje enostavno grafiko, ki se ob zagonu izpiše na VGA. Na net4801 to naredi pravo zgago na zaporednem terminalu, zato je najbolje iz nje zbrisati vse grafične smeti in pustiti samo znakovno vsebino ali jo prirediti lastnim potrebam. Jaz sem si jo popravil takole:
Bering-uClibc Firewall
(3.1 - April 2008)
(uClibc 0.9.28 Bering-uClibc team)
Setup for solid state mhttpd web server with USB key disk
Tom Erjavec, 2008
uClibc Bering na disketi je okleščen na minimum, prav zato, da gre na eno disketo. Nima niti podpore za disk. Če hočemo Linux naložiti s kartice CF, torej navidezno z diska, mora zagonska koda najprej videti disk. Zato potrebujemo gonilnik za IDE. Na distribucijskem CDju je med drugimi paketi tudi initrd_ide.lrp, slika začetnega diska RAM, ki je navedena v datoteki syslinux.cfg. Je enak kot initrd.lrp, le da vsebuje tudi gonilnike za naprave IDE, npr. pogon CD ali disk.
initrd_ide.lrp s CDja sem prenesel na kartico CF, zbrisal z nje izvirni initrd.lrp in novega preimenoval v initrd.lrp.
V paketu etc.lrp je datoteka inittab, ki med drugim Linuxu pove, na kateri vmesnik naj se zažene sistemski terminal ali, kot mu pravimo pravilno slovensko, "konzola". V izvirnem paketu je to na VGA, jaz pa sem ga na net4801 moral preusmeriti na zaporedna vrata. Paketi tipa lrp so stisnjeni datotečni arhivi. Če hočemo spreminjati njihovo vsebino, je treba paket najprej odpreti, narediti spremembo in nato vse datoteke zapakirati nazaj. To sem naredil z nizom ukazov:
cd /delovni
mkdir etc-lrp
cd etc-lrp
tar xzvf ../etc.lrp
joe etc/inittab
tar czvf ../etc.lrp *
Jaz sem uporabil urejevalnik joe, vi pa uporabite svojega najljubšega. V inittab sem preprečil zagon konzole na VGA in omogočil njen zagon na zaporednih vratih ttyS0. Popravljene vrstice so:
#tty1::respawn:/sbin/getty 38400 tty1
#tty2::respawn:/sbin/getty 38400 tty2
ttyS0::respawn:/sbin/getty -L ttyS0 19200 vt100
V imenik /delovni sem s CDja dodal še paket openntpd.lrp, ki sem ga potreboval za sinhronizacijo z resničnim časom iz omrežja. V imeniku /delovni je bila zdaj vsebina, ki se je sposobna zagnati s kartice CF. Zdaj sem jo lahko prenesel na kartico CF. Vse, razen datoteke ldlinux.sys, kot sem omenil zgoraj. Povezal sem se na datotečni sistem na kartici CF in prenesel pravo vsebino. Torej:
cd /delovni
rm ldlinux.sys
mount /dev/sda1 /mnt
cp * /mnt
Skoraj smo na koncu. V syslinux.cfg sem zapisal, da je konfiguracijska datoteka za Bering v razdelku /dev/hda2. Zato sem jo moral na koncu zares prestaviti tja.
rm /mnt/leaf.cfg
umount /mnt
mount /dev/sda2 /mnt
cp leaf.cfg /mnt
Vsebino leaf.cfg v razdelku /dev/sda2 sem popravil tako, da je vsebovala naslednje ključne vrstice:
LRP="root config etc modules iptables dhcpcd keyboard shorwall ulogd dnsmasq dropbear mhttpd openntpd webconf"
PKGPATH="/dev/hda2:msdos, /dev/hda1:msdos"
syst_size=16M
log_size=10M
ki naložijo potrebne aplikacijske pakete in zagotovijo dovolj prostora v navideznih diskih RAM za sistem in za zapisnike. V PKGPATH sta navedena dva razdelka. Najprej se naložijo aplikacije iz razdelka hda1. Če so kakšni aplikacijski paketi v hda2, se v živ sistem v pomnilnik naložijo še od tam in, če imajo slučajno enako ime, povozijo tiste, ki so se naložili iz hda1. To je uporabno, če želimo testirati kakšne spremembe. Z zgornjim mehanizmom jih lahko brez tveganja preizkusimo iz hda2, ne da bi spremenili vsebino izvirne kopije v hda1.
To je zaenkrat vse. Zložil sem roke in noge, prišil glavo. Čas je, da mojega pingvinskega Frankensteina z elektriko zbudim v življenje.
Pozdravljen, svet
Z zaporednim kablom sem povezal RS-232 na namiznem računalniku z RS-232 na net4801. Na namiznem Linuxu sem pognal minicom, terminalski program. Vključil sem napajalnik. BIOS je na terminal povedal svoje, pomežiknile so LED diode na net4801 in syslinux si je prisvojil terminal. Zagledal sem svoje ime iz datoteke syslinux.dpy. Naložil se je initrd. Začel se je nalagati Linux. Na disk RAM je potegnilo vse aplikacijske pakete iz PKGPATH. Frankenstein je odprl oči, vstati pa še ni mogel, ker ni imel naloženih pravih gonilnikov.
A tudi nekaterim fosilom iz časov pravih programerjev nam je dandanašnji težko delati na terminalu z zaporednim kablom. Urejevalnik besedil se nekako zatika. Prva moja želja je bila usposobiti omrežni priključek, da bi se od zaporedne konzole lahko poslovil in prešel na omrežni terminal čez ssh.
Osnovne konfiguracije uClibc Beringa na tem mestu ne bom opisoval. Dokumentacija je v omrežju, podoben postopek za starejši Bering pa sem opisal tudi v Monitorju maja 2004 v prispevku "Usmerjevalnik z ugnezdenim Linuxom". Omenil bom samo ključne informacije za pripravo konfiguracije, ki se nanašajo na konkretno strojno podlago in na cilj, ki sem si ga zadal v tem projektu.
Izza odra
Seveda ponavadi v prvo in v živo v resnici ne gre tako gladko, kot se bere potem, ko je po dobljeni bitki zapisano. Ko so me zafrkavali gonilniki za USB, sem si pomagal z branjem sporočil iz sistema. Ukaz dmesg je bil tu moj prijatelj.
Pri ugibanju, zakaj promet gre ven iz spletnega strežnika, ne pa tudi nazaj noter, sem si pomagal s knjižnico libpcap in orodjem tcpdump. Pognal sem ju na Bering_FW in na Bering_SVR. Filter sem nastavil samo na vmesnik in na tip prometa, ki me je zanimal, in skušal razbrati, kje je težava. Tako sem ugotovil, da Bering sam po sebi ne dela preslikav NAT med DMZ cono in zunanjim svetom, saj mi je uspela "mojstrovina" spraviti skozi javni vmesnik požarne pregrade pakete z zasebnimi IP naslovi, kar je strogo prepovedana internetna "dejavnost". Na podoben način sem ugotovil, da DNS zahtevki, ki jih generira spletni strežnik, ne pridejo do DNS strežnika na Bering_FW, zato spletni strežnik "visi" v prazno, dokler se ne izteče čakalni čas za odgovor. Zdravila za te ugotovitve so že opisana višje zgoraj v besedilu (funkcija NAT).
Največje preglavice pa mi je naredilo avtomatsko povezovanje datotek na ključku USB v datotečni sistem ob zagonu; Linux pravi temu mount. Z zapisom ene vrstice v datoteki /etc/fstab zagotovimo avtomatično povezovanje ene diskovne naprave v datotečni sistem. Nenavadni simptom, da sem razdelek na /dev/hda1 (na kartici CF na vmesniku IDE procesorske ploščice) lahko povezal, razdelka na /dev/sda1 (na ključku USB v vmesniku USB) pa ne, mi je dal misliti, da je nekaj narobe z gonilniki za USB. Proti temu je pa govorilo to, da je ročno povezovanje mount -t vfat /dev/sda1 /mnt delovalo brez težav. Razkopal sem initrd.lrp (postopek je opisan v glavnem članku) in v zagonski datoteki linuxrc ročno dodal ukaz za povezovanje - a brez uspeha. Na pomoč pri lovljenju hroščev mi je priskočil Uroš Juvan, s katerim sva se strinjala, da se avtomatsko povezovanje v datotečni sistem s tabelo /etc/fstab očitno dogaja prej, kot operacijski sistem skozi gonilnike začuti disk USB. Izkazalo se je, da res prihaja do časovnega problema pri vzporednem izvajanju zagonskih procesov v Linuxu. Gonilnik za USB razmeroma počasi začuti vstavljeni ključek v reži USB, medtem pa je Linux že povezal vse, kar je (uspešno) videl v tabeli fstab. Rešitev je bila zadržati izvajanje skripta /etc/init.d/mountall.sh za toliko časa, da bo gonilnik za USB opravil svoje delo. To sva naredila tako, da sva za 5 sekund zakasnila povezovanje iz tabele fstab:
#===== mountall.sh ======
RCDLINKS="S,S35"
# Mount local file systems in /etc/fstab.
# --- Inserted by Tom: 5 sec delay for USB driver to complete
sleep 5
# --- End insertion
echo "Mounting local file systems..."
mount -a
#
#===== End of mountall.sh ======
Popravke je bilo treba narediti v paketu etc.lrp. Naredil sem jih po podobnem postopku, kot je bil opisan zgoraj v odstavku "Križno konfiguriranje ...". Po tem popravku sta se gonilnik USB in Linuxov zagonski postopek sinhronizirala. Spletne strani na ključku USB so se avtomatsko povezale v strežni imenik spletnega strežnika.
Postavitev gonilnikov
net4801 uporablja ethernetne čipe National Semiconductors. Po navodilih uClibc Bering sem z orodjem lrcfg postavil v /etc/modules prave gonilnike. Najprej mora biti aktiviran crc32 in za njim natsemi. Gonilnike za druge tipe ethernetnih čipov sem izključil. Vse druge privzete gonilnike sem pustil aktivne, ker jih Bering potrebuje za delovanje. Ob novem zagonu se je gonilnik natsemi za ethernet brez težav postavil pokonci.
Na hitro sem nastavil še edini omrežni vmesnik, ki sem ga želel aktivirati, eth0. O tem nekaj besed kasneje.
Namesto da bi končal delo z gonilniki, sem naredil najprej korak vstran, da bi se čim prej znebil zaporedne konzole. Po navodilih sem postavil konfiguracijo za paket dropbear in si zagotovil terminalski dostop prek etherneta s protokolom ssh. Pri večji hitrosti je delo precej bolj udobno. Nato sem zaporedni konzolni kabel odstranil in se začel s Beringom pogovarjati na daljavo prek omrežja.
Postavljanje USB in branje ključka USB
Nazaj h gonilnikom. Da bi mi spletni strežnik lahko vsebino bral s ključka USB, sem moral strojno opremo pripraviti do tega, da bi najprej zaznala priključek USB, potem pa še, da bi Linux prepoznal ključek USB kot disk oziroma datotečni sistem. Kot zanalašč v navodilih za Bering ni niti besedice o tej problematiki. Po nekaj pogovorih z dr. Googlom mi je uspelo razvozlati USB skrivnosti na Linuxu.
USB podpora diskom se preslikuje prek gonilnikov SCSI. Za delovanje diska prek vmesnika USB so potrebni trije moduli: usbcore, usb-ohci in usb-storage. Njihove funkcije so splošna podpora naprav USB, zaznavanje nove naprave na vmesniku USB in podpora USB pomnilniške naprave. Za preslikovanje naprav skozi vmesnik SCSI pa so potrebni moduli scsi_mod, sd_mod in sg. Vse naštete module sem poiskal na distribucijskem CDju za uClibc Bering in jih prenesel v imenik /lib/modules. Nato sem jih dodal v konfiguracijsko datoteko za nalaganje modulov ob zagonu, /etc/modules:
# USB podpora
usbcore
usb-ohci
# SCSI & USB podpora za USB diskovni sistem
scsi_mod
sd_mod
sg
usb-storage
Po novem zagonu sistema so se vsi moduli pravilno naložili. Ključek USB sem vtaknil v vmesnik, ga logično povezal v datotečni sistem in uspešno pregledal vsebino:
# mount -t vfat /dev/sda1 /mnt
# ls /mnt
Paziti moramo, da ne pozabimo navesti tipa datotečnega sistema vfat, to je Microsoftov "Virtual FAT", dodatek sistemu FAT-16, ki podpira dolga imena datotek. Če tega ne storimo, je privzet tip FAT-16 in namesto dolgih imen vidimo 8-znakovne spakedranke imen.
Ključek USB je smiselno uporabljati s podporo dolgim imenom datotek in tudi Linux ga mora povezati v datotečni sistem prek pravega gonilnika. V nasprotnem primeru si pri spletnih straneh z osemznakovnimi imeni ne moremo dosti pomagati.
Ko sem že pri gonilnikih za diske ... Ko je konfiguracijsko delo končano, lahko iz varnostnih vzrokov na koncu zagonskega procesa dodamo vrstice, ki z ukazom rmmod odstranijo iz pomnilnika gonilnike za naprave IDE, ide-detect, ide-disk in ide-core. S tem je iz sistema preprečen dostop do vsebine na kartici CF. A če se sovražnik prek omrežja in dveh požarnih pregrad dokoplje do našega sistema, bi si lahko znova naložil gonilnike z diska RAM. Zato je treba te datoteke tudi odstraniti iz imenika /boot/lib/modules/ na disku RAM, kamor se zapišejo ob vzpostavljanju sistema. Karkoli že morebitni vlomilec zdaj naredi v pomnilniku, je neškodljivo. A te spremembe ne smemo shraniti na kartico CF, drugače nov zagon sistema s kartice ne bi bil mogoč. Omenjeni postopek je smiselno narediti šele čisto na koncu, ko sistem stabilno deluje.
Druge storitve
Potreboval sem še odjemalca omrežnega časa, ntpd, ker sem želel, da so zapisi v mojih dnevnikih sinhronizirani, če bi kdaj le prišlo do kakšnih varnostnih težav. Kakšne posebne nastavitve ni bilo treba narediti, zagon paketa openntpd pa sem zagotovil že prej z vpisom v leaf.cfg. Enako velja za spletni strežnik. Paket mhttpd je mini izvedba spletnega strežnika, ki pa je po privzeti konfiguraciji nastavljen tako, da zna streči samo angleško kodno tabelo, kot se je pokazalo pri prvem preizkusu spletnih strani s slovenskimi znaki. To težavo sem odpravil tako, da sem v konfiguracijsko datoteko za mhttpd, /etc/mini_httpd.conf, dodal vrstico:
charset=UTF-8
Ob tem velja omeniti, da je mhttpd v izvirni konfiguraciji že povezan s paketom webconf, ki omogoča upravljanje z uClibc Beringom na daljavo prek spleta. Zato je imenik, iz katerega mhttpd streže spletne strani, določen kot:
dir=/var/webconf/www
To nastavitev sem pustil nedotaknjeno, ker sem želel obdržati webconf. Če ga nisem hotel povoziti z vsebino ključka, sem moral med zaganjanjem sistema ustvariti nov imenik, kamor bom kasneje povezal vsebino ključka USB. Odločil sem se, da v ta namen spremenim vsebino datoteke linuxrc, katere naloga je vzpostavljanje začetnega stanja. linuxrc je skrit v datoteki initrd.lrp, ki je stisnjena slika začetnega diska RAM. Tak datotečni sistem lahko odpremo z modulom loop, ki pa ga moramo najprej naložiti. Postopek je opisan spodaj:
# namesti modul "loop"
insmod loop.o
# poveži CF kartico v RAM disk
mount /dev/hda1 /mnt
cd /mnt
# naredi začasni imenik
mkdir zacasni
cd zacasni
# vzemi kopijo initrd
cp /mnt/initrd.lrp .
# razpihni jo
mv initrd.lrp initrd.gz
gunzip initrd.gz
# ustvari prazen imenik, kamor povežeš začetni RAM disk
mkdir initrd.dir
# poveži začetni RAM disk v ta imenik s pomočjo modula loop
mount initrd initrd.dir -o loop
# pojdi v sliko začetnega RAM diska in naredi spremembe
cd initrd.dir
Na tem mestu sem se zakopal v datoteko linuxrc in ji na koncu dodal ukaz, ki je ustvaril nov imenik:
qt mkdir /var/webconf/www/html
Zdaj je bilo treba spremenjeno datoteko zapakirati nazaj v paket initrd.lrp. To sem naredil takole:
# razveži začetni RAM disk iz imenika
umount initrd.dir
# stisni datoteko initrd
gzip initrd
# preimenuj stisnjeno datoteko nazaj v paket
mv initrd.gz initrd.lrp
Ta paket sem vrnil nazaj na kartico CF.
Dokončna rešitev povezovanja vsebine ključka v strežni imenik spletnega strežnika je bil vpis dodatne vrstice v tabelo /etc/fstab, ki omogoča avtomatsko povezovanje ob zagonu Linuxa [ 7 ]. Postopek sem opravil na enak način, kot je opisan zgoraj za datoteko inittab.
#dev_name mnt_point fs-type options dump pass
/dev/sda1 /var/webconf/www/html vfat defaults 0 0
Pri avtomatskem povezovanju vsebine ključka USB v datotečni sistem pa se mi pokazala največja težava, katere rešitev je opisana v odstavku "Izza odra".
Po novem zagonu sistema je novi pingvin sam po sebi dobro dihal, a je bil v omrežnem smislu še mrtev. Čas je bil za podrobnejši pregled omrežnih nastavitev.
Konfiguriranje omrežja
Na sliki 4 je primer omrežnega okolja, kamor sem nameraval postaviti novi strežnik. Bering_FW je požarna pregrada, kot sem jo opisal v prispevkih iz leta 2004 in 2005. Bering_SVR je spletni strežnik, ki ga postavljamo.
Omrežna postavitev opisane rešitve
Da bi postavitev delovala, sem moral najprej na Bering_FW in tam nastaviti tretji fizični omrežni vmesnik, eth2, ki do zdaj ni bil uporabljen:
auto eth2
iface eth2 inet static
address 192.168.2.1
netmask 255.255.255.0
broadcast 192.168.2.255
Prevajanje naslovov DNS sem v datoteki /etc/resolv.conf nastavil takole:
nameserver <DNS naslov mojega internetnega operaterja>
kjer je treba namesto zgornjega puščičastega oklepaja zapisati ustrezen naslov IP. Nato sem moral na Bering_FW popraviti nastavitve požarne pregrade Shorewall. V /etc/shorewall/zones sem moral določiti nov omrežni segment, ki bo opravljal funkcijo "demilitarizirane cone", kot izrazoslovje požarnih pregrad imenuje strežniški segment:
#ZONE DISPLAY COMMENTS
dmz DMZ Demilitarized zone
V datoteki /etc/shorewall/interfaces sem povezal nov logični omrežni segment požarne pregrade s fizičnim vmesnikom:
#ZONE INTERFACE BROADCAST
dmz eth2 detect
uClibc Bering je privzeto nastavljen tako, da sprejema zahtevke za prevajanje naslovov DNS samo iz notranjega zasebnega omrežja, ki je na vmesniku eth1. Če sem hotel novemu spletnemu strežniku v coni DMZ omogočiti prevajanje naslovov DNS, sem moral v datoteko /etc/dnsmasq.conf dodati vrstico:
interface=eth2
In zdaj je prišlo na vrsto loščenje pravil za požarno pregrado. Za novi segment je bilo treba zagotoviti preslikovanje naslovov demilitarizirane cone v javne naslove in nasprotno, tako imenovani NAT, Network Address Translation. Požarna pregrada Shorewall na Bering_FW bo v vsakem paketu spletnega strežnika zamenjala naslov 192.168.2.2 v svoj zunanji IP naslov in ga poslala v omrežje, kot da izvira od nje. V nasprotnem primeru komuniciranje z zunanjim svetom pred požarno pregrado Bering_FW ne bi bilo mogoče. V datoteki /etc/shorewall/masq sem zato dodal pravilo:
#INTERFACE SUBNET
eth0 eth2
Na koncu je bilo treba požarno pregrado še prepričati, da bo prepuščala pakete iz spletnega strežnika, ki bodo morali ven v omrežje, in da bo uporabnike iz omrežja spustila do novega spletnega strežnika. Po privzeti politiki namreč Shorewall ne dovoli ničesar takega. Bering_FW ima nastavljene tri segmente: net (zunanji, javni segment), loc (notranji, privatni segment) in dmz (demilitarizirana cona za spletni strežnik). Je še četrto področje, fw, to je požarna pregrada sama. V datoteko pravil za Shorewall, /etc/shorewall/rules, sem dodal več pravil.
Najprej sem s funkcijo DNAT (Destination NAT) omogočil zunanjemu svetu, da vidi spletni strežnik Bering_SVR v demilitarizirani coni:
#ACTION SOURCE DEST PROTO DEST SOURCE
# PORT PORT
DNAT net dmz:192.168.2.2 tcp 80 -
Na Bering_FW vrtim proxy strežnik za prevajanje naslovov DNS. Zato ga spletni strežnik lahko uporabi v ta namen. Moral pa sem Shorewallu povedati, naj spusti zahtevke DNS iz demilitarizirane cone do sebe:
ACCEPT dmz fw udp 53
ACCEPT dmz fw tcp 53
Iz notranjega omrežja sem hotel priti do spletnega strežnika v demilitarizirani coni s protokolom ssh za terminalski dostop:
ACCEPT loc dmz tcp 22
Spletnemu strežniku sem moral omogočiti dostop do časovnih strežnikov v omrežju:
ACCEPT dmz net udp 123
To so bila vsa pravila, ki sem jih potreboval na požarni pregradi Bering_FW za delovanje spletnega strežnika.
Posebnosti nastavitev na spletnem strežniku
Spletni strežnik sem postavljal na distribuciji Linuxa, ki sicer služi internetnemu prehodu s požarno pregrado. Zato sem moral nekaj nastavitev spremeniti.
Za začetek sem se znebil vseh omrežnih vmesnikov, razen javnega. Seveda javnega, saj bo spletni strežnik stregel javnim zahtevkom. Zato sem vzpostavil le vmesnik eth0:
auto eth0
iface eth0 inet static
address 192.168.2.2
netmask 255.255.255.0
broadcast 192.168.2.255
gateway 192.168.2.1
Hm, "javni" vmesnik z zasebnim naslovom "192.168.2.2" ?? Ja. Postavljen je v resnici v coni DMZ požarne pregrade BERING_FW, ta pa ga z malo prej omenjenim pravilom DNAT odpira v javno omrežje in hkrati s pravilom NAT preslika njegov zasebni naslov v resnični javni naslov. Čista stvar. Drugih omrežnih vmesnikov nisem potreboval. Za privzeti usmerjevalnik je določen naslov DMZ vmesnika na Bering_FW.
Prevajanje naslovov DNS sem v datoteki /etc/resolv.conf nastavil takole:
nameserver 192.168.2.1
Spet zasebni naslov? Ja, ker je funkcijo proxy DNS strežnika za DMZ segment prevzel Bering_FW.
Požarna pregrada na spletnem strežniku
V datoteki /etc/shorewall/zones sem nastavil dve coni: fw in net.
V datoteki /etc/shorewall/interfaces sem nastavil en vmesnik:
#ZONE INTERFACE BROADCAST OPTIONS
net eth0 detect routefilter
Pri tem velja poudariti, da sem moral zbrisati nastavitev norfc1918 v polju OPTIONS. Slednje namreč preprečuje uporabo zasebnih naslovov na vmesniku. Ker je v mojem primeru na "javnem" vmesniku eth0 zasebni naslovni prostor (192.168.2.2), to dvoje ne gre skupaj.
V datoteki /etc/shorewall/policy sem pustil izjemno restriktivno politiko:
#SOURCE DEST POLICY LOG net all DROP ULOG
# THE FOLLOWING POLICY MUST BE LAST
all all REJECT ULOG
ali, z drugimi besedami: požarna pregrada ne dovoli ničesar ... razen tistega, kar bom eksplicitno dovolil s pravili.
Nisem se smel zavajati, da je spletni strežnik že itak v coni DMZ za požarno pregrado Bering_FW. Pravilo DNAT preslikuje vse zahtevke za vrata 80 iz javnega naslova Bering_FW na vmesnik 192.168.2.2 spletnega strežnika. Torej se bo v resnici vesoljni internet pasel po tem vmesniku. Zato je prav, da je politika zelo restriktivna.
V datoteki /etc/shorewall/rules sem nato dovolil naslednji promet (pazljivo oko bo ugotovilo, da je zgradba pravil v novejšem Shorewallu enostavnejša kot prej, npr. na starejšem Bering_FW):
#ACTION SOURCE DEST PROTO DEST SOURCE
DNS/ACCEPT fw net
SSH/ACCEPT net fw
Web/ACCEPT net fw
NTP/ACCEPT fw net
to pomeni, da je spletni strežnik smel v omrežje spraševati za prevajanje DNS imen in sinhronizirati čas z omrežjem, iz omrežja pa je lahko sprejel samo zahtevke za streženje spleta in za kriptirane SSH povezave. To je vse. Za vse drugo je nedosegljiv in neopazen.
Na novo sem zagnal spletni strežnik. Malce preizkušanja je hitro pokazalo, da se čas na spletnem strežniku ne ujema z resničnim časom. Poseči sem moral še v datoteko
/etc/TZ in nastaviti časovno območje:
CET-1CDT
to pomeni nekaj takega kot "tukaj imamo C(entral)E(uropean)T(ime), ki je 1 uro pred greenwiškim časom in ga poleti prilagajamo v C(entral-european)D(aylight-saving)T(ime)". Pravzaprav razumljivo. Vse črke so brezpredmetne, pravilno sinhronizacijo zagotavlja le število -1.
Končana igrača je prav lična, neslišna, ne porabi skoraj nič elektrike, ne potrebuje vzdrževanja, po zmogljivostih pa, zahvaljujoč optimizirani programski opremi, že s tako skromnim procesorjem daleč presega hitrost internetnih povezav, ki jih imamo rezidenčni uporabniki.
Nalaganje vsebine spletnih strani
S katerimkoli orodjem že se bo kdo lotil izdelave spletnih strani - velika verjetnost je, da jih bo pripravljal v okolju Windows, ker so orodja bolj razširjena (in verjetno prijaznejša). Pri tem je treba paziti, da bo kodna tabela izdelka enaka nastavitvi kodne tabele na strežniku. Jaz sem jo zgoraj nastavil na UTF-8, morda pa jo bo kdo drug raje na ISO 8859-2 ali pa morda celo na kakšno Microsoftovo kodno tabelo.
Nalaganje strani na spletni strežnik je enostavno: vsebino prepišeš na ključek in ga vtakneš v strežnik. Če pa strežnik ni na polici, temveč nekje daleč v omrežju, je priročno uporabiti orodje WinSCP [ 5 ], ki v okolju Windows omogoča uporabo tunela SSH za varno prenašanje datotek. Pri nastavljanju je nekaj zvijač, brez katerih se WinSCP ne ujame s strežnikom SSH Dropbear na Beringu. Napotki so opisani v [ 5 ].
Test odzivnosti
Ko je moj spletni strežnik brez gibljivih delov preizkušeno deloval pravilno, sem si vzel čas in izdelal spletno predstavitev rodbinskega drevesa Erjavcev. Vsebuje 62 strani html in 153 fotografij ali skenov velikosti od 6 KB do 303 KB. Vse slike so glede velikosti optimirane za splet.
Zanimalo me je, kako bi se strežnik obnašal pod bremenom več kot enega uporabnika, to je mene, čeprav za zasebni spletni strežnik najbrž ni pričakovati kakšnih hudih obremenitev.
V omrežju sem našel orodje Webserver Stress Tool [ 6 ]. Namenjeno je obremenilnemu testiranju spletnih strežnikov. Simulira določeno število uporabnikov, ki z nastavljivo frekvenco in vzorcem "klikajo" po straneh spletnega strežnika, orodje pa medtem meri odzivnost strežnika. Naložil sem si preizkusno različico, ki je omejena na deset sočasnih simuliranih uporabnikov, a to je bilo zame videti dovolj.
Nastavil sem naslednje parametre testiranja:
Nastavitve so ustrezale prikazu 120 strani na minuto oziroma 7200 na uro. Ker resnični uporabniki ne klikajo na 5 sekund, temveč, na primer, na eno minuto (recimo, da vmes še kaj preberejo), bi ta test realno ustrezal obremenitvi z več kot 100 uporabniki. Obremenilni test sem izvajal na opisani strojni opremi s procesorjem NSC SC1100, 266 MHz, 128 MB RAM, operacijski sistem Linux uClibc Bering 3.1, spletni strežnik mini_httpd.
Čeprav se morda čudno sliši, a procesor s skromnimi 266 MHz pod pilotiranjem turbo pingvina dejansko vzleti. Povprečno testno spletno stran iz kombinacije besedila in fotografij, resda statično, postreže v 17 milisekundah.
Testno okolje sem pripravil tako, da ni vplivalo na merjenje odzivnosti samega strežnika. Na povezavi med odjemalcem in strežnikom sem moral torej zagotoviti pasovno širino, ki bi bistveno presegala generirani promet. Izkazalo se je, da je hitrost 100 Mb/s dovolj velika. Strežnik in odjemalca sem tako neposredno povezal prek 100 Mb/s ethernetnega stikala. Odzivnost strežnika je prikazana na grafih spodaj.
Kot zanimivost, kako omrežje vpliva na uporabniško izkušnjo, si poglejmo še Grafa 5 in 6, ki sem ju izmeril nad isto vsebino, le da je bil odjemalec tokrat na drugi strani Ljubljane v omrežju drugega internetnega operaterja. Vidimo, da se je čas za prikaz strani povprečno podaljšal za približno 17-krat, v najslabšem primeru pa za 100-krat.
Promet proti strežniku (zahtevki) je bil v povprečju okrog 140 Kb/s, promet proti odjemalcu (odgovori) pa povprečno okrog 4 Mb/s.
Čisto vsi uporabniki so bili "postreženi" v manj kot 20 milisekundah.
Strežnik je izpolnil povprečni zahtevek v 17 ms. Ob povprečni frekvenci 2 zadetka na sekundo je bil strežnik sposoben obdelati še precej večje breme. Če izračunamo "na prst" - v 1 sekundi: 1000 ms/17 ms = 58 zahtevkov ali približno 30-krat več kot v tem testu.
V 300 sekundah testiranja ni bilo med 7200 generiranimi zahtevki niti enega, katerega izpolnitev bi trajala dlje kot 20 milisekund, kar precej pa jih je bilo okrog 14 ms. Niti en zahtevek ni ostal nerealiziran.
Čas odziva se v primerjavi z Grafom 3 bistveno podaljša na poti prek omrežij dveh internetnih operaterjev. Strežnik je hiter, omrežji pa nista. K temu veliko prispeva asimetrija rezidenčnih internetnih povezav. Smer proti omrežju je precej počasnejša kot smer proti uporabniku. Za odzivnost strežnika pa je pomembno ravno nasprotno.
V primerjavi z Grafom 2 se uporabniški čas čakanja na odziv prek dveh omrežij internetnih operaterjev podaljšuje glede na zasičenost omrežja. Zato je distribucija na tem grafu večja, medtem ko je na Grafu 2 v idealnih laboratorijskih razmerah sploh ni.
Sklep
Rezultat opisanega večernega projekta je "Solid State Web Server" oziroma spletni strežnik brez gibljivih sestavnih delov. Njegova odlika ni le večja zanesljivost, temveč tudi minimalna poraba električne energije, to pa ob nepretrgani uporabi danes ni zanemarljiva lastnost. Poraba 7 W je približno 50-krat manjša od porabe namiznega računalnika. Poleg tega deluje v popolni tišini in ni moteč niti v dnevni sobi. Zahteva tudi minimalno prostora in ga je mogoče na neopaznem mestu pritrditi kar na zid. Programska oprema omogoča celovito upravljanje skozi kriptirano povezavo na daljavo, zato je lahko strežnik na poljubni lokaciji. Vgrajena požarna pregrada in možnost izklopa gonilnikov za pomnilniško kartico mu zagotavljata visoko stopnjo varnosti.
Opravljen laboratorijski test odzivnosti je ustrezal realni stalni obremenitvi z več kot 100 uporabniki. Ocenjena zmogljivost strežnika naj bi bila do 30-krat večja od testirane. Ne glede na oceno zgornje meje zmogljivosti že opravljeni preizkus potrjuje, da njegova procesna moč veliko presega potrebe, ki jim je namenjen.
Reference
[1] http://en.wikipedia.org/wiki/Real_Programmer
[2] http://leaf.sourceforge.net/bering-uclibc/
[3] http://www.e-bulles.be/bubble/Bering-uClibc_on_Compact-Flash
[4] http://www.telltronics.org/software/Bering/BeringCross.html
[5] http://www.mail-archive.com/leaf-user@lists.sourceforge.net/msg18822.html
[6] http://www.paessler.com/webstress/download
[7] http://www.tuxfiles.org/linuxhelp/fstab.html