19 abril 2015

Bloquear, banear las ips de atacantes al ssh en Raspberrypi con un script de Python

Los que tenemos una Raspberry pi y nos conectamos desde cualquier parte del mundo :-) a través de ssh, tenemos un problema con ciertos personajes que quieren acceder también a ella vía bruteforce. Si no sabes de lo que estoy hablando te aconsejo que accedas al fichero auth.log dentro del directorio /var/log/, aquí podrás ver los múltiples intentos de acceso que tienes.
Cansado de que se quisieran conectar a ella, decidí crearme un script en python que lee el fichero auth.log y mete las ips  en un conjunto y las añade al iptables. Con ello banneo o bloqueo todas las ips extrañas.
Antes de ejecutar el script deberás instalar la aplicación ipset y iptables si no las tienes ya instaladas, para ello deberás ejecutar los siguientes comandos:

  1. sudo apt-get update
  2. sudo apt-get install ipset
  3. sudo apt-get install iptables

Aquí os dejo el script pero podéis descargarlo desde github 

#!/usr/bin/env python3.4
 
"""
 sudo ipset create banthis hash:ip maxelem 1000000
 sudo ipset add banthis 1.1.1.1
 sudo iptables -I INPUT -m set --match-set banthis src -p tcp --destination-port 22 -j DROP


 $ sudo ipset save banthis > banthis.txt
 $ sudo ipset destroy banthis
 $ sudo ipset restore -f banthis.txt

"""

# IMPORTS
import hashlib
import re
import socket
import pprint
import subprocess
import commands
import os
import time
 
# VARS
log_path = '/var/log/auth.log'
hosts=[]
full_hosts_data=[]
previous_ip = ""
previous_host = ""

#Create ipset
def create_banthis():
 return subprocess.call("ipset create banthis hash:ip maxelem 1000000" ,shell=True);

#Save ipset
def save_banthis():
 return subprocess.call("ipset save banthis > banthis.txt" ,shell=True);

#Add ip to ipset
def anhade_ip(ip):
 return subprocess.call(["ipset", "add", "banthis", ip]);

#Add ipset to iptables
def anhade_iptables():
 subprocess.call("iptables -I INPUT -m set --match-set banthis src -p tcp --destination-port 22 -j DROP",shell=True);

def borra_iptables():
 p = subprocess.Popen(["iptables", "-L", "--line-numbers"], stdout=subprocess.PIPE);
 output , err = p.communicate();
 for line in output.split("\n"):
  check_1 = line.find("banthis");
  if check_1 != -1 :
   index= subprocess.call("iptables -D INPUT " + line[0],shell=True);

#Check requirements
def check_requirements():
  ipset_status = commands.getstatusoutput("hash ipset")
  iptables_status = commands.getstatusoutput("hash iptables")
  if ipset_status[0] != 0 or iptables_status[0] != 0:
    raise Exception("Iptables and/or ipset not found, please intstall these dependencies first")

#Adjust lines
def adjust_item( str, i ):
    if len(str) < i:
        for j in range(i-len(str)):
            str = str + " "
    return str
 
#Lookup ip by name
def get_ip_hostname(name):
 try:
            return socket.gethostbyname(name);
        except Exception:
            return "0";

#Date
def get_date( my_line ):
    date_words = my_line.split(":")
    date = date_words[0] +":"+ date_words[1] +":"+ ((date_words[2]).split(" "))[0]
    return date

try:
 start_time = time.time();
 check_requirements();
 if save_banthis() == 1:
  create_banthis();

 # READ FILE
 with open(log_path, 'rt') as log:
     text = log.read();

 # COLLECTING HOSTS AND IPS
 for line in text.split("\n"):
  if "sshd" in line:
   if len(line) > 5:
    check_1 = line.find("rhost=")
    if check_1 != -1 :
     words = line[check_1:len(line)].split(" ");
     if len(words) > 2 :
      if len(words[2]) > 5 :
       user = words[2].split("=")[1];
       ip = words[0].split("=")[1];
       host = ip;
       pat = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$");  
       test = pat.match(ip);
       if not test:
        ip = get_ip_hostname(ip);
        if ip != "0":
         host = ip;  
       exists_check = 0
       for my_host in hosts:
        if my_host["ip"] == ip:
         exists_check = 1;
       if exists_check == 0:
        if not "192.168." in ip :
         hosts.append({"ip":ip, "hostname":host , "accounts":user, "date":get_date( line )});

 # PRINT TABLE HEADERS
 print(
  adjust_item("DATE", 16 ),
  adjust_item("IP", 15),
  adjust_item("HOSTNAME", 40),
  adjust_item("ACCOUNTS", 30)
 )
 for item in hosts:
  anhade_ip(item["ip"])
  parsed_ip           = adjust_item( item["ip"],          15 )
  parsed_date         = adjust_item( item["date"],        16 )
  parsed_host         = adjust_item( item["hostname"] ,   40 )
  parsed_accounts     = adjust_item( item["accounts"],    30 )
  print(
   parsed_date[:16],
   parsed_ip, parsed_host[:40],
   parsed_accounts[:30],
  )
 borra_iptables();
 anhade_iptables();
 print("--- %s seconds ---" % (time.time() - start_time));
except Exception as e:
 print str(e);

2 comentarios:

Trend Devs dijo...

El uso exclusivo de claves públicas para autenticación SSH en lugar de passwords te evitaría también ataques por fuerza bruta.

Jorge dijo...

Si Trend puedes utilizar RSA, con esto consigo que las ips atacantes queden bloqueadas para realizar cualquier ataque.