tavis nörttimaailmassa

EksisONE - artikkeleita ja ohjeita nörttimaailmasta

Virtuaalipalvelin 5: Fail2ban

Fail2ban on kohtuullisen yleisesti käytetty suoja koputtelijoita, murtoyrityksiä ja brute force attack yrityksiä vastaan. Kun yrityksiä (lähes) mitä tahansa palvelua vastaan on tullut liikaa, niin Fail2ban blokkaa IP-osoitteen määrätyksi ajaksi. Ei se mikään täydellinen suoja ole nykypäivänä, kun IP osoitteet ovat eräällä tavalla kierrätettävää bulkkitavaraa, mutta se vie terän useimmilta kokeilijoilta.

Fail2ban laitetaan seuraamaan mitä tahansa logia, joka seuraa kirjautumisyrityksiä ja tallentaa koputtelijan IP-osoitteen. Tällainen logi on tyypillisesti palvelun access.log tai error.log ja niitä tekee

  • webpalvelimet, kuten Apache2 tai Nginx
  • tietokantapalvelin, kuten MySQL
  • välimuistipalvelin, kuten Varnish

Fail2ban seuraa nimenomaan epäonnistumisia logeista ja sulkee niiden perusteella IP-osoitteen. Itsestäänselvyys, joka kuitenkin osalta aika ajoin unohtuu. Jos kirjautuminen onnistuu, niin eihän mikään palvelu maailmassa ei voi tietää onko kyseessä roisto vai aito käyttäjä. Samaten Fail2ban ei pysty suojaamaan vaikka rikkinäiseltä WordPressin lisäosalta, joka päästää hyökkääjän sisään.

Fail2bannin idea

EksisONE sivustolle ei pysty luomaan käyttäjätiliä. Ei sellaista mihinkään tarvita. Joten kun on kokeillut kolme kertaa kirjautumista, niin IP estetään – kolme siksi, että itse aika ajoin kämmään joko tunnuksen, salasanan tai molemmat, enkä halua sulkea itseäni ulos (vaikka eston purku onnistuukin shellistä ja oman IP:n pystyy asetuksissa olemaan estämättä).

Minulla oli aikoinaan xmlrpc.php estettynä ensin Apachessa, sitten Varnishin avulla ja viimeiseksi Nginxin virtual hostissa, koska en sitä mihinkään tarvinnut ja se on turvallisuusriski – aika harva muuten tarvitsee, joten se kannattaisi aina estää – ja siksi  Fail2ban seurasi logeista sen käyttöyrityksiä. Se antoi bannin jo yhdestä yrityksestä; pelkästään siksi, että aiheuttavat turhaa kuormaa, tarkoitus on aiheuttaa haittaa ja samat yrittelevät kokeilevat aina myös kirjautumista sekä muita aukkoja.

Nyttemmin käytän XMLRPC:tä iPadissä yhden tekstieditorin kanssa, joten muutin hieman Nginxissä virtual hostin asetuksia:

location ~* xmlrpc.php {
   access_log /var/log/nginx/blocked.log blocked;
   allow oma.ip.osoite;
   deny all;
}

Samalla vaihdoin Fail2bannin seuraamaan blocked.log tiedostoa ja bannaamaan IP:t sieltä.

Kun joku itseluottamusta uhkuva script kiddie kokeilee toimiiko joku käyttäjätunnus/salasanapari, tai pääsisikö xmlrpc.php käytöllä aikoja sitten suljettuun haavoittuvuuteen, niin hän epäonnistuu ja siitä jää merkki logeihin. Kuten vaikka epäonnistunut kirjautuminen tai xmlrpc.php tapauksessa ylipäätään yrittäminen. Mitään ei kuitenkaan sinällään tapahtunut, kunhan koputtelee. Tilanne on hieman sama kuin että joku löytäisi Ruotsin tai Tallinnan lautalta avaimen ja alkaisi kokeilla sen toimivuutta randomisti eri maissa ja eri kaupungeissa eri oviin. Mitään vahinkoa ei tapahdu, koska avain ei sovi lukkoon ja ovi ei aukene. Tai toisena vertauksena – soitetaan ovikelloa ja kun kukaan ei vastaa, niin mennään seuraavaan osoitteen pirauttelemaan. Vaaraa ei siis sinänsä ole, mutta se on pirun ärsyttävää ja jos löydetty avain kuitenkin sattumalta sopisi, niin sitä varten on asennettava valvontakamera sekä lukittu portti – jotka maksavat, ja kaikki vain siksi, että joku ehkä joskus jossain. Tai ovikellovertauksessa – se, että oven takana ylipäätään ollaan, häiritsee.

Siksi käytetään Fail2bania tai vastaavia ratkaisuja, jotka sulkevat mahdollisuuden joksikin aikaa ja toivotaan, että kielto menisi perille ja kolkuttelija lähtisi kiusaamaan jotakuta muuta. Paitsi että uusia yrittäjiä on koko ajan jonossa…

Fail2ban laskee logeista yritykset ja kun niitä on sallittua enemmän määrätyn aikajakson sisään, niin IP-osoite suljetaan määrätyksi ajaksi. Sinänsä simppeliä, kunhan ymmärtää, että kyseessä ei ole ennakolta suojaava toimi, vaan yritykset keskeyttävä seuraus.

Aitoihin bottiarmeijoiden hyökkäyksiin Fail2ban on liian kevyt, mutta jos moinen massiivinen palvelunestohyökkäys tapahtuisi, niin suurin osa meistä nyppäisi töpselin seinästä ja toivoisi, että myrsky laantuuu jossain vaiheessa. Ja ne jotka tietävät mitä tekevät, ja heillä on siihen myös rahaa ja varaa, eivät rakenna systeemiään pelkän Fail2bannin päälle – eivätkä ole täällä lukemassa kuin ajanvietteekseen.

Kaikki hyökkäykset eivät kuitenkaan ole sellaista kokoluokkaa kuin mitä pankkeja tai valtion laitoksia vastaan suunnataan. Pienempiä yrityksiä vastaan Fail2ban, ja mielellään kuorrutettuna Varnisihilla, on hyödyllinen.

Fail2ban on myös useilla webhotelleilla käytössä, joten kyseessä ei ole vain virtuaalipalvelimien herkku. Ero tulee siinä, että jos webhotelli ei sitä tarjoa, niin sitten ollaan ilman tai kysytään, josko moisen saisi asiakkaidenkin käyttöön. Virtuaaliservereillä asia on yksinkertaisempi: Fail2ban asennetaan ja otetaan käyttöön.

Asennus

Kuten aina, niin itse en käytä sudo-komentoa, jota muista se, jos et tee töitä root-tunnuksella. Ja aivan kuten aina, niin asennus alkaa päivittämisellä:

apt update
apt dist-upgrade -y

Asennetaan yksi palikka lisää, joka auttaa serveriä muistamaan palomuurin asetukset bootissa:

apt install iptables-persistent

Kun sinulta kysytään asetetaanko jo ufw:ssä olevat IP4 ja IP6 tiedot automaattisesti valmiiksi, niin vastaa molempiin yes. Jos et salli, niin joudut asettamaan ne käsin myöhemmin.

Netin neuvot väittävät, että Fail2ban juurikin tarvitsee iptables-persistent apua. Aidosti Fail2ban nimenomaan ei tarvitse sitä, koska se asettaa omin voimin sääntönsä serverin käynnistyessä. Muutoin iptables-persistent auttaa elämää palomuurin kanssa ja se on syytä asentaa, jos teet säätöjä suoraan iptablesiin, kuten vaikka otat käyttöön geo-eston. Edes UFW ei tarvitse iptables-persistent tuomia komentoja.

Fail2ban asentuu myöskin tutulla tavalla:

apt install fail2ban

Fail2ban löytyy hakemistosta /etc/fail2ban ja samassa paikassa on sen konfigurointitiedostot.

  • fail2ban.conf on itse ohjelmalle, ja useimmille oletukset lienevät turvallisin vaihtoehto alkuun
  • jail.conf pitää sisällään varsinaiset asetukset; mitä vahditaan ja tätäkään ei sinällään muokata
  • paths-*.conf kertoo polut, pääosin logitiedostoihin, joita fail2ban seuraa – silti ne asetetaan myös erikseen

Asetustiedostojen suorittamisessa on järjestys. Ensin tehdään .conf ja sen jälkeen itse tekemäsi .local. Ylipäätään  muokkaukset kannattaa aina tehdä .local-tiedostoon, niin niitä ei jyrätä, jos jokin ohjelmapäivitys muuttaa perusteita. Jos sinun täytyy tehdä useampi asennustiedosto eri palveluille, niin silloin ne kannattaa laittaa jail.d-hakemistoon. Ensi alkuun sekavan oloista, mutta se selviää matkan varrella.

Asetukset

Kopioidaan alkuperäinen jail.conf muokattavaksi tiedostoksi jail.local ja avataan se:

cd /etc/fail2ban
cp jail.conf jail.local
nano jail.local

Termistöstä sen verran, että jail, vankila tai putka, tarkoittaa asetusta, joka seuraa jotain tapahtumaa ja sitten ehtojen täytyttyä estää IP-osoitteen.

Muutamaa kohtaa kannattaa säätää. Selaa jail.local tiedostoa hieman alaspäin, sillä aivan alussa on vain ohjeet. Kommentoimalla risuaidalla # otat pois päältä, ja poistamalla sen laitat asetuksen ylipäätään käyttöön.

  • [INCLUDES] kohtaan lisätään ne tarpeen mukaan muut jail-tiedostot; alkuun kannattaa jättää oletuksille ja kun sinne on lisättävä jotain, niin siinä vaiheessa tiedät sen suuremmin arvuuttelematta – kääntäen, jos et tiedä mitä sinne lisätään, niin et lisää mitään.
  • [DEFAULT] alla on ne säädöt, joita käytetään, jos niitä ei ole laitettu jail-kohtaan erikseen.
    • ignorself = true – ota käyttöön, jos localhostia, hostia ja omaa IP-osoitettasi ei seurata
    • ignoreip = 127.0.0.1/8 ::1 – aseta ne IP-osoitteet. joita ei bannata. Erota ne toisistaan välilyonnillä. Ehkä kannattaa laittaa oma IP-osoite myös?
    • bantime = 10m – kuinka pitkäksi ajaksi bannataan oletuksena; ohjeet puhuvat sekunneista, mutta kelpaa myös ainakin m minuuteille, h tunneille ja d päiville.
    • findtime = 10m – minkä ajan sisällä yrityksiä lasketaan; jos retry tulee täyteen findtimen aikana, niin IP bannataan bantimen ajaksi. Jos samalta IP:ltä tulee uusi yritys findtimen päättymisen jälkeen, niin se lasketaan uudeksi yritykseksi. En tiedä onko se aina kiinteä aikalohko vai rullaava aika, täytyykin selvittää, mutta oletan kiinteää.
    • maxretry = 5 – kuinka monta kertaa saa yrittää kirjautumista tai muuta mitä seurataan
    • enabled = false – jos muuttaa tämän muotoon true, niin kaikki jailit ovat heti aktiivisia. Varmuuden vuoksi kannattanee jättää tämä rauhaan ja lisätä enabled = true erikseen jokaiseen myöhemmin asetettavaan jail-määritykseen. Silloin saa sammutettua asioita yksi kerrallaan, jos tulee tarvetta. Tai tehtyä uusia ilman, että ne yrittävät toimia ennen kuin on itse varma, että asetukset ovat kohdallaan.
  • # Actions alla ovat ne toimet, joita tehdään kun lähdetään antamaan bannia. Ne on koostettu aliasten taakse, ettei tarvitsisi kirjoittaa koko rotlaa. Niitä voi lisätä halunsa ja osaamisensa mukaan, mutta minulle on valmiit riittäneet. Siellä on kuitenkin pari muokkausta odottavaa kohtaa. Jos haluat sähköposti-ilmoituksen banneista, niin aina ne kannattaa asettaa.
    • destemail = root@localhost – vastaanottajan osoite, mutta sopivaksi
    • sender = root@<fq-hostname> – lähettäjänä näkyvä osoite
    • mta = sendmail – mikä lähettää sähköpostit (Mail Transfer Agent). Tämä on hämäävä, sillä ainoat sallitut ovat sendmail ja mail, eikä mail ohjelmaa kannata käyttää. Koska itse käytän postfixiä, niin asetin sen ja fail2ban ei tykännyt. Sen voi kiertää kopioimalla ensin kaikki sendmail-alkuiset tiedostot hakemistossa /etc/fail2ban/action.d postfix-alkuisiksi ja sitten muokkaamalla niistä sendmailin asiat postfixille sopiviksi. Onneksi tuota ei tarvitse tehdä, vaan postfixistä huolimatta asetus saa olla sendmail ja mailit liikkuvat – kunhan järjestelmästä löytyy sendmail. Voit vilkaista hakemiston (Ubuntussa) /usr/sbin ja jos sendmail on siellä, niin kaikki on hyvin; jos ei löydy niin asenna, apt install sendmail toiminee.
    • protocol = tcp – saanee olla oletuksessa
    • viimeisenä lohkosta löytyy action = %(action_)s – siinä valitaan mitä fail2ban tekee bannatessaan. Suluissa oleva on joku aiemmin määritelty action alias. Sen voi jättää oletukselle, jolloin bannataan, mutta ei lähetetä ilmoitusta tai valita jonkun aiemmin olleista määrityksistä. Itse muutin sen muotoon action = action_mwl. Koska tämä on oletus, niin haluttaessa olla rauhassa sähköpostitulvalta, niin se täytyy sitten laittaa erikseen jailiin – esimerkiksi SSH:n yrittäjiä vahtiva jail bannaa melkoisesti, joten siellä komensin action = %(action_)s etten saisi ilmoituksia. Suurin osa tapahtumista on harvinaisempia (paitsi xmlrpc.php…) ja niistä haluan uteliaisuuttani tiedon.
  • loppu on jail-määrityksiä ja ne ovat niitä, jotka hoitavat homman. Sieltä voi poistaa tarpeettomia ja lisätä puuttuvia, jos siltä tuntuu. Lisäyksissä kannattaa ottaa mallia olevista ja lukea dokumentit. Muista lisätä enabled = true jokaiseen, jonka haluat toimintaan.

Kun olet tyytyväinen, niin käynnistetään Fail2ban:

systemctl start fail2ban

Varmistetaan, että se käynnistyy myös rebootissa:

systemctl enable fail2ban

Kannattaa vilkaista, että käynnistyikö Fail2ban:

systemctl status fail2ban

Jos näet virheitä, niin kannattaa lähteä tarkistamaan ilmoitettuja kohtia. Yleisin ongelma lienee siinä, että Fail2ban ei löydä logeja.

Serverin reboot

Jostain syystä virtuaaliserverin reboot on ollut minulle hankala. Kumpikaan, UFW ja Fail2ban, ei selviä siitä kunnialla. Kun ensimmäisen kerran jälkeen kävijät valittivat, että sivustoa ei löydy, mutta curlilla samassa virtuaaliserverissä sain kuitenkin 200 OK vastauksen, niin opin kaksi toimintatapaa.

Ennen rebootia sammutetaan Fail2ban:

systemctl stop fail2ban

Rebootin jälkeen käynnistetään UFW uudestaan:

systemctl restart ufw

Palomuuri jää johonkin ihmeelliseen välitilaan ja jos ottaa statuksen, niin se valittaa logeistaan.

Nykyään oikaisen lisää:

  • buuttaan serverin normaalisti shutdown -r now
  • kun VPS on taas jalkeillaan komennan järjestyksessä
    • systemctl restart ufw
    • systemctl start netfilter-persistent
    • fail2ban-client restart

Tuo on ainoa löytämäni tapa saada palomuuri järkiinsä buutin jälkeen. Kyllä, ärsyttävää käsityötä.

Fail2ban käytössä

Ensiasennuksen tässä vaiheessa Fail2ban ei vielä tee mitään, jos jätit [DEFAULT] alla enabled = false muuttamatta. Nyt täytyy laittaa jailit päälle.

nano /etc/fail2ban/jail.local

Lisää jokaiseen jailiin enabled = true esimerkiksi näin:

[sshd]
mode = normal
port = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
bantime = 48h
maxretry = 1
action = %(action_)s
enabled = true

Kun olet suunnilleen tyytyväinen, niin tarkasta ensin syntaksi:

fail2ban-client -t

Jos/kun sait OK ilmoituksen, niin käsketään Fail2banin ottavan uudet asiat käyttöön:

systemctl reload fail2ban
  • Saat saman tehtyä manuaalien enemmän tykkäämällä komennolla:
fail2ban-client reload

Jos sähköpostiasetukset ovat kunnossa, niin saat hetken kuluttua ilmoituksen jokaisen jailin käyttöönotosta.

Jailin rakenne

Jailit rakennetaan aina (suunnilleen) samalla tavalla.

  • [wordpress] (jailin nimi laitetaan hakasulkeisiin)
  • enabled = true (true, niin käytössä; rivi kokonaan pois, kommentoitu tai muodossa false, niin ei käytössä)
  • port = http,https (mitä porttia käytetään, voi olla porttinumerokin)
  • filter = wordpress (filtteri määrää mitä etsitään ja on asetettu hakemistoon /etc/fail2ban/filter.dja päättyy .conf)
  • logpath = /var/log/apache2/access*.log (mitä logia seurataan)
  • maxretry = 3 (montako kertaa yrittää)
  • findtime = 30m (minä aikana saa yrittää)
  • bantime = 48h (kuinka kauan saa bannia, kun yrityskerrat tulevat täyteen aikarajan sisällä)

Tuon jailin filter olisi /etc/fail2ban/filter.d/wordpress.conf:

  • [Definition] (vaadittu)
  • failregex = ^<HOST> .* ”POST .*wp-login.php (regex muodossa mitä tietoa haetaan
  • ignoreregex = (jos jokin osuma jätettäisiin pois)

Joten esimerkki seuraisi yrityksiä kirjautua osoitteeseen http(s)://<domain>/xmlrpc.php jokaisessa domainissa ja kolmas epäonnistunut yritys puolen tunnin sisällä aiheuttaisi IP-osoitteen sulkemisen 48 tunniksi. Kirjautumista vaativilla sivustoilla kannattaa varmaan sallia viisi yritystä lyhyemmällä aikavälillä ja lyhentää bantime vaikka varttiin, koska ei muistamattomia käyttäjiä kuitenkaan saisi rangaista bottien yritysten takia.

Kannattaa myös muistaa, että Suomessa(kin) samaa operaattorin julkista IP-osoitetta käyttää aika moni. Jos bannaat IP:n kokonaan, tai pitkäksi aikaa, niin samalla julkisella IP:llä ei pääse kukaan sivustolle.

Filtterin ja regexin tarkastus

Ennen kuin tappelet liikaa asetusten kanssa huomattuasi, että IP-osoitteet eivät olekaan estettyjä, niin tarkista filtterin regexin toimivuus esimerkiksi näin:

fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/ban.conf
  • Korjaa kokeiltava log-tiedosto ja filtteri oikeiksi.

Jos saat vastaukseksi jotain tämän tyyppistä:

Lines: 14 lines, 0 ignored, 0 matched, 14 missed

ja missed on se määrä, joka olisi pitänyt olla matched eli osuneet, niin jokin on pielessä.

Varnish

Koska Varnish on aidosti se, joka hakee Apachelta tietoa (tai mikä backendissä sitten onkaan), niin IP-osoite on oletuksena 127.0.0.1 aivan riippumatta käyttäjästä. Useimmilla taitaa löytyä default.vcl -tiedostosta tämä:

sub vcl_recv {
    ...
    set req.http.X-Forwarded-For = client.ip;
    ...
}

Se on kuitenkin syytä muuttaa muotoon:

vcl 4.1;

import std;
...
sub vcl_recv {
    ...
    if (req.restarts == 0) {
    if (req.http.X-Forwarded-For) {
        set req.http.X-Forwarded-For =
        req.http.X-Forwarded-For + " " + req.http.X-Real-IP;
    } else {
        set req.http.X-Forwarded-For = req.http.X-Real-IP;
    }
    }
    ...
}

Ja X-Real-IP on asetettu Nginxin (tai mikä ennen Varnishia onkaan) virtual confissa:

...
location / {
    ...
    proxy_set_header X-Real-IP  $remote_addr;
    ...
}
...
  • Tuo on ainoa tapa saada aito IP-osoite. Kaikissa ohjeissa käytetty client.ip on aina 127.0.0.1 jos ja kun Varnishin edessä on mikä tahansa proxy, vaikka sitten Nginx tai Apache2 hoitamassa SSL:n. Jos Varnish kuuntelee suoraan http-liikennettä portissa 80, niin client.ip toimii. Joten jos sinulla PURGE pyytää lupaa ACL-listalta ja client.ip sallii localhostin, niin kuka tahansa saa tyhjennettyä cachen.

X-Forwarded-For otettiin aikoinaan käyttöön juurikin siksi, että proxyt eivät saaneet kerrottua kävijän IP-osoitetta webserverille. Nyt kävijän IP-osoite siirretään avaimelle X-Forwarded-For ja kuljetetaan siinä webserverille, joka osaa käyttää sitä (ja laittaa sen logeihin) kuten kuuluukin.

Tässä on kuitenkin pieni ongelma. Kävijän IP-siirtyy nyt tietueen viimeiseksi ja ilman säätöä käytetään oletuksena Varnishin kautta tulevaa – joka on 127.0.0.1. Joten muokataan hieman virtual hostin asetuksia.

  • Avataan virtual hostin porttia 81 (tai mihin Varnish juttelee) kuunteleva conf:
nano /etc/apache2/sites-available/example.tld.conf

Korvaa olemassa olevat ErrorLog ja CustomLog (jos löytyvät):

LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{User-agent}i\"" varnishcombined
CustomLog ${APACHE_LOG_DIR}/access.log varnishcombined env=!dontlog

Nyt Fail2ban saa luettua oikean IP-osoitteen Varnishin takaa.

Jos sinulla on Nginx backendin webserverinä, niin joudut hieman googlettamaan. Hakusanat varnish+nginx+real+ip löytänevät oikean tiedon. Nginx hoitaa nimittäin asian hieman eri tavalla ja eräällä tavalla helpommin. Mutta en neuvo sitä, koska en ymmärrä Nginx:stä yhtään mitään.

Varnish vai Fail2ban?

Koska Varnish on ensimmäisenä, niin aivan kaikkia ehdotettuja Apache2/Nginx:n vahtimisia ei ehkä kannata tehdä Fail2banilla. Tai sitten kannattaa, se vähän riippuu. Jotta Fail2ban pystyisi toimimaan, niin periaatteessa kävijän täytyy läpäistä ensin Varnish ja sitten saada Apachelta vastaus, jotta Fail2ban voisi päättää mitä tehdä.

Mutta tuo pätee periaatteessa. Filtteröin ensimmäisen kerran ensimmäisenä olevassa Nginxissä, ja Fail2ban bannaa sen login perusteella. Sen jälkeen kuvioihin astuu Varnish ja antaa virheen osalle yrittäjistä, ja Fail2ban pääsee taas töihin. Loput sitten jäävät, tai eivät jää, kiinni backendin Apachessa.

Verkkosivujen suurin paine ei tule yrityksistä kirjautua sisään, onko xmlrpc käytössä vai ei, tai kokeiluista onko vaikka wp-config.php tiedoston joku backup näkyvissä maailmalle. Kuorma tulee boteista ja spidereista, jotka kahlaavat koko sivustoa läpi. Tunnetujen bottien kohdalla IP-bannaaminen on sinällään turhaa, koska ne voidaan tunnistetaan user agentilla ja pysäyttää heti alkuunsa. Jos kerää edes 20 yleisimmän huonosti käyttäytyvän botin ja totaalisen turhien SEO-harvestereiden tunnisteet ja estää ne heti alussa Varnishissa, tai vielä mielummin Varnisihin edessä olevalla Nginx:llä, niin kuormitus tipahtaa rajusti aivan riippumatta onko IP-osoite bannattu tunniksi tai vuodeksi – nuo eivät nimittäin lopeta koskaan yrittämistä.

Ja juurikin sinnikkyyden takia niiden käyttämät IP-osoitteet kannattaa bannata aikajaksolle -1 eli ikuisiksi ajoiksi. Harmi vaan, että ne laajentavat ja muuttavat IP-avaruuttaan kohtuullisen tiheästi.

Voit oikoa mutkia ja hyödyntää keräämääni bad bad bot -listaa: https://github.com/eksiscloud/varnish_6.x/blob/master/ext/bad-bot.vcl

Kiusaa tekevät ne harvesterit, joiden user-agentit ovat väärennettyjä, ja niissä ehkä Fail2ban saattaa auttaa. En vain ole vielä keksinyt, että miten. Yksi tapa olisi rankaista 404-osumista, mutta silloin tipahtaa aika äkkiä googlebot linjoilta.

Jos käy niin, että joutuu bottiarmeijan hyökkäyksen kohteeksi, niin silloin tarvitaan ehkä molempia. Varnish taittaa cachen avulla pahimman terän ja Fail2ban blokkaa hetkeksi floodaavat IP-osoitteet.

Itse ohjaan turhan koputtelijat Varnishin syntetisoimalla error-sivulle – rehelliset botit saavat osasta paikoista error 403 ja muut error 666. Sen jälkeen bannaan error 666 saaneet toisesta yrityksestä. Logina käytän fronttina olevaa Nginxin access.log tiedostoa sekä Varnishin omaa.

varnishncsa

Edellä oleva pätee siinä tapauksessa, että IP-tieto haetaan nimenomaan taustalla olevan Apachen (tai Nginxin) logeista, ja sinne päätyy vain sellainen pyyntö, joka on läpäissyt cachen – eli pyydettyä urlia ei löydy välimuistista tai sitä ei Varnishin default.vcl -tiedostossa estetä tai ohjata muualle. Itse annan pahojen bottien listassa tunnetuille tapauksille syntetisoidun error 666 virheen, joka ei koskaan pääse backendiin, joten sitä ei niissä logeissa näy, mutta kylläkin sen serverin logeissa, joka hoitaa mm. SSL-terminoinnin, minulla siis Nginx. Jos asennat Varnishin yllyttämänä Hitchin, niin menetät tuon ja joudut taistelemaan Varnishin astetta hankalamman login kanssa. Ylipäätään kaikki Varnishille tehdyt jailit hakevat tietonsa Varnishin logista, joka on yleensä Ubuntuissa /var/log/varnish/varnishncsa.log.

Ongelma on siinä, että Varnish logittaa oletuksena saapuvan osoitteen, joka on sen edessä olevan SSL:n hoitavan serverin osoite, useimmiten 127.0.0.1, ei kävijän IP-osoite. Joten ilman säätöä yksikään varnish-jail ei koskaan bannaa mitään. Onneksi tuo on helposti asetettavissa ja nyt hyödynnetään X-Forwarded-For headeria.

  • Pysäytä ensin varnishncsa:
systemctl stop varnishncsa
  • Avaa asetukset:
systemctl edit --full varnishncsa
  • Kommentoi risuaidalla pitkä rivi, joka alkaa ExecStart ja tee uusi:
ExecStart=/usr/bin/varnishncsa -a -F '%%{X-Forwarded-For}i %%l %%u %%t "%%r" %%s %%b "%%{Referer}i" "%%{User-agent}i"' -w /var/log/varnish/varnishncsa.log -D -P /run/varnishncsa/varnishncsa.pid
  • Käynnistetään demoni uudestaan:
systemctl daemon-reload
  • Käynnistetään varniscncsa:
systemctl start varnishncsa

Nyt sinulla pitäisi logiin tulla ensimmäiseksi kävijän aito IP. Voit tarkistaa sen:

cat /var/log/varnish/varnishncsa.log
  • Jostain syystä logit eivät aina suostu rakentumaan ja varnishncsa kaatuu timeoutiin. Silloin täytyy tehdä yliajava määritys. Vaikka se on tismalleen sama kuin varsinainen, niin se toimii. En tiedä syytä moiseen. Tehdään uusi tiedosto komennolla:
systemctl edit varnishncsa
  • Tuo tekee tiedoston /etc/systemd/system/varnishncsa.service.d/override.conf. Jos haluat joskus estää sen toiminnan, niin poista komennolla rm -f. Kopioi siihen tämä:
[Service]
ExecStart=
ExecStart=/usr/bin/varnishncsa -a -F '%%{X-Forwarded-For}i %%l %%u %%t "%%r" %%s %%b "%%{Referer}i" "%%{User-agent}i"' -w /var/log/varnish/varnishncsa.log -D -P /run/varnishncsa/varnishncsa.pid
  • Käynnistä systemctl start varnishncsa ja homma toimii taas.

Jos googletat asiaa, niin löydät nopeasti samat ohjeet, mutta yhdellä erolla, jonka takia komento ei toimi ja varnishncsa kaatuu. Jokaisessa ohjeessa on suunnilleen samat vivut, mutta niissä käytetään vain yhtä %-merkkiä. Se toimii komentorivillä, kun halutaan syöte ruudulle, mutta ei logiin kirjoitettaessa. Toinen prosenttimerkki on ns escape, joka kertoo, että seuraava prosenttimerkki on ihan oikeasti prosenttimerkki.

Eli tämä toimii kylläkin komentorivillä, mutta ei logeihin:

varnishncsa -F '%{X-Forwarded-For}i %l %u %t "%r" %s %b "%{Referer}i" "%{User-agent}i"'

Tuota esimerkkiä on jaettu ahkerasti, mutta koska se on väärä, niin taas herää kysymys, että eivätkö he tosiaankaan koskaan itse kokeile neuvojaan.

Tuo ei vielä auta Fail2bania bannaamaan yhtään mitään, sillä logirivin muoto täytyy kerto filterillekin.

Minulla on Varnishissa pätkä, joka ohjaa pahat sekä turhat botit syntetisoidulle error 666 sivulle – se aiemmin mainittu bad bad bot -lista. Silloin ne eivät suuremmin kuormita serveriä ja eteneminen pysähtyy välittömästi. Mutta botit sekä muut haahuolijat näkyvät edelleen logeissa ja saapuvassa liikenteessä, plus koska ne eivät väsy eivätkä luovuta, niin liikennemäärä on ikuista. Tässä on yhden yön livetilanne:

Se, että kannattaako noihin reagoida on enemmältikin kosmeettinen asia. Mutta ne ärsyttävät minua, joten pysäytän ne jo heti alkuun serverin kynnykselle – aidosti: yritän pysäyttää, sillä IP-osoitteiden bannaaminen on hävitty sota, oikeammin vain viivästystaistelua. Niillä korteilla kuitenkin pelataan mitkä on.

Minulla on jail:

[varnish-403] 
enabled = true 
port = http,https 
filter = varnish-403 
logpath = /var/log/varnish/varnishncsa.log 
maxretry = 2 
findtime = 24h 
bantime = 12h 
action = %(action_mwl)s

12 tuntia ei ole riittävä bannausaika, mutta se antaa hieman anteeksi mahdollisille virhepositiivisille ja toistuessaan tuleekin sitten pidempi ban.

Filtteri varnish-403.conf on tällainen:

[Definition]
failregex = ^<HOST>\, .*  - - .* "(GET|POST|HEAD).*HTTP.*" 403 .* .* .*$

ignoreregex =

Minä en todellakaan osaa regexiä, mutta uskoakseni se sopii tähän logista otettuun malliriviin:

35.172.243.71, 127.0.0.1 - - [29/Jan/2020:12:28:56 +0200] "GET http://www.katiska.info/ads.txt HTTP/1.0" 403 586 "-" "Trade Desk ads.txt & sellers.json crawler"

Ainakin IP-osoitteita estetään ja seuratessani asiaa virhepositiivisia ei ollut. Se, että saadaanko tuolla kaikkia pyydystetty, on eri asia. Mutta siinä aletaan lähestyä panos/hyöty-ajattelua, sillä vaikka osalla (ainakin ihmisistä) IP onkin X-Forwarded-For headerin takia malliin 1.2.3.4, 127.0.0.1 127.0.0.1 .. joka ei jää kiinni toiseen kertaan kerrotun localhostin takia, on aika mitätön asia.

Tuon käyttöä kannattaa hieman harkita, koska se bannaa kaikki 403 virheet, ja osa voi tulla aivan ns. laillisistakin syistä. Ehkä kannattaa botteja ja koputtelijoita varten käyttää jotain eksoottisempaa virhettä, vaikka 413 – tai sitten hyödyntää Varnishia ja tekee ihan oman virhekoodin, 666 on minusta hauska.

Moodle

Jos logia ei löydy, niin fail2ban on juuri sen palvelun suojaamisessa hampaaton. Yksi sellainen on Moodle, joka merkitsee kirjautumisyitykset tietokantaan, ei teksimuotoiseen ulkoiseen logiin. Moodle hallitsee itse yrityskertoja, ja jos epäonnistumisia tulee liian monta, niin tili suljetaan ja käyttäjälle lähtee vahvistussähköposti, jonka antamaa linkki on klikattava tilin aukaisemiseksi.

Ongelman tynkää on siinä, että se ei edelleenkään estä yrittämästä, koska ylipäätään suurella todennäköisyydellä käyttäjätunnus on ollut väärin ja vaikka se olisikin oikein ja epäonnistuminen tulee salasanasta, niin aito käyttäjä suljetaan – ei murtoa yrittävä botti.

Joku osaava varmaan saisi tehtyä helpollakin koodinpätkän, joka koostaisi epäonnistuneiden kirjautumisten eventeistä tekstilogin, mutta minä en osaa. Joten mennään helpompaa tietä. Moodle antaa epäonnistuneesta kirjautumisesta error 303 ja ohjaa takaisin loginiin – joten käytetään tuota erroria hyödyksi.

Tämä tulee jailiksi:

[moodle] 
enabled = true 
port = http,https 
filter = moodle 
logpath = /var/log/apache2/access*.log 
maxretry = 4 
findtime = 15m 
bantime = 1h 
action = %(action_)s

Tämä laitetaan filtteriksi:

[Definition]
failregex = ^<HOST> - .* "(GET|POST|HEAD).*HTTP.*" 303 .*$

ignoreregex =

Tuossa on yksi vaje ja se on se. että nyt lasketaan kaikki error 303 samaan – tosin, en ole törmännyt siihen kuin Moodlen kanssa. Joten jos joku hallitsee keksinnön suoraan helvetistä nimeltään regex niin voisi vaikka kommentteihin vinkata miten tuo rajataan yhteen domainiin. Sitä ennen kikkailen itse säädetyn 666 virheen kanssa.

Yleiset komennot

  • systemctl reload/restart/start/stop/enable/status fail2ban
  • fail2ban-client -t tarkistaa syntaksin
  • fail2ban-client -d tarkistaa myös toimivuuden, mutta laajemmin
  • fail2ban-client status kertoo tilan
  • fail2ban-client status <jail> kertoo määrätyn jailin tilan, montako on kaikkiaan bannattu ja mitkä IP:t ovat juuri sillä hetkellä bannattuja; jailin nimi annetaan ilman hakasulkeita
  • fail2ban-client reload käynnistää uudestaan esimerkiksi kun jail.local on päivitetty
  • fail2ban-client set <jail> unbanip <ip-osoite> kun haluaa poistaa bannin käsin
  • fail2ban-client set <jail> banip <ip-osoite> kun haluaa manuaalisesti bannata IP-osoitteen
  • fail2ban-client unban --all poistaa kaikki bannit kaikista jaileistä
  • fail2ban-client unban <ip-osoite> poistaa bannin siltä IP-osoitteelta mistä tahansa jailistä
  • https://www.fail2ban.org/wiki/index.php/Commands

Ja kuten aina, niin komentoja voi yhdistellä ja virittää.

  • Tämä näyttää kaikista jaileistä sillä hetkellä bannatut IP-osoitteet
fail2ban-client status | grep "Jail list:" | sed "s/ //g" | awk '{split($2,a,",");for(i in a) system("fail2ban-client status " a[i])}' | grep "Status\|IP list"
  • Saat myös näkyviin kaikki entiset bannit ja syyt miksi ne on bannattu (logien puitteissa tietysti)
zgrep 'Ban' /var/log/fail2ban.log*

Bannit asetetaan palomuuriin itables ja sen kautta voi asiaa myös selvitellä. Google on siltä osin ystäväsi.

Bannien massapoisto

Vanhoista Failbanin versioista puuttui massapoisto, mutta nykyään se on helppoa.

Tein virheen ja onnistuin bannaamaan aivan kaikki kävijät, mukaanlukien itseni. Yksi tapa olisi flushata iptables, mutta silloin ei kannata buutata serveriä tai käynnistää Fail2bannia uudestaan niin, että jail on käytössä. Bannatut IP-osoitteet ovat nimittäin tietokannassa ja käynnistyessään Fail2ban estää IP:t uudestaan. Bannitiedot olisi siis poistettava Fail2bannista ja antaa sen siivota iptables.

On toinenkin syy olla koskematta iptables-tietoihin. Se on vaikeaa ja tarvittavat komentorimpsut ovat melkoisen kryptisiä. Minulla ei ole minkäänlaista halua alkaa kokeilemaan serverin palomuurissa onko kopypeistaamani komento toimiva tai tekeekö se ylipäätään sitä mitä minä haluan.

Onneksi isot poistot ovat helppoja.

  • fail2ban-client unban --all poistaa kaikki bannit kaikista jaileistä
  • fail2ban-client unban <ip-osoite> poistaa bannin siltä IP-osoitteelta mistä tahansa jailistä

Noilla pääsee jo pitkälle, jos on valmis hukkaamaan kaikki IP-osoitteet – joka ei sinällään ole sen suurempi kysymys. Samat yrittäjät tulevat kuitenkin uudestaan, eikä maailmasi kaatunut silloinkaan kun et vielä bannannut ketään.

Tietokannan tyhjentäminen

Joskus saattaa saada asiat totaalisen solmuun ja vanhat tietot IP-osoitteista kummittelevat, vaikka ei pitäisi. Silloin saattaa olla helpointa jyrätä Fail2bannin tietokanta ja aloittaa siltä osin kuin tyhjältä pöydältä (jos sinulla on rikkinäisiä jailejä aktiivisena, niin mikään ei muutu ennenkuin ne on korjattu).

Fail2ban tekee oman tietokantansa, eikä se ole mukana serverisi MySQL- tai MariaDB -kannoissa. Löydät tietokannan sijannin tällä komennolla:

fail2ban-client get dbfile

Jos et ole tehnyt jotain omia säätöjä ja sinulla on Ubuntu, muista en tiedä, niin tietokanta on /var/lib/fail2ban/fail2ban.sqlite3

Tietokannan poistaminen on yhtä helppoa kuin tiedoston poistaminen, koska se on tiedosto.

systemctl stop fail2ban
rm -f /var/lib/fail2ban/fail2ban.sqlite3
systemctl start fail2ban

Käynnistymisen myötä Fail2ban tekee uuden ja tyhjän tietokannan.

Yhden jailin vapauttaminen

Jos kuitenkin haluaa poistaa vain yhdestä jailistä kaikki bannit, niin asia muuttuu kertaluokkaa enemmän teippiviritykseksi.

Google paljastaa muutamankin skriptin, jolla massapoisto kieltolistalta voidaan tehdä, mutta siihen on olemassa helpompikin tapa: määrätään bannausajaksi yksi sekunti:

fail2ban-client set <jail> bantime 1

Sekunnin kuluttua IP-osoitteet vapautetaan. Aidosti  siihen menee hieman enemmän aikaa, koska Fail2ban poistaa niitä yksitellen omalla komennollaan.

Kun kaikki IP-osoitteet on poistettu, niin findtimeen kannattaa hieman kiinnittää huomiota. Jos se turhan pitkä, niin Fail2ban lukee logeja taaksepäin ja bannaa uudestaan juuri vapauttamasti osoitteet. Sitä ehkä kannattaa säätää jailissä. Ainakin jos findtime on pitkähkö. Joten toinen vaihtoehto on muuttaa bantime suoraan jailiin, fiksata heti findtime ja käynnistää Fail2ban uudestaan – ja palauttaa sopivammat arvot jossain vaiheessa, yleensä alkuperäisen findtimen kuluttua, takaisin entiselleen.

Kun jail on tyhjentynyt, niin tee uusi bantime samalla komennolla.

Määrätyn IP-osoitteen vapauttaminen

Jos ei ole merkitystä mistä jailistä jokin IP-osoite vapautetaan, niin komento fail2ban-client unban <ip-osoite> on helpoin. Silloin IP-osoite vapautetaan kaikista jaileistä. Useimmiten tuo on helpoimman vaivan tie.

Joskus on kuitenkin poikkeuksellisia tarpeita, kuten että IP-osoite halutaan pois jostain ikuisuudeksi bannaavasta eli bantime = -1 mutta haluttaisiin pitää silti jossan lyhyemmän ajan bannissa. Tai sitten vaan haluat tietää missä bannissa IP-osoite on. Silloin joudutaan menemään hieman mutkan kautta, koska Fail2ban ei salli IP-osoitteella etsimistä.

Komento näyttää kaikki bannatut IP-osoitteet iptables tiedoista:

iptables -L -n > ban.txt

Tuo tekee tiedoston ban.txt, josta sitten etsit joko käsipelillä tai vaikka search-toiminnolla haluamassasi editorissa missä etsimäsi IP-osoite piileskelee, Tietysti voi antaa komennon ilman > ban.txt putkea, mutta silloin scrollaat ruutua.

Etsit tietoa minkä säännön alla IP-osoite piilee. Etsin omastani (harjoituksen takia) yhden liian tiukan säädön takia bannattua IP-osoitetta 66.249.76.131 – se on Googlen botti.

Chain f2b-nginx-444 (1 references) 
target prot opt source      destination 
REJECT all -- 66.249.76.131 0.0.0.0/0 reject-with icmp-port-unreachable

Sääntö ei ole tismalleen sama kuin jailin nimi, mutta niin lähellä, että siitä selviää mistä on kyse. Fail2bannin jailit saat näkyviin:

fail2ban-client status

Sieltä löytyi jail nimeltä nginx-444. joten jatko oli yksinkertainen:

fail2ban-client set nginx-444 unbanip 66.249.76.131

Toki virhebannin aiheuttama syy on myös korjattava, mutta tuolla saa IP-osoitteen vapautettua haluamastaan bannista.

IP-osoitteen salliminen

Jos haluaa varmistaa, että jokin IP-osoite tai tai jopa IP-alue ei koskaan joudu bannattavaksi, niin voidaan asettaa sallittavat osoitteet. Laitetaan joko jail.local tiedostoon tai sitten jail-kohtaisesti ignoreip kohdalleen.

Sinulla lienee jail.local tiedostossa jo valmiiksi vähintään localhost sallittuna:

ignoreip = 127.0.0.1/8 ::1

IP-osoitteet erotetaan toisistaan välilyönnillä ja sinne kannattaa laittaa ainakin oman virtuaaliserverin julkinen IP-osoite. Pelkästään siksi, että jokainen sivustosi toiminto, jonka joku lisäosa tekee tai jonka itse teet SSH:ssa vaikka curl-komennolla näkyy logeissa julkisena IP-osoitteena, ei localhostina. Silloin varmistat, että nuo eivät ainakaan joudu bannatuksi, vaikka jail menisikin hieman pieleen – olen sulkenut itseni ja maailman ulos kerran.

Oman kotiosoitteen salliminen on makuasia. Jos sitä ei laita ignore-listaan, niin kotoa pystyy kokeilemaan toimivuutta kuin olisi vieras.

Minulla oli yhdessä vaiheessa piilotteleva virhe jossain, jonka takia Googlen botti joutui aika ajoin banniin. Ennenkuin löysin syypään, niin kiersin ongelman laittamalla Googlen bottien koko IP-avaruuden ignorelistaan.

Ne IP-osoitteet, jotka sallit jail.local tiedostossa, ovat globaaleja ja pätevät kaikkien jailien kanssa. Yleensä per jail pystyy ylittämään jail.local tiedoston oletusasetukset, jolloin voisi asettaa jokaiseen jailiin ikiomat ignoreip-listat, mutta asia ei aivan toimi noin ignoreip kanssa. Jail nimittäin käyttää molempia.

Se aiheuttaa kaksi asiaa:

  • sallituiksi menevät molemmat IP-listat, jos ne ovat erilaisia jail.local tiedostossa ja jailissä
  • jos niissä on samoja, niin saat virheilmoituksen malliin:

fail2ban.filter[11739]: WARNING Ignore duplicate '31.13.64.0/18' ('31.13.64.0/18'), already in ignore list

IP-osoitetta ei estetä, mutta tuo turha urputus on ärsyttävää, koska se toistuu joka kerta, kun IP osuu ignore-listaan. Joten kannattanee käyttää joko yhtä jail.local tiedostossa tai sitten jokaisessa jailissä aidosti erilaisia.

IP alueen estäminen

Joskus pääsee helpommalla, kun bannaa samantien ns. turhat IP:t. Jos googletat Geo-tietoon perustuvan tavan, niin saat äkkiä suljettua sinällään turhat Kiinan, Egyptin, Romanian, Syyrian, Etelä-Korean ja pahimman kaikista: USA:n (älä sulje, sivustosi lakkaa toimimasta). Siinä on vain riskinä, että suljetaankin aivan vääriä osoitteita. Sen sijaan voi valitakäyttöön vaivaalloisemnan tien ja sulkea alueita käsin.

Esimerkiksi sellainen erittäin huonotapainen korealainen botti kuin Daum operoi (ainakin) IP-alueella 203.133.160.0 – 203.133.191.255. Tuohon mahtuu sen verran IP-avaruutta, että sen sulkeminen IP kerrallaan per käyntikerta on ikuisuusprojekti. Toki Daum tulee sulkea user agentinsa mukaan webserverillä tai Varnishissa, mutta saman asian saa tehtyä Fail2bannissakin käsin.

Kun törmäät logeissa johonkin kolkuttelijaan, niin saat tarkistetua helposti mistä päin maailmaa IP osoite tulee:

whois 116.202.35.92

Samalla saat tiedon sen palveluntarjoajan IP-avaruudesta.

Et pysty antamaan Fail2bannissa IP-osoitteita jokerimerkillä malliin 203.133.160.* enkä myöskään väliviivalla 203.133.160.0 – 203.133.191.255. On pakko käyttää ns. CIDR muotoa (Classless Inter-Domain Routing, luokaton reititys), jossa kerrotaan alkava IP-osoite ja sitten toisella luvulla kuinka suuri avaruus on.

  • 203.133.160.* = 203.133.160.0/24 (203.133.160.0 – 203.133.160.255)
  • 203.133.*.* = 203.133.0.0/16 (203.133.0.0 – 203.133.0.0 – 203.133.255.255)
  • 203.*.*.* = 203.0.0.0/8 (203.0.0.0 – 203.255.255.255)

Onneksi IP-avaruuden kertova numero ei ole vakioitu mihinkään 8/16/24 arvioihin, kuten esimerkissä, vaan CIDR kertoo minkä vaan IP-alueen koon. Kun googlettaa cidr calculator niin löydät useammankin – helpomman ja vaikeudestaan päätellen laajempia. Moisella laskurilla saadaan tulokseksi, että koko tuon Daumin käyttämän IP-alueen sulkeminen kertalaakista onnistuu tällä:

fail2ban-client set <jail> banip 203.133.160.0/19

Jos innostut sulkemaan IP-avaruuksia urakalla ja oivallat, että niitähän voi kerätä tai generoida käsin listaan eli tekee pseudo-login, jonka antaa sitten automaagisesti jailin pureskeltavaksi, koska haluat välttää työn bannata niitä käsin, niin minulla on huonoja uutisia. Ainakaan vielä versiossa 0.10.2 ei pysty logi-tiedoston kautta sulkemaan kokonaisia alueita. Ainoastaan erilliset IP-osoitteet kelpaavat.

On toinen huono uutinen. IP-osoitteiden kerääminen on tolkuton työ. Elämässään voi tehdä muutakin kuin vahtia logeja ja selvittää IP-osoitteita. Mutta sen voi tehdä, minä olen kerran uhrannut aika paljonkin aikaani moiseen turhuuteen.

Oma jail.local ja filtterit

Löydät googlella aivan samat, mutta jos haluat oikaista, niin voit vilkaista mikä minulla on käytössä olevana versiona muutamalla wordpressillä, parilla woocommercella ja moodlella.

Suuntaa tänne:

https://git.eksis.one/jagster/Fail2ban