Raspberry Pi SSH LED Notification
With finals over and semester break a go, I finally have found some time to play around with my second Raspberry Pi. The Raspberry Pi is a great piece of hardware with endless potential for projects. My first rpi has been setup as a dedicated media box, so using it for development purposes wasn’t an option; thankfully these little credit card size computers run $50 a pop so ordering another one wasn’t a big deal.
I have never dabbled in circuity design, so I figured the rpi would make for good basis. The RPI has GPIO (General Programming Input Output) pins. These pins allow for sensors, LCD devices, LEDs and other peripherals to be connected to it.
Since keeping track of which pins are what on the rpi can be a PITA, I decided to pick up a cobbler adapter from ADAfruit since it nicely labels what each pins is (5V, 3V, 26, 13, etc) and plugs into a breadboard easily.
Now that I had a way to send signals to and from the breadboard, it was time to start designing my first mini-project. SSH is a service I am constantly using so I wanted to build something around that. That’s when I came up with the idea of an SSH notifier. Using a already provided GPIO library, I coded up a simple SSH brute force notifier that allowed me to visually see potentially malicious traffic.
How it works:
As SSH login attempts occur, the process constantly keeps watching for failed login attempts by reading the last three lines of the access log. If it captures 3 failed login attempts in a row from the same IP address, it triggers a red LED for 30 seconds as well as adds a DROP rule in iptables for the offending IP address. After the LED is turned off, it waits for 30 seconds before checking the logs again. To avoid banning the same IP over and over again, there is a condition which checks to make sure that the last IP to be checked is not the same as the IP just banned.
After executing the SSH notifier
pi@raspberrypi ~ $ tail -f /var/log/auth.log Dec 26 07:28:06 raspberrypi sshd[7438]: pam_unix(sshd:session): session opened for user pi by (uid=0) Dec 26 07:28:08 raspberrypi sudo: pi : TTY=pts/0 ; PWD=/home/pi ; USER=root ; COMMAND=/bin/grep pi /etc/shadow Dec 26 07:28:08 raspberrypi sudo: pam_unix(sudo:session): session opened for user root by pi(uid=0) Dec 26 07:28:08 raspberrypi sudo: pam_unix(sudo:session): session closed for user root Dec 26 07:28:19 raspberrypi sudo: pi : TTY=pts/0 ; PWD=/home/pi ; USER=root ; COMMAND=/sbin/iptables -L Dec 26 07:28:19 raspberrypi sudo: pam_unix(sudo:session): session opened for user root by pi(uid=0) Dec 26 07:28:19 raspberrypi sudo: pam_unix(sudo:session): session closed for user root Dec 26 07:43:23 raspberrypi sshd[5919]: pam_unix(sshd:session): session closed for user pi Dec 26 08:17:01 raspberrypi CRON[7483]: pam_unix(cron:session): session opened for user root by (uid=0) Dec 26 08:17:01 raspberrypi CRON[7483]: pam_unix(cron:session): session closed for user root Dec 26 08:35:27 raspberrypi sshd[7499]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=berlin.local user=root Dec 26 08:35:29 raspberrypi sshd[7499]: Failed password for root from 192.168.1.122 port 58201 ssh2 Dec 26 08:35:32 raspberrypi sshd[7499]: Failed password for root from 192.168.1.122 port 58201 ssh2 Dec 26 08:35:34 raspberrypi sshd[7499]: Failed password for root from 192.168.1.122 port 58201 ssh2 Dec 26 08:35:36 raspberrypi sshd[7499]: Failed password for root from 192.168.1.122 port 58201 ssh2 Dec 26 08:35:39 raspberrypi sshd[7499]: Failed password for root from 192.168.1.122 port 58201 ssh2 |
We can see that there are more than 2 failed login attempts so as soon as the script runs it will pick up the last 5 lines, check for 3 failed passwords and ban the IP address which is depicted in the images below.
With the process running in the background, it will continue to check the logs for brute force attempts and warn me with a visual notification.
Video Demonstration:
Code:
led.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import RPi.GPIO as GPIO, time import sys GPIO.setmode(GPIO.BCM) # Takes a pin number from the GPIO def turnOn(pin): LED_pin = pin #LED_color = led GPIO.setup(LED_pin, GPIO.OUT) GPIO.output(LED_pin, True) return # Takes a pin number from the GPIO def turnOff(pin): LED_pin = pin #LED_color = led GPIO.setup(LED_pin, GPIO.OUT) GPIO.output(LED_pin, False) return |
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#!/usr/bin/env python # SSH Monitor with LED notification from led import turnOn, turnOff import array import time import os import re # Set the SSH log file director logfilepath = "/var/log/auth.log" # Function which reads the last 10 lines of a file. def readFromEnd( f ): BUFSIZ = 1024 f.seek(0, 2) bytes = f.tell() size = 10 block = -1 data = [] while size > 0 and bytes > 0: if (bytes - BUFSIZ > 0): # Seek back one whole BUFSIZ f.seek(block*BUFSIZ, 2) # read BUFFER data.append(f.read(BUFSIZ)) else: # file too small, start from begining f.seek(0,0) # only read what was not read data.append(f.read(bytes)) linesFound = data[-1].count('\n') size -= linesFound bytes -= BUFSIZ block -= 1 return ''.join(data).splitlines()[-size:] attempts = 0 ip = [] match = 0 culprit_ip = 0 while True: turnOn(18) #failedLogin = 2 file = open(logfilepath, "r") # Print one line at a time data = readFromEnd(file) # Check through the list array if (attempts < len(data)): for num in range(0,10): if (data[num].find("Failed password") > 0): temp = data[num] ip = re.findall( r'[0-9]+(?:\.[0-9]+){3}', temp) ip_check = re.findall( r'[0-9]+(?:\.[0-9]+){3}', data[num]) #print data[num] if ((ip == ip_check) and (str(ip).strip('[]') != culprit_ip) and (data[num].find("Failed password") > 0)): print ("Match", match, data[num]) match += 1 attempts += 1 elif ((ip == ip_check) and (str(ip).strip('[]') != culprit_ip) and (data[num].find("Accepted password") > 0)): match = 0 attempts = 0 # Check for 3 failed brute force attempts in a row. # When that condition is met it activates the LED if (match == 3): culprit_ip = str(ip).strip('[]') print "Potential brute force attempt from", culprit_ip # reset match count match = 0 # reset attempts attempts = 0 turnOff(18) turnOn(23) # Ban IP from server using iptables print "Banning IP: ", culprit_ip os.system("iptables -A INPUT -s "+culprit_ip+" -j DROP") time.sleep(30) turnOff(23) match = 0 attempts = 0 print "Searching again" time.sleep(5) |
I am thankful to find you very good job , saqib from pakistan