Stoppe maset fra robotene

Hvis du kjører en ssh-innloggingstjeneste på en maskin som er tilgjengelig fra Internett, er jeg ganske sikker på at du har sett noe som likner på dette i autentiseringsloggene dine:

Sep 26 03:12:34 skapet sshd[25771]: Failed password for root from 
200.72.41.31 port 40992 ssh2
Sep 26 03:12:34 skapet sshd[5279]: Failed password for root from 
200.72.41.31 port 40992 ssh2
Sep 26 03:12:35 skapet sshd[5279]: Received disconnect from 
200.72.41.31: 11: Bye Bye
Sep 26 03:12:44 skapet sshd[29635]: Invalid user admin from 
200.72.41.31
Sep 26 03:12:44 skapet sshd[24703]: input_userauth_request: 
invalid user admin
Sep 26 03:12:44 skapet sshd[24703]: Failed password for invalid user 
admin from 200.72.41.31 port 41484 ssh2
Sep 26 03:12:44 skapet sshd[29635]: Failed password for invalid user 
admin from 200.72.41.31 port 41484 ssh2
Sep 26 03:12:45 skapet sshd[24703]: Connection closed by 200.72.41.31
Sep 26 03:13:10 skapet sshd[11459]: Failed password for root from 
200.72.41.31 port 43344 ssh2
Sep 26 03:13:10 skapet sshd[7635]: Failed password for root from 
200.72.41.31 port 43344 ssh2
Sep 26 03:13:10 skapet sshd[11459]: Received disconnect from 
200.72.41.31: 11: Bye Bye
Sep 26 03:13:15 skapet sshd[31357]: Invalid user admin from 200.72.41.31
Sep 26 03:13:15 skapet sshd[10543]: input_userauth_request: invalid 
user admin
Sep 26 03:13:15 skapet sshd[10543]: Failed password for invalid user 
admin from 200.72.41.31 port 43811 ssh2
Sep 26 03:13:15 skapet sshd[31357]: Failed password for invalid user 
admin from 200.72.41.31 port 43811 ssh2
Sep 26 03:13:15 skapet sshd[10543]: Received disconnect from 
200.72.41.31: 11: Bye Bye
Sep 26 03:13:25 skapet sshd[6526]: Connection closed by 200.72.41.31

Så blir gjentakelsene ganske kjedelige å følge med på. Det er slik et "brute force"-angrep ser ut. Det betyr at noen, eller mer sannsynlig en datamaskin noen har brutt seg inn på, prøver en tilnærmet uendelig rekke kombinasjoner av brukernavn og passord for å finne frem til noe som gir dem adgang til systemet ditt.

Det enkleste ville være å skrive en regel i pf.conf som blokkerte all tilgang. Det ville i sin tur føre til en helt ny klasse med problemer, ikke minst hva du måtte finne på for å la personer med legitime ærender på maskinen din slippe til. Du kunne kanskje overveie å flytte tjenesten til en annen port, men det er sannsynligvis lite som hindrer de som nå krafser på port 22 i å skanne seg frem til port 22222 for å fortsette.

Siden OpenBSD 3.7 har PF tilbudt en litt mer elegant løsning. Du kan skrive pass-regler slik at de håndhever visse begrensninger på hva maskinene som kobler seg til kan gjøre. I tillegg kan du legge de som går over grensene over i en tabell med adresser som du sperrer for all eller noe tilgang. Hvis du vil, kan du til og med velge å droppe alle eksisterende forbindelser fra maskiner som går ut over grensene du setter. Det gjør du slik:

Først setter du opp tabellen. I tabelldelen legger du til

table <bruteforce> persist

Så setter du opp ganske tidlig i regelsettet ditt en regel for å blokkere trafikk fra adressene i tabellen:

block quick from <bruteforce>

Og til slutt skriver du pass-regelen.

pass inet proto tcp from any to $localnet port $tcp_services \
        flags S/SA keep state \
	(max-src-conn 100, max-src-conn-rate 15/5, \
         overload <bruteforce> flush global)

Dette er ganske likt noe vi har sett før, eller hva? Faktisk er den første delen identisk med den generelle pass-regelen vi satte sammen tidligere. Delen i parentes er det nye materialet, som vil fjerne en del av belastningen på nettet ditt.

max-src-conn er det antallet samtidige forbindelser du tillater fra en enkelt maskin. I dette eksempelet har jeg satt det til 100. I din konfigurasjon passer det kanskje med en noe lavere eller noe høyere verdi.

max-src-conn-rate er et mål på hvor ofte en gitt maskin får lov til å opprette nye forbindelser. Her er dette angitt til 15 forbindelser i løpet av 5 sekunder. Igjen er det opp til deg selv å avgjøre hva som passer for ditt oppsett.

overload <bruteforce> betyr at hvis en maskin prøver å gå ut over disse begrensningene, blir adressen dens lagt til i tabellen bruteforce. Regelsettet vårt blokkerer som vi vet all trafikk fra adresser i tabellen bruteforce.

Til slutt angir flush global at når en maskin går over grensene, vil de aktive forbindelsene fra den aktuelle maskinen bli avsluttet. Vi har også med global, som betyr at for sikkerhets skyld avslutter vi også forbindelser som ville blitt sluppet gjennom av andre pass-regler.

Effekten er ganske dramatisk. De som prøver seg, ender som oftes opp med å generere meldinger av type "Fatal: timeout before authentication". Da kan de ikke plage oss mer, om ikke annet.

Husk igjen på at regelen i dette eksempelet først og fremst er ment som illustrasjon. Det er ikke usannsynlig at du på ditt nettverk er bedre tjent med andre regler eller kombinasjoner av regler.

Det er for eksempel mulig at du ønsker å tillate et rimelig stort antall forbindelser generelt, men gjerne vil stramme litt til når det gjelder ssh. Da kan du supplere regelen over med noe slikt som dette, litt tidligere i regelsettet:

pass quick proto { tcp, udp } from any to any port ssh \
        flags S/SA keep state \
        (max-src-conn 15, max-src-conn-rate 5/3, \
        overload <bruteforce> flush global)

Du finner frem til den beste kombinasjonen for akkurat din situasjon ved å lese de aktuelle man-sidene og brukerhåndboken for PF, og kanskje litt eksperimentering.

expiretable rydder tabellene

Nå er vi kommet så langt at vi har tabeller som vil bli fylt opp automatisk av overload-regler, og siden det er rimelig å forvente at gatewayene våre vil ha oppetid som måles i måneder, vil tabellene gradvis vokse seg større og ta stadig mer plass i minnet.

Du kan også oppleve at en IP-adresse du blokkerte i forrige uke på grunn av for stor trafikk faktisk var en dynamisk tildelt adresse, som nå brukes av en annen kunde hos den aktuelle ISPen, som faktisk har legitim grunn til å prøve å kommunisere med maskiner i ditt nettverk.

Det var slike situasjoner som førte til at Henrik Gustafsson skrev expiretable, som fjerner tabelloppføringer som ikke har blitt referert i et gitt tidsrom.

Et nyttig eksempel kan være å bruke expiretable som metode for å fjerne foreldete oppføringer i <bruteforce>-tabellen.

Du kan for eksempel la expiretable fjerne oppføringer i <bruteforce>-tabellen som er eldre enn 24 timer ved å legge til en linje i /etc/rc.local med dette innholdet:

/usr/local/sbin/expiretable -v -d -t 24h bruteforce

expiretable ble nylig tatt med i ports-treet på FreeBSD og OpenBSD[1].

Hvis expiretable ikke er tilgjengelig via pakkesystemet du bruker, kan du laste ned programmet fra Henriks web på http://expiretable.fnord.se/

Fotnoter

[1]

som henholdsvis /usr/ports/security/expiretable og /usr/ports/sysutils/expiretable