See Part 2.
My previous article was about securing your Raspberry Pi and having it alert you via email whenever something suspicious occurred. This time I’m taking it a step further, and adding real time push notifications whenever somebody logs in via ssh.
For delivering the notifications I’m using Pushover, which is a generic push service, with clients for both iOS and Android. The app isn’t free, but once you purchase it, the service is free up to 7500 messages per app, per month, which is more than enough for this purpose.
Assuming you’ve already purchased the app, you start by registering a new application with Pushover.
I’ve created a small python script for monitoring /var/log/auth.log for whenever a user logs in, but when I wrote it I took into consideration that it should be easy to extend for monitoring more files.
Here is the script
#!/usr/bin/env python
import httplib
import urllib
import subprocess
import re
from threading import Thread
from socket import gethostname
try:
from queue import Queue
except ImportError:
from Queue import Queue
class PushoverNotification(object):
_token = "PUSHOVER_APP_TOKEN"
_user = "PUSHOVER_USER_TOKEN"
@property
def token(self):
return self._token
@token.setter
def token(self,value):
self._token = value
@property
def user(self):
return self._user
@user.setter
def user(self, value):
self._user = value
def __init__(self):
pass
def send_notification(self,message,token=None, user=None):
if token is None:
token = self.token
if user is None:
user = self.user
conn = httplib.HTTPSConnection("api.pushover.net:443")
conn.request("POST", "/1/messages.json",
urllib.urlencode({
"token": token,
"user": user,
"message": message,
}), { "Content-type": "application/x-www-form-urlencoded" })
resp = conn.getresponse()
if resp.status != 200:
raise Exception("Error : %d " % resp.status)
class LogWatcher(object):
_patterns = list()
class WatcherThread(Thread):
def __init__(self, queue, filename, patterns, prefix=""):
super(LogWatcher.WatcherThread,self).__init__()
self._queue = queue
self._filename = filename
self._patterns = patterns
self._prefix = prefix
def run(self):
cmd = ['tail','-F',self._filename]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for l in iter(p.stdout.readline,''):
for m in self._patterns:
match = m.match(l)
if match is not None:
self._queue.put("%s%s" %(self._prefix,l)
break
def __init__(self):
self._queue = Queue()
def monitor_file(self, filename, patterns, prefix = ""):
monitored_patterns = list()
for pat in patterns:
if isinstance(pat,str):
p = re.compile(pat)
if p is not None:
monitored_patterns.append(p)
else:
raise Exception("Error compiling pattern:\"%s\"" % pat)
else:
monitored_patterns.append(pat)
t = LogWatcher.WatcherThread(self._queue, filename, patterns, prefix)
t.start()
def iterate_matches(self):
r = self._queue.get()
yield r
self._queue.task_done()
def main():
p = PushoverNotification()
l = LogWatcher()
pattern = re.compile("^.*sshd\\[[0-9]+\\]: [^ ]+ session opened for user ([^ ]+).*$")
l.monitor_file('/var/log/auth.log',(pattern,),"%s:" % gethostname())
for user in l.iterate_matches():
p.send_notification("%s" % user)
if __name__ == '__main__':
main()
In the main() function, the “rules” for monitoring is setup, and the following lines
pattern = re.compile("^.*sshd\\[[0-9]+\\]: [^ ]+ session opened for user ([^ ]+).*$")
l.monitor_file('/var/log/auth.log',(pattern,),"%s:" % gethostname())
setup a regular expression looking for the login pattern, and register a file for monitoring. Multiple patterns can be specified per file.
The script then starts a new thread for monitoring the file, and whenever a line matches a regular expression, the line is put on the queue, and a push notification is sent.
To use the script, fill in your Pushover application token, along with your Pushover user token and save it to /usr/local/bin/pushover.py and set execute permissions on it.
$sudo chmod 700 /usr/local/bin/pushover.py
For a quick test, try running the script
$sudo /usr/local/bin/pushover.py
Next, try logging in from another terminal. You should receive a push notification from Pushover. If you don’t receive a notification, go back and check that you filled in the tokens correctly.
Setting it up as a service
Next we’re gonna register the script as a service that starts up whenever the system reboots. Save the following shell script to /etc/init.d/pushover
#!/bin/sh
### BEGIN INIT INFO
# Provides: pushover
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
# Should-Start: $network
# Should-Stop: $network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Daemonized version of pushover
# Description: Starts the pushover daemon for realtime login monitoring.
### END INIT INFO
DAEMON=/usr/local/bin/pushover
start() {
echo "Starting pushover"
start-stop-daemon -b -o -c root -S -u root -x $DAEMON --
}
stop() {
dbpid=`pgrep -fu root $DAEMON`
if [ ! -z "$dbpid" ]; then
echo "Stopping pushover"
start-stop-daemon -o -c root -K -u root -x $DAEMON
fi
}
status() {
dbpid=`pgrep -fu root $DAEMON`
if [ -z "$dbpid" ]; then
echo "pushover: not running."
else
echo "pushover: running (pid $dbpid)"
fi
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|reload|force-reload)
stop
start
;;
status)
status
;;
*)
echo "Usage: /etc/init.d/pushover {start|stop|reload|force-reload|restart|status}"
exit 1
esac
exit 0
and set permissions on it
$sudo chmod 700 /etc/init.d/pushover
Next we’ll register the service for automatic startup
$sudo update-rc.d pushover defaults
And start the service with
$sudo service pushover start
And that’s it. You should now receive a push notification every time someone logs into your Raspberry Pi.
Extending the script
The script above is what i use for now, but as i promised the script is easy to extend. All you need is to provide a regular expression, and a file to monitor. I recommend using something like Reggy for testing your expressions.
E.g. if you’d like a notification every time fail2ban bans an IP, the following regular expression will filter that out
"^.*fail2ban.actions: WARNING \\[ssh\\] Ban [0-9]+.[0-9]+.[0-9]+.[0-9]+$"
and to add it, you add the following code
pattern = re.compile("^.*fail2ban.actions: WARNING \\[ssh\\] Ban [0-9]+.[0-9]+.[0-9]+.[0-9]+$")
l.monitor_file('/var/log/fail2ban.log',(pattern,),"%s:" % gethostname())
main() should look like this after adding
def main():
p = PushoverNotification()
l = LogWatcher()
pattern = re.compile("^.*sshd\\[[0-9]+\\]: [^ ]+ session opened for user ([^ ]+).*$")
l.monitor_file('/var/log/auth.log',(pattern,),"%s:" % gethostname())
pattern = re.compile("^.*fail2ban.actions: WARNING \\[ssh\\] Ban [0-9]+.[0-9]+.[0-9]+.[0-9]+$")
l.monitor_file('/var/log/fail2ban.log',(pattern,),"%s:" % gethostname())
for user in l.iterate_matches():
p.send_notification("%s logged in" % user)
I may add a config file later on to make it easier to add rules, but for now some Python knowledge is required.