Kolmannet tehtävät – Apache, kotisivut ja lisää lokien analysointia

Aikaisemmilla viikoilla tehtäviä on ollut viisi kappaletta, mutta tällä viikolla niitä on peräti kaksitoista. Ideana kuitenkin on, ettei niistä tarvitse tehdä jokaista:

” Tee viisi vapaavalintaista kohtaa. Säädä vaikeustaso oikeaksi: jos olet ihan alussa ja tämä on haastavaa, tee helpoimmat a b c d i. Jos osaat jo perusteet, tee useampia tai vaikeampia kohtia. Tarkoitus on, että tehtävät tehtyäsi osaat enemmän kuin osasit ennen. ”

Tero Karvinen

Itse opin ensimmäiset tämän viikon aiheeseen liittyvät asiat vasta viime tunnilla, joten taidan suosiolla pitäytyä helpommissa tehtävissä. Sen lisäksi eilisen ncmpcpp-sekoilun jäljiltä yksinkertaisuuden kaipuuni on vahva.

Tehtävä a

Ensimmäisenä on vuorossa palvelinohjelma Apachen asennus ja sen toimivuuden testaaminen kotisivujen avulla. Teimme vastaavan harjoituksen viime tunnilla, mihin liittyen tein tällä kertaa myös muistiinpanoja. Yritänkin nyt seurata ihan omia ohjeitani (tai oikeammin siis Tero Karvisen ohjeita) sen sijaan että normaaliin tapaan alkaisin etsiä ohjeita Googlesta.

Ennen varsinaista Apachen asentamista on hyvä säätää ensin palomuurin asetukset kuntoon. Ubuntun oletustyökalu palomuuriasetusten säätämiseen on UFW eli Uncomplicated Firewall. Oletuksella palomuuri ei ole päällä, joten tämä asetus täytyy vaihtaa. Sitä ennen kuitenkin avataan TCP-portti numero 22, joka mahdollistaa mm. SSH-yhteyksien muodostamisen ja turvallisen tiedostojen siirtämisen scp-komennon avulla (Lähde).

$ sudo ufw allow 22/tcp

Tämän jälkeen palomuuri otetaan varsinaisesti käyttöön komennolla

$ sudo ufw enable

ja seuraavaksi avataan HTTP-liikenteen mahdollistava TCP-portti 80.

$ sudo ufw allow 80/tcp

Nyt kun asetukset on säädetty, katsotaan miltä palomuurin status näyttää.

Ainakin minun silmääni kaikki vaikuttaa olevan kuten pitääkin.

Seuraavaksi asennetaankin itse Apache komennolla

$ sudo apt-get install apache2

, jota ennen ajan myös komennon

$ sudo apt-get update

varmistuakseni siitä, että ladattavat paketit ovat mahdollisimman uusia.

Asennuksen valmistuttua avasin selaimen ja kirjoitin osoitekenttään localhost, jossa näkyi Apachen oletussivu, mistä päättelin asennuksen onnistuneen.

Tämän jälkeen kannattaa mahdollisimman pikaisesti ylikirjoittaa normaali index.html-sivu, sillä hakkerit erikseen etsivät kyseisen sivun perusteella palvelimia, jotka saattavat keskeneräisyydestään johtuen olla otollisempia murtautumiskohteita kuin sellaiset palvelimet, joita ollaan konfiguroitu enemmän. Hyvä komento sivun ylikirjoittamiseen on esimerkiksi

$ echo hei|sudo tee /var/www/html/index.html

, josta nähdään myös, missä hakemistossa index.html-sivu sijaitsee. Tee-komento kirjoittaa echossa annetun tekstin annettuun tiedostoon. Tämän jälkeen selaimen näkymä muuttuu seuraavanlaiseksi:

Seuraavaksi lähdetään tekemään käyttäjille omia kotisivuja. Aluksi käyttäjille pitää myöntää oikeudet tehdä omiin kotihakemistoihinsa public_html-kansiot. Tämä tapahtuu komennolla

$ sudo a2enmod userdir

, joka sallii Apache2-moduulin käytön käyttäjien omissa hakemistoissa (Lähde). Tässä vaiheessa terminaaliin tulostuu

To activate the new configuration, you need to run:
systemctl restart apache2

, joten potkaiskaamme ehdotetulla komennolla Apache uudestaan käyntiin, jotta muutokset tulisivat voimaan.

Seuraavaksi navigoin omaan kotihakemistooni ja loin sinne public_html-kansion ja sen sisään index.html-sivun seuraavilla komennoilla:

$ mkdir public_html
$ cd public_html
$ nano index.html

Index-sivulle voi kirjoittaa, mitä nyt haluaakaan, oma http://localhost/~helkera -osoitteesta löytyvä sivuni näyttää pienen säätämisen jälkeen tältä:

Aluksi hieno ASCII-kala tulostui ilman tyhjiä merkkejä, minkä takia se näytti vaan epämääräiseltä littanalta merkkijonolta. Stackoverflow:sta sain selville, että ASCII-kuvat saa säilymään oikeassa muodossa käyttämällä <pre> -tageja kuvan ympärillä. Kyseinen kala on itseasiassa komentoriviohjelma Fish:n logo, josta joku ystävällinen sielu kyseli tekstinä kopioitavaa versiota GitHubissa.

Tehtävä b

Nyt kun kotisivut on saatu toimimaan, niitä olisi tarkoitus käyttää ja samalla tarkastella, mitä Apachen lokiin ilmestyy.

Apachen lokit ovat hakemistossa /var/log/apache2, ja sieltä löytyvät tiedostot access.log, error.log ja other_vhosts_access.log. Avasin ensin access.login ja aloin tarkastella sen viimeisimpiä rivejä, jotka olivat seuraavanlaiset:

127.0.0.1 - - [03/Feb/2019:17:36:22 +0200] "GET / HTTP/1.1" 304 179 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"
127.0.0.1 - - [03/Feb/2019:17:38:49 +0200] "GET / HTTP/1.1" 200 285 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"
127.0.0.1 - - [03/Feb/2019:17:38:49 +0200] "GET /favicon.ico HTTP/1.1" 404 500 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"
127.0.0.1 - - [03/Feb/2019:17:40:16 +0200] "GET /~jokumuu HTTP/1.1" 404 498 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"
127.0.0.1 - - [03/Feb/2019:17:41:17 +0200] "GET /~helkera/ HTTP/1.1" 200 669 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"

Ensimmäisenä kussakin lokimerkinnässä näkyy ip-osoite, josta GET-pyyntö on tullut, eli tässä tapauksessa localhost. Seuraavana toisen viivan paikalla voisi olla clientin identiteettiä kuvaava nimi, jota tässä tapauksessa ei ole kuitenkaan saatavilla. Hakasulkeiden sisällä on aika, jolloin palvelinta on kutsuttu. Seuraavaksi lokissa määritellään, että kyse oli juuri GET-pyynnöstä, joka kohdistui yleiseen index.html:ään käyttäen HTTP 1.1 -protokollaa.

Seuraavaksi tuleva koodi kuvaa serverin statusta pyyntöön liittyen. 304 tarkoittaa, että sivuun ei ole tehty muutoksia sen jälkeen kun se on tallennettu client-koneen välimuistiin, joten sivu haetaan välimuistista. 200 puolestaan tulostuu, jos selain ei hae sivua välimuistista, vaan lataa sen onnistuneesti palvelimelta. 404 taas ilmaisee, että haettua kohdetta ei löytynyt. Esimerkiksi sivun faviconin hakeminen tuotti 404-koodin, koska en ole asettanut sivulle faviconia. Myös yrittäessäni mennä osoitteeseen, jota ei ole olemassa, saatu koodi on 404. (Lähde)

Seuraava luku kuvaa pyydetyn objektin kokoa. Tämän jälkeen tuleva lainausmerkkien sisällä oleva viiva tarkoittaa, että käyttäjä tuli sivuille suoraan ilman että häntä olisi ohjattu jostain muualta, eli hän saattoi esimerkiksi kirjoittaa sivuston osoitteen suoraan osoitekenttään, kuten itsekin tein (Lähde). Tämän jälkeen tulee ns. User-agent -elementti, joka kertoo, mitä tietoja pyynnön tehnyt selain on lähettänyt itsestään, eli tässä tapauksessa tiedon siitä, että käytetty selain oli Mozilla Firefox ja käyttöjärjestelmä Ubuntu (Lähde).

Tehtävä c

C-tehtävän tarkoituksena on aiheuttaa virhe palvelimella ajettavaan koodiin ja etsiä lokeista kyseiseen tapahtumaan liittyvät rivit. Edellisessä tehtävässä en vielä tehnyt omalle nettisivulleni minkäänlaista koodia, joten se on edessä nyt. Tunnilla katselimme yhdessä php-koodia, joten pitäydyn tällä kertaa siinä, vaikka Python kiinnostaakin minua.

Hain oikeaa php-pakettia asennettavaksi komennolla:

$ apt-cache search php|grep php|grep -i apache

Aluksi komento siis hakee kaikkia ladattavia paketteja, joiden nimessä tai kuvauksessa mainitaan php. Niitä on kuitenkin valtava määrä, joten lisäämällä grep-komentoja mukaan saadaan rajattua hakutuloksia järkevästi. Edellä ajettu komento tuottaakin neljä hakutulosta, joista latasin Apache2:selle tarkoitetun php-moduulin oletusversion komennolla

$ sudo apt-get install libapache2-mod-php

Seuraavaksi potkaisin vielä Apachen uudestaan käyntiin, jotta tehdyt muutokset tulisivat varmasti voimaan:

$ sudo systemctl restart apache2

Tässä vaiheessa yritin jo ajaa pientä php-koodinpätkää, mutta sivulle tulostuikin pelkkää tyhjyyttä. Sen lisäksi sivun lähdekoodissa näkyi annettu php-koodi suoritetun koodin lopputuloksen sijasta, mikä ei todellakaan ole toivottava tilanne. Melko pitkään asiaa ihmeteltyäni muistin, että oletusasetukset eivät salli php-skriptien ajamista käyttäjien kotihakemistoista käsin, ja niinpä asetuksia pitää muuttaa. Antamalla komento

$ sudoedit /etc/apache2/mods-available/php7.2.conf

päästään muokkaamaan Apachen conf-tiedostoa siten, että muutetaan viisi alinta kuvassa näkyvää riviä kommenteiksi:

Tallentamisen jälkeen polkaisin Apachen käyntiin vielä uudestaan ylempänä mainitsemallani komennolla ja sen jälkeen oli aika luoda php:ta käyttävä sivu kotihakemiston public_html-kansioon. Itse en halunnut lisätä hienolle index.html-kalasivulleni ylimääräistä hässäkkää, joten loin kokonaan uuden tiedoston php:n testaamista varten komennolla

$ gedit hello.php

Olin aikaisemmin ladannut gedit-nimisen tekstieditorin tällaisia tilanteita varten, koska sen avulla on vähän kätevämpää koodailla. Käyttämäni koodinpätkä vaikutti toimivan sivulla, kuten seuraavasta kuvasta näkyy:

Seuraavaksi olikin vuorossa php-koodiin virheen tekeminen. Oletin, että tämä onnistuisi suhteellisen helposti esimerkiksi hankkiutumalla eroon lainausmerkeistä tai suluista. Poistinkin Hello World -tekstin edestä lainausmerkin ja myös laskutoimituksen jälkeisen sulun. Olin avannut jo valmiiksi tekstieditoriin /var/log/apache2-hakemistossa sijaitsevan error.log-tiedoston, ja heti kun tallensin virheellisen koodin ja latasin nettisivun uudestaan, lokiin ilmestyi uusi rivi:

[Sun Feb 03 19:46:08.319395 2019] [php7:emerg] [pid 14330] [client 127.0.0.1:59334] PHP Parse error:  syntax error, unexpected 'World' (T_STRING), expecting ',' or ';' in /home/helkera/public_html/hello.php on line 7

Ensin siis ilmoitetaan, koska kyseinen virhe tapahtui. En ole varma, mitä php7:emerg kokonaisuudessaan tarkoittaa, mutta pid on asianosaisen prosessin id-numero. Seuraavana listalla on ainakin client-koneen IP-osoite ja ilmeisesti myös portti. Sen jälkeen alkaa tarkempi selitys itse virheestä, joka tässä yhteydessä liittyy siihen, ettei koodia saatu parsittua syntaksivirheen vuoksi. Ilmoitus valittaa sanasta World, koska koodi ilmeisesti tyssää juuri siihen kohtaan lainausmerkkien puuttumisen takia. Koska koodin parsija ei tiedä, että kyseessä on tekstinpätkä, se olettaa rivin loppuvan Hello-sanan jälkeen, ja kaipailee siten esimerkiksi puolipistettä tämän merkiksi. Loppuun tulostuu myös, missä hakemistossa virheellinen tiedosto sijaitsee, ja vielä tarkemmin, millä rivillä koodissa virhe tapahtuu.

Tehtävä d

Seuraavaksi häiriötä piti aiheuttaa Apachen config-tiedostoihin. Valitsin kohteekseni /etc/apache2 -kansiossa olevan apache2.config:in. Selailin sitä pitkän aikaa yrittäen miettiä, minkä muuttamisesta tulisi varmasti virheitä. En kuitenkaan ollut ihan varma, joten päädyin siirtämään koko tiedoston pois omasta kansiostaan omaan kotihakemistooni. Sen jälkeen päivitin selaimessani auki olevan sivun ja tutkin, tuliko Apachen error.logiin näkyviin mitään. Yllätyksekseni ei tullut, ja kaikki toimi ihan hyvin ilman config-tiedostoakin. Ajattelin, että ehkä Apache pärjäilee omilla oletusasetuksillaan, vaikkei sillä olisikaan pääsyä config-tiedostoon. Siirsin siis tiedoston takaisin alkuperäiselle paikalleen ja avasin sen uudestaan

$ sudoedit apache2.conf

-komennolla. Tällä kertaa vaihdoin Timeout-kohdassa olevan numeron tekstiksi. Error.logiin ei tämänkään jälkeen ilmestynyt mitään, eikä syslogistakaan löytynyt mitään järkevää. Päätin siis kokeilla tehtävänannossa mainittua apache2ctl configtest -komentoa. Vihdoinkin sain terminaaliin virheilmoituksen:

AH00526: Syntax error on line 92 of /etc/apache2/apache2.conf:
Timeout takes one argument, Timeout duration (sec)
Action 'configtest' failed.
The Apache error log may have more information.

Nopean googletuksen perusteella vaikuttaa siltä, että AH00526 on Apachen syntaksivirheisiin liittyvä koodi, kuten tekstistäkin voi päätellä. Virheessä on myös kerrottu, missä tiedostossa ja millä rivillä kyseinen virhe sijaitsee, ja mukana on myös pienet ohjeet oikean arvon valitsemiseksi. Vaikka virheessä mainitaan, että Apachen virhelokissa saattaa olla lisätietoa, näin ei ainakaan tässä tilanteessa ollut. Viime töikseni editoin vielä config-tiedoston takaisin alkuperäiseen muotoonsa. Ajoin myös uudestaan apache2ctl configtest -komennon, ja vaikka oletin, että virheitä ei pitäisi enää tulla, sain kuitenkin seuraavanlaisen ilmoituksen:

AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message
Syntax OK

Tämä ei kuitenkaan ole erityisen ihmeellistä, sillä Apache ei varmaankaan voi määritellä domain-nimeä automaattisesti, enkä minä ole sellaista asettanut. Config-tiedoston syntaksi on sentään nyt kunnossa.

Tehtävä i

Tässä ns. helppojen tehtävien viimeisessä tehtävässä oli tarkoituksena aiheuttaa lokiin mahdollisimman monta erilaista HTTP-statusta. Tehtävässä b sain jo aikaiseksi statukset 200, 304 ja 404, joten nyt lähdin metsästämään muita statuksia. Analysoin aikaisemmin jo aika tarkasti lokitulosteiden yleisen rakenteen, joten kommentoin tässä tehtävässä lähinnä saatuja statuskoodeja.

Aluksi avasin avukseni Wikipedia-sivun HTTP-statuksista ja lukiessani listaa kiinnitin huomiota koodiin 414, joka kertoo siitä, että annettu URI on liian pitkä. Yritin tavoitella tätä virhettä kirjoittamalla selaimen osoitekenttään niin pitkän osoitteen kuin mahdollista, mutta kuten ennakkoon arvelinkin, se ei riittänyt, sillä sain sen sijaan koodin 404:

127.0.0.1 - - [03/Feb/2019:21:08:41 +0200] "GET /~helkera/kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk HTTP/1.1" 404 718 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"

Seuraavaksi ajattelin, että koska en ole määritellyt sivulle mitään, mihin voisi käyttää POST-metodia, sen yrittäminen saattaisi tuottaa jonkinlaisen virheen. Tämäkään ei kuitenkaan toteutunut, ja käytettyäni löytämääni komentoa

wget --post-data "username=Helkera" http://localhost/~helkera/

sain silti statukseksi 200 sekä seuraavan ilmoituksen terminaaliin:

--2019-02-03 22:55:59--  http://localhost/~helkera/
Resolving localhost (localhost)… 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80… connected.
HTTP request sent, awaiting response… 200 OK
Length: 634 [text/html]
index.html: Permission denied

Cannot write to ‘index.html’ (Permission denied).

Lokissakin yritykseni näkyi koodilla 200:

127.0.0.1 - - [03/Feb/2019:22:55:59 +0200] "POST /~helkera/ HTTP/1.1" 200 942 "-" "Wget/1.19.4 (linux-gnu)"

Asioiden liian hyvästä toimivuudesta ärsyyntyneenä päätin yrittää jotain vähän erilaista. Tein kokonaan uuden käyttäjän komennolla

 $ useradd holkura

, jonka jälkeen asetin käyttäjälle salasanan komennolla

$ passwd holkura

Salasanan syötettyäni kirjauduin uusille tunnuksille antamalla komennon

$ su holkura

ja syöttämällä juuri määrittelemäni salasanan. Sen jälkeen yritin seuraavanlaista GET-pyyntöä:

$ wget http://localhost/~helkera

ja vaikka odotin saavani 403-statuksen, joka kertoo että käyttäjällä ei ole lupaa pyydettyyn resurssiin, sainkin seuraavanlaisen vastauksen:

--2019-02-03 23:01:46--  http://localhost/~helkera
Resolving localhost (localhost)… 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80… connected.
HTTP request sent, awaiting response… 301 Moved Permanently
Location: http://localhost/~helkera/ [following]
--2019-02-03 23:01:46-- http://localhost/~helkera/
Reusing existing connection to localhost:80.
HTTP request sent, awaiting response… 200 OK
Length: 634 [text/html]
~helkera: Permission denied

Cannot write to ‘~helkera’ (Permission denied).

En ole varma, miksi sain statuksen, joka kielii siitä, että pyydetty resurssi olisi pysyvästi siirretty muualle kielletyn statuskoodin sijasta. Kuten edelliselläkin yrityksellä, lokitulosteet olivat myös nyt hieman erilaiset:

127.0.0.1 - - [03/Feb/2019:23:01:46 +0200] "GET /~helkera HTTP/1.1" 301 572 "-" "Wget/1.19.4 (linux-gnu)"
127.0.0.1 - - [03/Feb/2019:23:01:46 +0200] "GET /~helkera/ HTTP/1.1" 200 941 "-" "Wget/1.19.4 (linux-gnu)"

Koska tein pari aikaisempaa GET-pyyntöä suoraan terminaalista, tällä kertaa esimerkiksi selaintiedot puuttuvat. Terminaali selkeästi lähettää käyttöjärjestelmästään vähemmän spesifiä tietoa selaimeen verrattuna.

Seuraavaksi päätin tehdä holkura-käyttäjänä seuraavan GET-pyynnön:

$ wget http://localhost/~holkura

Normaalin, toimivan hakemistoni sijasta kutsuin siis sivua, jota ei ole olemassa, koska uudella käyttäjällä ei ole kotihakemistossaan public_html-kansiota, eikä myöskään tarvittavaa index-sivua. Omaan oikeaan kotihakemistooni tällä toisella käyttäjällä ei taas ole oikeuksia. Tällä kertaa lykästi ja sain tavoittelemani statuksen:

--2019-02-03 23:31:59--  http://localhost/~holkura
Resolving localhost (localhost)… 127.0.0.1
Connecting to localhost (localhost)|127.0.0.1|:80… connected.
HTTP request sent, awaiting response… 403 Forbidden
2019-02-03 23:31:59 ERROR 403: Forbidden.

Myös lokiin tulostui oheinen rivi:

127.0.0.1 - - [03/Feb/2019:23:31:59 +0200] "GET /~holkura HTTP/1.1" 403 509 "-" "Wget/1.19.4 (linux-gnu)"

Seuraavaksi päätin yrittää jotain jännittävämpää. Apachen config-tiedostoa tutkiessani näin siellä timeout-attribuutin, ja ajattelin, että jos muuttaisin sen arvon riittävän pieneksi, saattaisin saada jonkinlaisen aikakatkaisuun liittyvän virheen. Vaihdoin sudoeditillä oletusarvon 100:sta 0.0000001:teen, tallensin tiedoston ja potkaisin Apachen uudestaan käytiin. Kaikki toimi kuitenkin aivan entiseen malliin. Tarkistin vielä apache2ctl configtest -komennolla, oliko syöttämässäni asetuksessa jotain vikaa, mistä johtuen asetus ei olisi tullut voimaan, mutta mitään sellaista ei ainakaan tulosteesta ilmennyt. Myös yrittämäni pyynnöt saivat access.logissa statuksekseen 200. Ilmeisesti siis pyynnöt joko toimivat salamannopeasti (mikä tuntuu todennäköiseltä) tai jossain muualla on häikkää. Voi myös olla, että ymmärrän väärin, mihin aikakatkaisua tässä yhteydessä ylipäätäänsä käytetään.

Viimeisenä ajattelin yrittää tehtävänannossa mainittua statusta 500, eli internal server erroria. Tämä on geneerinen virheilmoitus, jota käytetään monesti silloin, kun spesifimpää virheilmoitusta ei voida antaa. Ottaessani selvää kuinka tällaisen virheilmoituksen saisi aikaiseksi kiinnitin huomiota siihen, että monet ihmiset kertoivat saaneensa 500-statuksen virheellisen php-koodin takia. Löysinkin tällaisen keskustelun, jonka innoittamana lisäsin omaan hello.php-tiedostooni olemattoman funktion kutsun seuraavalla tavalla:

Kuten kuvasta näkyy, funktion jälkeinen tekstielementti ei enää tulostu ja access.logiin on ilmaantunut uusi rivi:

127.0.0.1 - - [04/Feb/2019:00:28:54 +0200] "GET /~helkera/hello.php HTTP/1.1" 500 288 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0"

En olisi ikinä uskonut olevani näin mielissäni saadessani internal server errorin, haha!

Siinä olivat tämän viikon tehtävät. Näin jälkikäteen ajateltuna olisin kyllä ihan hyvin voinut sittenkin tehdä LAMP-stackin pystyttämistehtävän viimeisen tehtävän sijasta. Siihen olisi saattanut mennä vähemmän aikaakin :D Mutta tehty mikä tehty, enköhän minä sen LAMPin tule pystyttäneeksi joka tapauksessa ennemmin tai myöhemmin. Ja olihan tämä tarkoituksellinen virheiden tekeminen omalla tavallaan aika hauskaa.

Tietoa kirjoittajasta

Noora Huttunen

Opiskelen ohjelmistotuotantoa Haaga-Helian tietojenkäsittelylinjalla.