ELEKTROTEHNI ˇ SKI VESTNIK 88(1-2): 54–60, 2021 IZVIRNI STROKOVNI ˇ CLANEK Varen zagon nepreverjene programske kode v sistemu PIVO ˇ Ziga Rojec Univerza v Ljubljani, Fakulteta za elektrotehniko, Trˇ zaˇ ska 25, 1000 Ljubljana, Slovenija E-poˇ sta: ziga.rojec@fe.uni-lj.si Povzetek. PIVO (programerjevo interaktivno vadbeno okolje) je sistem za interaktivni ˇ studij algoritmiˇ cnega razmiˇ sljanja in programiranja, razvit na Fakulteti za elektrotehniko Univerze v Ljubljani. Uporabljamo ga za spodbujanje samostojnega ˇ studija pri predmetih, kjer se pouˇ cuje programiranje, primeren pa je tudi za izvajanje izpitov in tekmovanj. ˇ Studentje v sistemu prevzamejo nalogo, reˇ sitev zanjo razvijejo v svojem okolju, na streˇ znik pa oddajo zakljuˇ ceno izvorno kodo. Uporabnikova koda se na centralnem streˇ zniku prevede, zaˇ zene in preizkusi. Oddana izvorna koda je pogosto nepopolna in potencialno ˇ skodljiva za neprekinjeno delovanje streˇ znika. V ˇ clanku podrobno opisujemo naˇ cine za varen zagon nepreverjene kode, ki temeljijo na varnostnih mehanizmih jedra operacijskega sistema Linux. Z uporabo teh mehanizmov lahko streˇ znik varno in hitro souporablja veˇ c uporabnikov. Sistem PIVO so ˇ studentje dobro sprejeli, pozitivni uˇ cinki pri ˇ studiju pa so bili merljivi ˇ ze po prvih semestrih uporabe. Kljuˇ cne besede: pouˇ cevanje na daljavo, programiranje, programska koda, vsebnik, Seccomp, ˇ skodljiva koda A secure run of an untrusted program code in the PIVO system PIVO (Programmer’s Interactive Excercise Environment) is an interactive online practicing tool for algorithmical thinking and programming developed at the Faculty of Electrical En- gineering, University of Ljubljana, Slovenia. It is used for independent study, excercising and examination in program- ming courses. Through the sistem, a student aquires a task, develops a solution in his/hers personal working environment and submits the source code to the server. The code is compiled, run and tested in real time. The submitted code can be incomplete and potentially harmful to the server. This paper proposes techniques for untrusted code sandboxing based on built-in Linux security mechanisms. Using the techiques, PIVO serves multiple users safely and fast. Among the students, the PIVO environment is well accepted, its impacts and positive study improvements are measurable already after only a few semesters of usage. Keywords: distance learning, programming, program code, container, Seccomp, malicious code 1 UVOD Raˇ cunalniˇ ski sistemi so za sodobnega ˇ cloveka skoraj ne- izogibni spremljevalci. Raˇ cunalnik nam v razliˇ cnih obli- kah ne sluˇ zi le kot naˇ cin zabave, temveˇ c je tudi moˇ cno, velikokrat nenadomestljivo delovno orodje. Da lahko to orodje v polnosti izkoristimo, ni dovolj le poznavanje prednaloˇ zene programske opreme. Za reˇ sevanje nekon- vencionalnih problemov in koriˇ sˇ cenje vseh raˇ cunskih moˇ znosti je potrebno znanje programiranja. V tehniˇ skih, naravoslovnih in poslovodskih poklicih je Prejet 23. oktober, 2020 Odobren 8. januar, 2021 programiranje postalo skorajda neizogibno. Prav zato je naloga viˇ sjeˇ solskega izobraˇ zevalnega procesa, da mlade kadre ˇ cim bolje pripravi na t. i. algoritmiˇ cni naˇ cin razmiˇ sljanja. Pouˇ cevanje programiranja v visokem ˇ solstvu je zah- tevna naloga. ˇ Studentje, ki vstopajo v prvi letnik, pri- hajajo z najrazliˇ cnejˇ simi predznanji, od popolnega ne- znanja programiranja do takih, ki si z znanjem progra- miranja ˇ ze sluˇ zijo denar. Da ˇ studente v prvem letniku opremimo z minimalnim nivojem znanja algoritmiˇ cnega razmiˇ sljanja, na Fakulteti za elektrotehniko Univerze v Ljubljani pri pouˇ cevanju uporabljamo naslednje pri- stope: 1) predavanja konceptov, 2) vaje na primerih, 3) preverjanje samostojnega dela ˇ studentov (sprotne obveznosti), 4) preverjanje znanja (izpit). Naj bo kakovost predavanj in vaj na ˇ se tako visoki ravni, je kljuˇ c do uspeha v znanju programiranja neiz- ogibno povezana s tretjo toˇ cko – koliˇ cino samostojnega dela ˇ studentov. Programiranje je v osnovi ustvarjalen proces sinteze, ne samo analize. Zato je naˇ cinov do pravilnih reˇ sitev zadanega problema veliko, lahko celo neskonˇ cno mnogo. V jeziku pedagogov: pravilnih odgovorov na eno izpitno vpraˇ sanje je lahko veˇ c, saj so moˇ zni razliˇ cni naˇ cini pristopa k nalogi. Ker je vpis na tehniˇ skih fakultetah visok, imamo vsako leto opravka z okvirno ˇ stiristo ˇ studenti samo v prvem letniku [1]. V tem primeru postane preverjanje znanja zaradi ˇ casovnih in kadrovskih omejitev izjemno zahtevno. Za namen ocenjevanja je PIVO - PROGRAMERJEVO INTERAKTIVNO V ADBENO OKOLJE 55 treba vsak program analizirati, po moˇ znosti pognati v okolju, za katero je napisan, vstaviti vhodne in preveriti izhodne podatke ter dodeliti ocene uspeˇ snosti. To delo je brez napak in v doglednem ˇ casu teˇ zko opraviti roˇ cno. Ko zaˇ cnemo razvijati sistem, ki bo na osebnem raˇ cunalniku avtomatiˇ cno zaganjal in preverjal oddano kodo, se je treba zavedati, da je ˇ studentska koda lahko nepopolna. Naˇ stejmo nekaj najpogostejˇ sih primerov, ko najrazliˇ cnejˇ sa koda, ki jo zaporedoma preizkuˇ samo, pov- zroˇ ci teˇ zave: vsebuje sintaktiˇ cne napake (ni prevedljiva), vsebuje neskonˇ cne zanke, naslavlja globalne spremenljivke, znakovno kodiranje izvorne kode se ne ujema s sistemskim kodiranjem. ˇ Studentje lahko takˇ sno kodo oddajo v dobri veri, da se bo med avtomatskim pregledovanjem izkazala za ustrezno, ˇ se zlasti ˇ ce se med tem procesom izvedejo dodatni testi, ki ˇ studentu prej niso bili prikazani. Obstaja pa tudi drug spekter teˇ zav, povezanih z zaganjanjem tuje kode – lahko je namensko ˇ skodljiva. Za namen upoˇ casnitve, onemogoˇ canja ali vohunjenja na sistemu lahko: prekomerno izrablja sistemske vire, ustvarja ali bere sistemske datoteke, ustvarja nove procese, komunicira z zunanjim omreˇ zjem, in podobno. ˇ Ce sistem, ki bo pregledal kodo, poˇ zenemo le nekajkrat na semester, je stabilnost sistema dokaj enostavno zagotoviti z ustrezno skripto, ki omejuje npr. ˇ cas izvajanja doloˇ cenega procesa. Vse preostalo lahko reˇ simo z zaganjanjem sistema v virtualnem raˇ cunalniku in ustvarjanjem varnostnih kopij slike tega virtualnega raˇ cunalnika. Za zmanjˇ sanje teˇ zav v takih sistemih je proti avtorjem namensko ˇ skodljive kode treba uvelja- vljati posebne doloˇ cbe izpitnega reda, npr. negativne toˇ cke ali dodatno delo. ˇ Ce razvijamo sistem, ki bo deloval interaktivno in pregledoval ter ocenjeval naloge v realnem ˇ casu (na primer na izpitu), pa je arhitekturo takega sistema treba skrbno naˇ crtovati. Sistem mora ostati nedotaknjen in procesi morajo delovati neprekinjeno – tako v primeru zagona dobre kot ˇ skodljive kode. V priˇ cujoˇ cem prispevku bomo predstavili naˇ s pristop za izgradnjo sistema, v katerem je mogoˇ ce varno zagnati programsko kodo, ki ji a priori ne zaupamo, preizkusiti njeno delovanje in ohraniti nedotaknjenost sistema. 2 OBSTOJE ˇ CI SISTEMI AVTOMATSKEGA PREVERJANJA PROGRAMSKIH RE ˇ SITEV V tem kratkem podpoglavju navedimo nekaj obstojeˇ cih sistemov, ki so namenjeni zagonu nepreverjene kode in imajo predvsem pedagoˇ ski namen. V grobem lahko sisteme razdelimo na dva tipa, in sicer glede na to, kje se oddana koda izvede; to se lahko zgodi: v uporabnikovem brskalniku ali na gostiteljskem streˇ zniku. Na svetovnem spletu lahko najdemo nekaj strani, kjer je mogoˇ ce programsko kodo zagnati prek brskalnika. Spletna stran w3schools.com, na primer, uporabnikom strani omogoˇ ca, da preizkuˇ sajo kratke programe v jeziku JavaScript – ti se izvedejo v uporabnikovem brskalniku. Stran, ki izvaja kodo JavaScript v brskalniku, je dokaj enostavna za implementacijo, saj veˇ cina spletnih brskal- nikov ˇ ze v osnovi podpira interpretacijo tega jezika, zato jih na spletu najdemo veliko, npr. [6], [7], [8] in ˇ stevilne druge. Na spletu prav tako najdemo sisteme, ki ponujajo zagon uporabnikove kode tudi v drugih programskih jezikih. Pri tem velja omeniti, da so v preteklosti ne- kateri sistemi delovanje programske kode le simulirali z jezikom JavaScript v brskalniku, kar znatno omejuje moˇ znosti in smiselnost preverjanja delovanja kode. ˇ Ce ˇ zelimo program zares preizkusiti, je treba kodo zagnati na streˇ zniku, v okolju, ki ˇ ze v osnovi interpretira oz. pre- vede in zaˇ zene zapisano kodo. Primer takega sistema najdemo na spletni strani codechef.com, ki podpira izvajanje kode veˇ c kot 50 razliˇ cnih programskih jezikov [9]. Indijska spletna stran je ustvarjena tako, da lahko go- sti tudi programerska tekmovanja. Codecademy.com je stran, ki nudi interaktivne seminarje za uˇ cenje razliˇ cnih programskih jezikov, tudi v tem sistemu lahko izvedemo kodo na oddaljenem streˇ zniku [10]. Omeniti velja ˇ se codepad.org, ki je namenjen predvsem shranjevanju in medsebojnemu deljenju kratke programske kode [11]. Zanimivost sistema je, da za nalaganje in izvajanje kode ne zahteva registracije uporabnika, saj naj bi njihovi var- nostni ukrepi v veliki meri onemogoˇ cali zlorabe sistema in omogoˇ cali njegovo neprekinjeno delovanje. Enega od redkih odprtokodnih sistemov za interaktivno uˇ cenje programskega jezika C++ najdemo na omreˇ zju IRC. Gre za Geordi, t. i. bot-a, ki prevaja in poganja koˇ sˇ cke kode, ki jih v klepetalnik IRC vnaˇ sa uporabnik. Za zagotavljanje varnosti sistem Geordi uporablja poseben Linuxov mehanizem Seccomp [14]. V slovenskem prostoru je znan portal putka.si (Preverjanje uˇ cinkovitosti, temeljitosti in korektnosti algoritmov) Zavoda za raˇ cunalniˇ sko izobraˇ zevanje Lju- bljana, namenjen pouˇ cevanju programiranja in izvedbi tekmovanj. V Putki so naloge dostopne prek spletne strani, reˇ sitev pa uporabnik/ˇ student naloˇ zi v obliki da- toteke izvorne kode. Koda se na streˇ zniku prevede, ob izvajanju pa so onemogoˇ ceni vsi kritiˇ cni sistemski klici, s ˇ cimer upravljavci zagotavljajo varnost streˇ znika. Onemogoˇ ceno je tudi npr. ustvarjanje datotek in delo z njimi, upravljavci sistema pa lahko za posamezne naloge omogoˇ cijo doloˇ cene kritiˇ cne sistemske klice. Na ta naˇ cin je sistem sicer fleksibilen, vendar dojemljiv za varnostne probleme. Med obstojeˇ cimi interaktivnimi uˇ cnimi sistemi za pro- gramiranje v domaˇ cem prostoru izstopa Projekt Tomo 56 ROJEC [2]. Za podporo pouˇ cevanju programiranja ga upora- bljajo na veˇ c srednjih ˇ solah in fakultetah. Tomo deluje tako: Uporabnik se registrira na spletno mesto, kjer lahko izbira med nalogami, ki so mu na voljo. Streˇ znik uporabniku servira nalogo, zapisano v obliki skriptne datoteke (npr. tipa .py za Python). V skripto uporabnik vnaˇ sa programsko reˇ sitev in jo poˇ zene na lokalnem raˇ cunalniku. Del skripte, ki jo servira Projekt Tomo, vsebuje tudi vse ukaze, ki izhode uporabnikovega pro- grama poˇ slje nazaj na streˇ znik za ocenjevanje. Poudariti je treba, da se koda izvede zgolj lokalno, streˇ znik pa skrbi le za preverjanje pravilnosti izhodov programa. Iz skripte lahko torej ˇ ze nespreten uporabnik vnaprej izve za vse teste, ki bodo izvedeni na njegovi kodi. V nekaterih primerih je tak naˇ cin dela zelo dobrodoˇ sel (npr. za podporo pedagoˇ skemu delu pri laboratorijskih vajah), velikokrat pa se uporabniki zato osredotoˇ cijo zgolj na zadovoljevanje testnih primerov (ker so vsi znani), in ne na razvoj programa, ki bi celostno reˇ seval zadani problem [3], [4]. Prav zato je sistem manj primeren za konˇ cno preverjanje znanja in izvedbo tekmovanj. 3 SISTEM PIVO Zaradi nekaterih pomanjkljivosti obstojeˇ cih sistemov, predvsem pa zaradi enostavnejˇ sega vzdrˇ zevanja v hiˇ si smo se pri avtomatskem pregledovanju nalog iz progra- miranja odloˇ cili za vzpostavitev lastnega sistema, ki naj- bolje ustreza naˇ cinu dela pri pouˇ cevanju programiranja na naˇ si fakulteti. Ker uporabniki – programerji-zaˇ cetniki – odziv sistema prejmejo v zelo kratkem ˇ casu, smo ga poimenovali Programerjevo interaktivno vadbeno okolje (PIVO). 3.1 Formulacija problema Preverjanje nalog iz programiranja je mogoˇ ce avto- matizirati, ˇ ce probleme (naloge) ustrezno formuliramo. Pri programiranju algoritmov pogosto ˇ zelimo doloˇ cene vhodne podatke obdelati, kot rezultat pa vrniti bodisi izluˇ sˇ cen kos informacije bodisi spremenjene vhodne podatke. Primerna formulacija naloge je na primer, da ˇ studentje napiˇ sejo definicijo ustrezne programske funkcije (oz. metode, podprograma) v dogovorjenem programskem jeziku (slika 1). VHOD IZHOD int algoritem(int vhod); Slika 1: Raˇ cunalniˇ ski program: vhod-algoritem-izhod. Taka formulacija naloge se ujema tudi s sploˇ snimi smernicami pisanja dobre kode [12]. Ker so posame- zni sklopi kode zapisani modularno, je program na- mreˇ c veliko bolje berljiv ter primeren za razˇ sirjanje in vzdrˇ zevanje. Od ˇ studentov oz. uporabnikov sis- tema PIVO tako vedno zahtevamo, da ˇ zeleni algoritem zapiˇ sejo v obliki funkcije. ˇ Ce je naloga zastavljena tako, da sta tip in ˇ stevilo podatkov, ki jih program sprejme in vrne, toˇ cno doloˇ cena, lahko definiramo testne nize podatkov, ki jih na oddanem ˇ studentskem programu preizkusimo (slika 2). int(-3), int(0) int(10), int(5) int( 3), int(4) int(-3) int(15) int( 7) int vsota(int x, int y); Slika 2: Preizkuˇ sanje enostavnega raˇ cunalniˇ skega programa na testnih podatkih – primer vsote dveh ˇ stevil. Prav tako uporabnikom ˇ ze v navodilih za izvedbo naloge predstavimo celoten program, ki se bo zagnal ob testiranju. Tak pristop ima vsaj dve prednosti: uporabniki imajo vse informacije o tem, kako razvijati program na svojem sistemu; uporabniki vedo, na kakˇ sen naˇ cin bo njihov program preizkuˇ sen. Vnaprej navedemo vse dovoljene knjiˇ znice, globalne spremenljivke in naˇ cin izpisa rezultatov na standardni izhod. Spodaj je prikazan primer podane kode za nalogo preproste vsote dveh celih ˇ stevil v programskem jeziku C. #include int vsota(int, int); // Prototip funkcije. int main(){ int x, y; // Tukaj se bodo zaporedoma // izvedli testni klici. return 0; } // Od tu naprej bo // v prevajanje kode // vkljuceno vase delo. 3.2 Preizkuˇ sanje programa Testni klici zahtevane funkcije skupaj s priˇ cakovanimi izpisi na standardni izhod so na spletni strani sistema PIVO podani loˇ ceno: x = 3, y = 4; printf("%d", vsota(x, y)); // 7 x = 10, y = 5; printf("%d", vsota(x, y)); // 15 Razlog za loˇ ceno podajanje testnih klicev je pe- dagoˇ ski; izkuˇ snje so pokazale, da ˇ studentje laˇ zje ugo- tovijo priˇ cakovano delovanje funkcije, ˇ ce so testni nizi grafiˇ cno blizu navodil za delovanje priˇ cakovanega dela kode. Ob preizkuˇ sanju kode, ki je oddana na PIVO - PROGRAMERJEVO INTERAKTIVNO V ADBENO OKOLJE 57 streˇ znik, se k zgornjem nizu testov dodajo ˇ se taki, ki ˇ studentu/uporabniku vnaprej niso bili navedeni pri pre- vzemu naloge, pa vendar spadajo v definicijsko obmoˇ cje naloge (npr. vsota mora delovati tudi za negativna ˇ stevila). x = -3, y = 0; printf("%d", vsota(x, y)); // -3 S tem ko vsi testi niso vidni, se ˇ studentje/uporabniki resniˇ cno osredotoˇ cijo na reˇ sevanje problema, in ne samo na zadovoljevanje testnih primerov. 3.3 Oddaja kode na streˇ znik ˇ Student/uporabnik razvito izvorno kodo odda v temu namenjeno tekstovno okno na spletni strani. V nasprotju z nalaganjem tekstovne datoteke ima lepljenje kode v spletni obrazec nekaj prednosti: za ˇ studenta je hitrejˇ se ter odpadejo problemi z izvornim kodiranjem tekstovne datoteke, ki so se izkazali za veliko teˇ zavo v prvih razliˇ cicah sistema. Primer oddane uporabnikove kode, ki vrne pravilne rezultate: int vsota(int stevilo1, int stevilo2){ int r; stevilo1 += stevilo2; r = stevilo1; return r; } Veˇ cina sistema PIVO je skupaj s pripadajoˇ co sple- tno stranjo izdelana v programskem jeziku Python, s pomoˇ cjo knjiˇ znice Django, popularnega sistema za ra- zvoj in vzdrˇ zevanje streˇ zniˇ skega zaledja [16]. V ˇ casu nastanka tega prispevka PIVO podpira preizkuˇ sanje kode v programskih jezikih C in JavaScript (Node.js). 4 VAREN ZAGON TUJE KODE Najveˇ cji izziv pri zagonu tuje kode je zagotavljanje varnosti in s tem neprekinjenega delovanja streˇ znika, ˇ cemur bomo posvetili nekaj veˇ c besed. Pri varnosti v naˇ sem sistemu smo ubrali dokaj rigorozen pristop. Predvidevali smo, da je vsaka koda lahko namensko ˇ skodljiva in mora kot taka imeti ˇ cim bolj omejen dostop do preostale raˇ cunalniˇ ske infrastrukture. V tem poglavju bomo poskuˇ sali opisati naˇ cin varnega zagona kode, napisane v programskem jeziku C. 4.1 Inicializacija knjiˇ znic ˇ Ce razmiˇ sljanje zaˇ cnemo od spodaj navzgor, lahko z vidika varnosti sprva zagotovimo, da ima vsak program na voljo kveˇ cjemu tiste vire (programske ali strojne), ki jih nujno potrebuje za svoje delovanje. To zahtevo lahko izpolnimo, ˇ ce poskrbimo, da se ob prevajanju programa upoˇ stevajo le knjiˇ znice/moduli, ki smo jih vnaprej navedli v testnem programu – zagotoviti je treba, da uporabnik sistema ne more vkljuˇ citi dodatnih knjiˇ znic. To je mogoˇ ce narediti ˇ ze v fazi pred preva- janjem programa z enostavno analizo izvorne kode in odstranitvijo kljuˇ cnih ukazov za inicializacijo knjiˇ znic. 4.2 Prevajanje Sledi prevajanje izvorne kode. Za kodo v program- skem jeziku C uporabimo prevajalnik GCC, ki mu nastavimo opcijo za statiˇ cno prevajanje. To pomeni, da bo koda prevedena v strojni jezik skupaj z zahteva- nimi knjiˇ znicami; zagon programa je tako neodvisen od nameˇ sˇ cenih sistemskih knjiˇ znic. test.c test.bin SISTEM knjiznica.bin GCC Slika 3: Prevajanje in souporaba sistemskih knjiˇ znic. test.c knjiznica.bin + test.bin test_s.bin GCC -static Slika 4: Statiˇ cno prevajanje – knjiˇ znica je vkljuˇ cena v binarni del programa. ˇ Ce se zgodi, da je izvorna koda neprevedljiva, upo- rabniku vrnemo sporoˇ cila prevajalnika. Ker prevajalnik GCC velikokrat sporoˇ ca dele izvorne kode, v katerih bi lahko bila napaka, pred tem iz sporoˇ cil odstranimo morebitne (skrite) testne klice in morebitno vidno di- rektorijsko strukturo streˇ znika. 4.3 Zaklepanje procesa Preden preveden program zaˇ zenemo na streˇ zniku, moramo poskrbeti, da ta ne bo zahteval veˇ c sistemskih virov, kot je potrebno [13]. Ne pozabimo, da je preve- deni progam ˇ se vedno lahko nepopoln ali pa namensko ˇ skodljiv. Tipiˇ cen primer nepopolnega programa je, da ta vsebuje ponavljalne stavke, ki se nikoli ne izvedejo do konca. Tako program zaseda procesorski ˇ cas in drugi procesi na streˇ zniku zastanejo, kar je treba prepreˇ citi. Prav tako je treba omejiti zahteve po prevelikem kosu pomnilnika (angl. memory allocation), spreminjanju di- rektorijske strukture, ustvarjanju preveˇ c novih procesov in podobno. Vse te zahteve lahko zdruˇ zimo v program, ki procesu zaklene (angl. lockdown) nedovoljene sistemske klice in omeji vire. V operacijskem sistemu Linux lahko to storimo z uporabo funkcij iz knjiˇ znice seccomp (Secure Computing Mode) [14]. Seccomp je mehanizem v jedru Linuxa, ki omogoˇ ci prehod procesa v t. i. varno stanje, kjer so v osnovi onemogoˇ ceni vsi sistemski klici razen nekaj nujnih (sporoˇ cilo o stanju in izhodu procesa, branje in pisanje na standardni vhod/izhod). Toda ˇ ce upoˇ stevamo vse blokade, ki jih seccomp lahko izvede 58 ROJEC na procesu, se lahko kaj hitro zgodi, da bo program, ki ga ˇ zelimo preizkusiti, neuporaben. ˇ Ce je npr. ustvarjen za obdelavo tekstovnih datotek, mu je treba omogoˇ citi branje in/ali pisanje datotek. Naslednji set pravil za seccomp omogoˇ ci procesu odpiranje (open) in zapira- nje (close) datotek, striktno pa onemogoˇ ci kloniranje procesov (vfork ali ustvarjanje novih map (mkdir). #include scmp_filter_ctx const ctx = seccomp_init(SCMP_ACT_TRAP); seccomp_rule_add(ctx,SCMP_SYS(open), SCMP_ACT_ALLOW,0); seccomp_rule_add(ctx,SCMP_SYS(close), SCMP_ACT_ALLOW,0); seccomp_rule_add(ctx,SCMP_SYS(vfork), SCMP_ACT_ERRNO(0),0); seccomp_rule_add(ctx,SCMP_SYS(mkdir), SCMP_ACT_ERRNO(0),0); Pri omejevanju porabe sistemskih virov nam pomaga setrlimit(2) sistemski klic v Linuxu. Naslednji primer prikazuje omejevanje uporabe procesorskega ˇ casa RLIMIT_CPU na 2 sekundi (mehki pogoj) oziroma 3 sekunde (strogi pogoj). #include #include setrlimit(RLIMIT_CPU, 2, 3); Na obstojeˇ ci proces se torej nastavi ˇ casovna omejitev – ˇ ce je proces konˇ can v manj kot 2 sekundah, se blokada procesa ne sproˇ zi, ˇ ce preseˇ ze mejo mehkega pogoja, se mu poˇ slje signal SIGXCPU (preseˇ zen procesorski ˇ cas) in, nazadnje, ˇ ce proces po preseˇ zeni meji strogega pogoja ˇ se vedno obstaja, ta prejme signalSIGKILL. Na podoben naˇ cin lahko omejimo tudi najveˇ cjo velikost da- totek, ki jih proces lahko ustvari, in velikost pomnilnika (v bajtih), ki ga lahko rezerviramo na RAM-u. int slim = 10 * 1024 * 1024; int hlim = 12 * 1024 * 1024; setrlimit(RLIMIT_FSIZE, slim, hlim); setrlimit(RLIMIT_DATA, slim, hlim) Ko so vse opisane omejitve za proces nastavljene, lahko zaˇ zenemo novi, zaupanja nevreden proces – kodo uporabnika sistema PIVO. To storimo tako, da v pro- gramu, za katerega smo nastavili omejitve, pokliˇ cemo ukaz execv, ki zaˇ zene binarno datoteko, katere pot podamo v parametrih. Ukaz execv ne ustvari novega procesa, temveˇ c pod istimPID in enakimi pogoji (ome- jitvami sistemskih virov) zaˇ zene drug binarni program (slika 5). Spodnji klic funkcije execv zaˇ zene proces, ki smo ga podali kot prvi argument pri klicu programa lockdown (argv[1]), in kazalec usmeri na preosta- nek podanih parametrov (kazalecargv + 1), ki sluˇ zijo kot vhodni podatek v novi proces. execv(argv[1], argv + 1); lockdown.bin lockdown.bin run seccomp execv() test_s.bin lockdown.bin seccomp test_s.bin seccomp stdout Slika 5: Zaklepanje procesa – programlockdown.c zaklene dovoljenja za proces, ukaz execv() nadomesti program, ki trenutno teˇ ce, z uporabnikovo izvrˇ silno kodo test_s.bin pod isto (starˇ sevsko) procesno identiteto. Ob preverjanju de- lovanja test_s.bin spremljamo vsebino standardnega iz- hoda. 4.4 Zagon programa v vsebniku Zavoljo dodatne varnosti celoten proces zaklepanja in zagona procesa zaˇ zenemo v vsebniku Docker. Docker je orodje za razvoj aplikacij, v katerega vsebnik namestimo vse, kar bi naˇ s program lahko potreboval (izvrˇ silne datoteke, sistemske knjiˇ znice, direktorije itd.) [15]. Gre za ”lahkokategorni” naˇ cin virtualizacije, kjer je program zagnan znotraj vsebnika, med delovanjem pa je od gostiteljevega operacijskega sistema popolnoma izoliran – delita si le jedro operacijskega sistema. Docker ima ˇ ze vgrajene nekatere varnostne meha- nizme, ki onemogoˇ cajo ”pobeg” procesa iz nadzorova- nega okolja. Glavni namen uporabe vsebnika v naˇ sem primeru je simulacija praznega operacijskega sistema – na ta naˇ cin zagnani proces nima na voljo sistemskih direktorijev ali knjiˇ znic. Za zagon uporabnikove kode imamo pripravljen vsebnik, ki vsebuje le izvrˇ silno da- toteko lockdown.bin. Ko uporabnik poˇ slje zahtevek za zagon kode, se statiˇ cno prevedena izvrˇ silna datoteka (izvrˇ silna datoteka ˇ ze vkljuˇ cuje potrebne knjiˇ znice, glej poglavje 4.2) kopira v pripravljeni vsebnik. Tako je mo- rebitno ˇ skodljivi proces dodatno izoliran od sistema, za zagon pa ima na voljo izkljuˇ cno datoteke, ki so na voljo v tem vsebniku (lockdown.bin in test.bin). Po- udariti moramo, da ni treba, da je v vsebniku nameˇ sˇ cen kakrˇ senkoli drug program, niti Linuxova lupina Shell ali Bash, prek katerih bi lahko sistemu, nameˇ sˇ cenemu v Docker, poˇ siljali sistemske klice. Proces zaˇ zenemo z lockdown.bin, ki zaklene do- stop do sistemskih strojnih virov, kot je opisano v prejˇ snjem podpoglavju (4.3). Ko se proces zaˇ zene, lahko spremljamo njegov zapis na standardni izhod. Slika 4.4 prikazuje proces zagona izvrˇ silne datoteke v izoliranem okolju. PIVO - PROGRAMERJEVO INTERAKTIVNO V ADBENO OKOLJE 59 test_s.bin Docker lockdown.bin cp test_s.bin test_s.bin lockdown.bin Docker ./lockdown.bin test.bin stdout Ko se prek protokola HTTP zgodi zahteva po pre- vajanju in zagonu kode, se na streˇ zniku ustvari ko- pija praznega vsebnika. Ustvarjanje kopij vsebnika pov- zroˇ ca znaten ˇ casovni zamik (nekaj 100 ms), vendar pa omogoˇ ca, da se vsak program zaˇ zene v loˇ cenem okolju, v simulaciji popolnoma praznega operacijskega sistema. Alternativa temu pristopu bi bila uporaba enega sa- mega vsebnika Docker, v katerega po metodi cevo- voda (pipe-line) vnaˇ samo izvrˇ silne datoteke in jih pre- izkuˇ samo zaporedoma. Ta pristop na streˇ zniku deluje hitreje, vendar povzroˇ ca druge teˇ zave; pojavi se na primer vpraˇ sanje, kako posameznim procesom zagotoviti enakovredno okolje (prazen sistem), ˇ ce npr. procesi ustvarjajo ali berejo datoteke, se sklicujejo na razliˇ cne sistemske knjiˇ znice in podobno. 4.5 Navidezni raˇ cunalnik Za namen doseganja visoke varnosti in enostavnega upravljanja je sistem skupaj s streˇ znikom nameˇ sˇ cen v virtualni raˇ cunalnik. Tako se enostavno vzposta- vlja varnostne posnetke sistema (snapshots), morebitna ˇ skodljiva koda pa ima okrog vsebnika ˇ se dodatno var- nostno bariero, ki prepreˇ cuje, da bi imela dostop do centralnega raˇ cunalniˇ skega sistema. 4.6 Preizkus delovanja 4.6.1 Robustnost sistema: Varnost zaledja sistema PIVO smo preizkusili z naborom znanih odprtokodnih virusov in ˇ skodljivih programov. Omeniti velja, da je veˇ cina dostopne ˇ skodljive programske opreme sicer pri- lagojena delovanju v operacijskem sistemu Windows, tako da je za namene testiranja (oziroma ˇ skodovanja sis- temu) to opremo treba prilagoditi, da bi lahko ˇ skodovala sistemu Linux. Preizkusili smo oddaljeno ugaˇ sanje raˇ cunalnika, rekurzivne zahteve po velikih kosih po- mnilnika, rekurzivno kopiranje obstojeˇ cih procesov in podobno – v vseh primerih je sistem delovanje ˇ skodljive kode ustavil, preden bi priˇ slo do kakrˇ snegakoli opa- znega vpliva na operacijski sistem virtualnega streˇ znika. Spomnimo naj, da v osnovni razliˇ cici sistema PIVO v vsebniku Docker, ki je dodeljen vsakemu uporabniku, ni nameˇ sˇ cen niti Linuxov Shell, s pomoˇ cjo katerega bi lahko prek sistemskih klicev nenadzorovano (mimo pra- vil sistema Seccomp) upravljali z jedrom operacijskega sistema. 4.6.2 Uporaba v praksi: Do danaˇ snjega dne smo na Fakulteti za Elektrotehniko Univerze v Ljubljani sistem PIVO uporabljali za namen izvajanja obveznih domaˇ cih nalog pri predmetih iz programiranja na univerzitetnem in viˇ sjeˇ solskem strokovnem programu. Veˇ cjih poskusov zlorab sistema do zdaj nismo opazili – vsak ˇ student je v okolje PIVO prijavljen z univerzitetno identiteto, pri vsaki nalogi pa je na voljo le omejeno ˇ stevilo posku- sov. Oboje zmanjˇ suje privlaˇ cnost nalaganja namensko ˇ skodljive kode. Kljub vsemu je tako vzpostavljen sistem pripravljen na zahtevnejˇ so rabo, na primer za neregistri- rane uporabnike ter izpite in tekmovanja. 5 ZAKLJU ˇ CEK Roˇ cno ocenjevanje znanja programiranja je dolgotrajen proces. Med izobraˇ zevalnim procesom ga je mogoˇ ce av- tomatizirati na toˇ cki, ko ˇ studentje usvajajo principe t. i. algoritmiˇ cnega razmiˇ sljanja. Takrat je mogoˇ ce preverja- nje znanja omejiti na vstavljanje podatkov v algoritem in analizo izhodnih podatkov. ˇ Studentje doseˇ zejo najboljˇ se rezultate, ˇ ce imajo v ˇ casu ˇ studija na voljo hitre povratne informacije o svojem trenutnem uspehu. Zato smo pri predmetih iz programiranja na Fakulteti za elektroteh- niko Univerze v Ljubljani razvili sistem za interaktivno uˇ cenje programiranja PIVO (Programerjevo interaktivno vadbeno okolje), ki uporabniku v trenutku poda povratno informacijo o pravilnosti delovanja oddanega algoritma. Uporabnikova koda se izvede na centralnem streˇ zniku, zato je varnosti izvajanja posveˇ cena posebna pozornost. V ˇ clanku smo opisali tehniˇ cno reˇ sitev za hiter in va- ren zagon nepreverjene kode, ki temelji na tehnologiji Seccomp ter vsebniku Docker. Sistem je razvit tako, da se uporabnikova koda v strojni jezik prevede z najveˇ c tistimi viri, ki so nujno potrebni za delovanje programa. Posebni varnostni mehanizmi omejijo delo- vanje procesa na toˇ cno doloˇ cene funkcionalnosti, ki jih doloˇ cena naloga od uporabnika zahteva. Sistem v prvih letnikih ˇ studija na Fakulteti za elektrotehniko v praksi uporabljamo veˇ c let. Rezultati so spodbudni. Veˇ c kot 1000 ˇ studentov je dokazalo, da se je sploˇ sno znanje osnov programiranja tako izboljˇ salo, da bi bilo treba v naslednjih letih za vzdrˇ zevanje Gaussove krivulje uspeha na izpitu prag zahtevanega znanja celo dvigniti. Znova se je pokazalo, da je znanje programiranja odvisno pred- vsem od praktiˇ cnega dela in vloˇ zenega truda vsakega ˇ studenta. Sistem PIVO se je kot dober pripomoˇ cek izkazal ˇ se posebej v ˇ casu izvajanja ˇ studija na daljavo. ˇ Studentom je omogoˇ cil interaktiven ˇ studij na ˇ sirokem spektru pro- gramskih problemov brez omejitev (ˇ casovnih, kadro- vskih, prostorskih), ki bi sicer obstajale v fiziˇ cnem laboratoriju. Z uporabo sistema PIVO je bilo moˇ zno 60 ROJEC nadomestiti vsaj omejen del laboratorijskih vaj, ki bi jih bilo sicer potrebno opraviti na fakulteti. LITERATURA [1] Fakulteta za elektrotehniko Univerze v Ljubljani, Letno poroˇ cilo 2018, Fakulteta za elektrotehniko, 2019. URL: http://www.fe. uni-lj.si/mma/letno-2018/2019030613142714/ [2] G. Jerse, M. Lokar, Learning and Teaching Numerical Methods with a System for Automatic Assessment, INTERNATIONAL JOURNAL FOR TECHNOLOGY IN MATHEMATICS EDU- CATION, 2017. [3] M. Pretnar, Spletna storitev za poucevanje programiranja, VI- VID 2014 C P KRANJ, strani 194-201, Fakulteta za organiza- cijske vede, Kranj, 2014. [4] M. Lokar, M. Pretnar, A low overhead automated service for teaching programming, 15 KOL CALL C COMP E, strani 132-136, 2015. URL: https://doi.org/10.1145/2828959.2828964 [5] W3schools.com, dostopno 9. jan. 2020. URL: https://www. w3schools.com/ [6] fireship.io, dostopno 9. jan. 2020. URL: https://fireship.io [7] js.do, dostopno 9. jan. 2020. URL: https://js.do [8] playcode.io, dostopno na: https://playcode.io, jan. 2020. [9] Directi, codechef.com, dostopno na: https://www.codechef.com/ ide, jan. 2020. [10] codecademy.com, dostopno 10. jan. 2020. URL: https://www. codecademy.com [11] S. Hazel, codepad.org, dostopno 10. jan. 2020. URL: http: //codepad.org [12] I. Fajfar, Start programming using HTML, CSS, and JavaScript, Boca Raton ; London ; New York : CRC Press, Taylor & Francis Group, cop. 2016. [13] Eelis, Geordi: IRC C++ eval bot, Javna domena, dostopno na https://github.com/Eelis/geordi, Jan. 2020. [14] A. Arcangeli, Seccomp in Linux (4.19), dostopno na: https://www.kernel.org/doc/html/latest/userspace-api/seccomp filter.html, jan. 2020. [15] Docker, Inc., Docker, dostopno na https://docker.com, feb. 2020. [16] Django (Version 1.5), Django, dostopno na https:// djangoproject.com, feb. 2020. ˇ Ziga Rojec je leta 2014 magistriral, leta 2018 pa doktoriral na Fakulteti za elektrotehniko Univerze v Ljubljani. Od leta 2014 je zaposlen kot asistent na Fakulteti za elektrotehniko. Raziskovalno se ukvarja z inovativnimi postopki za avtomatsko sintezo topologij elektriˇ cnih vezij z uporabo evolucijskih algorit- mov. S ˇ studenti izvaja vaje s podroˇ cja programiranja in analize elektriˇ cnih vezij. Vzdrˇ zuje gruˇ co streˇ znikov Linux, ob tem pa tudi razvija in nadgrajuje sistem PIVO za interaktivno spletno uˇ cenje programiranja.