1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

OpenVPN Connection Email Notifications

Discussion in 'Tomato Firmware' started by lancethepants, Jan 17, 2012.

  1. lancethepants

    lancethepants Network Guru Member

    I thought it would be a cool idea to have the router send an email every time a client connected. For my own sake, I decided to set this up using python and gmail.

    First you'll need to install optware on your router. Check out this link to set it up.
    http://tomatousb.org/tut:how-to-set-up-nas-optware-etc-for-total-noobs

    Once optware is installed, run
    Code:
    ipkg install python27
    Then you'll need to load a couple of python scripts onto your router.

    /opt/scripts/mail.py
    Code:
    import os
    import smtplib
    import mimetypes
    from email.MIMEMultipart import MIMEMultipart
    from email.MIMEBase import MIMEBase
    from email.MIMEText import MIMEText
    from email.MIMEAudio import MIMEAudio
    from email.MIMEImage import MIMEImage
    from email.Encoders import encode_base64
    
    def sendMail(subject, text, *attachmentFilePaths):
      gmailUser = yourgmailaccount@gmail.com'
      gmailPassword = 'gmailpassword'
      recipient = 'recipientaddress@whatever.com'
    
      msg = MIMEMultipart()
      msg['From'] = gmailUser
      msg['To'] = recipient
      msg['Subject'] = subject
      msg.attach(MIMEText(text))
    
      for attachmentFilePath in attachmentFilePaths:
        msg.attach(getAttachment(attachmentFilePath))
    
      mailServer = smtplib.SMTP('smtp.gmail.com', 587)
      mailServer.ehlo()
      mailServer.starttls()
      mailServer.ehlo()
      mailServer.login(gmailUser, gmailPassword)
      mailServer.sendmail(gmailUser, recipient, msg.as_string())
      mailServer.close()
    
      print('Sent email to %s' % recipient)
    
    def getAttachment(attachmentFilePath):
      contentType, encoding = mimetypes.guess_type(attachmentFilePath)
    
      if contentType is None or encoding is not None:
        contentType = 'application/octet-stream'
    
      mainType, subType = contentType.split('/', 1)
      file = open(attachmentFilePath, 'rb')
    
      if mainType == 'text':
        attachment = MIMEText(file.read())
      elif mainType == 'message':
        attachment = email.message_from_file(file)
      elif mainType == 'image':
        attachment = MIMEImage(file.read(),_subType=subType)
      elif mainType == 'audio':
        attachment = MIMEAudio(file.read(),_subType=subType)
      else:
        attachment = MIMEBase(mainType, subType)
      attachment.set_payload(file.read())
      encode_base64(attachment)
    
      file.close()
    
      attachment.add_header('Content-Disposition', 'attachment',  filename=os.path.basename(attachmentFilePath))
      return attachment
    
    Code obtained from:
    http://codecomments.wordpress.com/2008/01/04/python-gmail-smtp-example/

    Fill in 'gmailUser', 'gmailPassword', and 'recipient' with your own credentials.
    You can fill out the SMTP information with your own if you want to use something other than gmail.

    /opt/scripts/client-connect.py
    Code:
    import datetime
    import os
    import mail
    
    time = datetime.datetime.now()
    name = os.environ['common_name']
    localip = os.environ['ifconfig_pool_remote_ip']
    remoteip = os.environ['untrusted_ip']
    formatted_time = time.strftime("%a %b %d %H:%M:%S%Z %Y:")
    
    subject = "OpenVPN Client Connection"
    message = formatted_time + " user \'" + name + "\' connected (local " + localip + " remote " + remoteip + ")"
    
    mail.sendMail(subject, message)
    
    
    In the OpenVPN server 'Custom Configuration' space in the web gui, put:
    Code:
    script-security 2
    --client-connect "/opt/bin/python2.7 /opt/scripts/client-connect.py"
    
    Each time a client connects, it will send an email notification to the recipient. You can play around with the message formatting in client-connect.py if you want to customize the email.

    The provided formatting will send a message looking like this
    Code:
    Tue Jan 17 18:52:04 2012: user 'client' connected (local 192.168.1.75 remote xxx.xxx.xxx.xxx)
    
    OpenVPN also provides many other variables you can retrieve and display if you like. For a list of all these variables look under the "Environmental Variables" section of the following link.

    http://openvpn.net/index.php/open-source/documentation/manuals/65-openvpn-20x-manpage.html

    Hope you find this useful. If anyone has interest, I can also make a write-up of how to send this as a text message instead using google voice.
     
  2. cpshankar

    cpshankar Networkin' Nut Member

    can you do this php? i would like to when an oven client goes off line and comes online. I would like a text message and an hourly summary email. this is a cool idea.
     
  3. lancethepants

    lancethepants Network Guru Member

    I used to play around with Google Voice texting with PHP. I even had a buddy setup a personal web site using php to send mass texts before google recently made this available.

    Google, however, has not released an official API. Most implementations to do this are hacky and are rather prone to break when google decides to change something with their login page. I looked for and tested a couple of PHP scripts, but could not find any working implementation. If you can find a working PHP script, I'd be happy to play with it.

    The Python script I've used for google voice has been consistently updated when things break, which is why I find it preferable. Python is just as easy to setup on the router as PHP. If you like, I can try to do what you described, but in Python, unless you can find a working PHP script.
    This is what I use for google voice.
    http://everydayscripting.blogspot.com/2011/10/login-problems-solved.html
     
  4. lancethepants

    lancethepants Network Guru Member

    Here is a tutorial to do what you described, in Python. In other words, this will send a text every time a client connects. It will also send an email with the past hours vpn logs.

    First you'll need to install optware on your router. Check out this link to set it up.
    http://tomatousb.org/tut:how-to-set-up-nas-optware-etc-for-total-noobs

    Once optware is installed, run
    Code:
    ipkg install python27
    You will need to create two files for this script.
    /opt/logs/vpn
    /opt/logs/hourly_vpn

    /opt/logs/vpn - will keep a continuous log of all vpn connections
    /opt/logs/hourly_vpn -will maintain logs for the current hour. Once they have been emailed at the end of the hour, it will be wiped clear.

    Download and unpack 'scripts.zip' and upload the following scripts and place them in /opt/scripts/
    client-connect.py
    gvoice.py
    hourly_vpn.py
    mail.py

    Code sources for mail.py
    http://codecomments.wordpress.com/2008/01/04/python-gmail-smtp-example/

    Code sources for gvoice.py
    http://everydayscripting.blogspot.com/
    https://github.com/hillmanov/gvoice

    Modify mail.py with your gmail username, password, and the recipient email.
    Modify client-connect.py with your gmail username, password, and text recipient number.

    In the OpenVPN server 'Custom Configuration' space in the web gui, put:
    Code:
    script-security 2
    --client-connect "/opt/bin/python2.7 /opt/scripts/client-connect.py"
    
    In Administration -> Scheduler -> Custom1
    Code:
    /opt/bin/python2.7 /opt/scripts/hourly_vpn.py
    Set it to execute every hour.

    And I believe that should do it. gvoice.py is liable to break because google occasionally changes how their logins work. You can check for updated code at the previous sited source if that happens.
     

    Attached Files:

  5. Milos

    Milos Networkin' Nut Member

    to lancethepants:

    I downloaded and used yours scripts and after this I've got error msg:

    root@asus:/tmp/home/root# /opt/bin/python2.7 /opt/scripts/client-connect.py
    Traceback (most recent call last):
    File "/opt/scripts/client-connect.py", line 6, in <module>
    name = os.environ['common_name']
    File "/opt/lib/python2.7/UserDict.py", line 23, in __getitem__
    raise KeyError(key)
    KeyError: 'common_name'

    So whats wrong ?
     
  6. lancethepants

    lancethepants Network Guru Member

    Looks like you've tried to run the script manually from the command line. It won't work just manually executed. It must be placed in the web gui. It makes sense that 'common_name' wasn't found because it will be supplied directly from OpenVPN when someone connects. Or were there issues before this, and you tried running it manually to check it out?

    OpenVPN_GUI.png
     
  7. Milos

    Milos Networkin' Nut Member

    I have used it so, but after adding this 2 lines I can´t connect to my router/vpn, without them it´s possible.
    So I tested it directly from command prompt of course with problems as I have mentioned before.
     
  8. lancethepants

    lancethepants Network Guru Member

    hmmm... Double check that all the scripts are in the correct place, that you've already pre-created /opt/logs/vpn & /opt/logs/hourly_vpn, and that your gmail username and password are correct.
     
  9. Milos

    Milos Networkin' Nut Member

    OK, I checked it and for sure I´ve reinstall all Tomato firmware+python .. etc.

    Now, it´s work (little bit ;-) ) - I found new, correct records in vpn&hourly_vpn files, but :

    1. login to this server (with email notify) takes more than twice time than without it.
    2. login/psw for my (sender) gmail account are good and still I´ve got any mail. For explanation - I´m sender and also receiver for this mails.
    So, what could be it now ?
     
  10. lancethepants

    lancethepants Network Guru Member

    Just to be clear we're on the same page. There are two tutorials in this thread. The first tutorial sends an email every time a client connects. I believe you are looking at the second tutorial. The second tutorials sends a text message (not an email) when a client connects, and then sends an email once per hour with the last hour's connection logs.

    The client-connect scripts in the first and second tutorial are different. If you're looking for emails notifications only, go with the code that's displayed in the first post only.
     
  11. jjw7mc

    jjw7mc Serious Server Member

    Hi Lancethepants

    I fallowed the steps you provided in you first post, installing python, created the two scrips, only changing "gmailUser", "gmailPassword", and "recipient" options, then placed them in "/opt/scripts/" and lastly added the entries you provided in OpenVPN server "custom configuration" space. I then rebooted the router but after making these changes I cannot connect to my VPN server, if I remove the entries I added in openvpn custom configuration space, I'm able to connect to the VPN sever again.
    I took a look at the logs and I see this entry from the vpn server
    Code:
    WARNING: Failed running command (--client-connect): external program exited with error status: 1
    
    I've double checked my gmail user name and password and they are correct. Any idesas why its not working?
     
  12. philess

    philess Networkin' Nut Member

    Wow this is great! Thanks so much for this lancethepants!
    Now if only i could get the same working but for when a (new) WLAN client connects ;-)

    I changed the formatting a little bit to my taste (the first tutorial, sending Email).
    Just thought it would be nice to have a multiline email and more info on disconnect,
    and maybe someone else will like my formatting too, so here it is.

    /opt/scripts/client-connect.py
    Code:
    import datetime
    import os
    import mail
     
    time = datetime.datetime.now()
    formatted_time = time.strftime("%A, %d. %B %Y %H:%M:%S")
    name = os.environ['common_name']
    localip = os.environ['ifconfig_pool_remote_ip']
    remoteip = os.environ['untrusted_ip']
     
    subject = "[TOMATO] OpenVPN CONNECT"
    message = formatted_time + "\r\n\r\nNew OpenVPN Client has connected.\r\n\r\nCommon Name: " + name + "\r\nRemote IP: " + remoteip + "\r\nLocal IP: " + localip + "\r\n\r\nEOF"
     
    mail.sendMail(subject, message)
    Output:
    Code:
    Wednesday, 06. February 2013 11:06:30
     
    New OpenVPN Client has connected.
     
    Common Name: clientmobile1
    Remote IP: 5.6.7.8
    Local IP: 192.168.10.10
     
    EOF

    /opt/scripts/client-disconnect.py
    Code:
    import datetime
    import os
    import mail
     
    time = datetime.datetime.now()
    formatted_time = time.strftime("%A, %d. %B %Y %H:%M:%S")
    name = os.environ['common_name']
    localip = os.environ['ifconfig_pool_remote_ip']
    remoteip = os.environ['untrusted_ip']
    bytesrec = os.environ['bytes_received']
    bytessent = os.environ['bytes_sent']
    duration = os.environ['time_duration']
     
    subject = "[TOMATO] OpenVPN DISCONNECT"
    message = formatted_time + "\r\n\r\nOpenVPN Client has disconnected.\r\n\r\nCommon Name: " + name + "\r\nRemote IP: " + remoteip + "\r\nLocal IP: " + localip + "\r\nBytes sent: " + bytessent + "\r\nBytes received: " + bytesrec + "\r\nDuration: " + duration + " sec.\r\n\r\nEOF"
     
     
    mail.sendMail(subject, message)
    Output:
    Code:
    Wednesday, 06. February 2013 11:12:51
     
    OpenVPN Client has disconnected.
     
    Common Name: clientmobile1
    Remote IP: 5.6.7.8
    Local IP: 192.168.10.10.
    Bytes sent: 5404
    Bytes received: 5738
    Duration: 381 sec.
     
    EOF
    For the client-disconnect.py this extra line in OpenVPN custom config is required:
    Code:
    --client-disconnect "/opt/bin/python2.7 /opt/scripts/client-disconnect.py"
    I tried to convert the seconds of the duration to a better format but the pythontotal noob that i am, failed.
    Tried this: formatted_duration = datetime.timedelta(seconds=duration)
    But no luck, even tho seconds=10000 in it works... probably some dumb variable string stuff.
    Of course the bytes sent/received could be converted to KB/MB/GB too.
     
  13. philess

    philess Networkin' Nut Member

    If you are absolutely sure that everything is correctly set up, i could only think of your gmail-password
    containing a special character that python doesnt like... would need to be escaped. Just a hunch tho.
     
  14. lancethepants

    lancethepants Network Guru Member

    My mail.py in the first post is missing a single quotation mark, so that could be a possibility.

    Code:
    gmailUser = yourgmailaccount@gmail.com'
    Unfortuanely I can't edit the first post. But change it to look like the following, using your own email of course, if that is the issue.

    Code:
    gmailUser = 'yourgmailaccount@gmail.com'

    I'm not sure what you mean by this statement. This should work for LAN or WLAN connecting clients. I am probably misunderstanding.

    Very nice formatting, I like it very much. I've actually changed to use it myself :)
     
  15. philess

    philess Networkin' Nut Member

    Oh sorry, i guess i shouldve been clearer. Something like this, but notification when a new WLAN client connects, without VPN.
    Example, using a Guest network, get notified when someone joins. There was talk about that in another thread already
    but i couldnt get a script that was posted there running and couldnt be bothered (yet) to further spend time on it.
     
  16. jjw7mc

    jjw7mc Serious Server Member

    Thanks lancethepants...I fixed the "gmailuser" entry in the "mail.py" script as you suggested, then rebooted router for good measure but I'm still having the same issue. My gmail password has no special characters, just letters, numbers and no spaces.
     
  17. philess

    philess Networkin' Nut Member

    Are you sure you have a working Python installation? Is the path correct? like /opt/bin/python27
    Because i actually have only 2.5 installed and had to change the path, but it works fine.
    Try executing the mail.py on its own, what kind of output do you get? python /opt/scripts/mail.py
     
  18. jjw7mc

    jjw7mc Serious Server Member

    Hi Philess,

    I had no errors when I isntalled Python through Entware. The path is correct, its "opt/bin/python2.7", theres also symlink named "python" in that same direcotry that points to "python2.7"

    Last night I started from scratch, erased NVRAM and configured only OpenVPN and Entware but I still had the same problem.

    When I run "python /opt/scripts/mail.py" in putty, nothing happens but when I excute "python /opt/scripts/client-connect.py" the below happens. Lancethepants said the below is normal since the script is not meant to be excecuted manually.

    Thanks for helping, this would be icing on the cake if I could get it to work, only difference I see is that i'm using Entware and not Optware. What version of tomato you running?
    Code:
    # python /opt/scripts/client-connect.py
    Traceback (most recent call last):
      File "/opt/scripts/client-connect.py", line 7, in <module>
        name = os.environ['common_name']
      File "/opt/lib/python2.7/UserDict.py", line 23, in __getitem__
        raise KeyError(key)
    KeyError: 'common_name'
    
     
  19. lancethepants

    lancethepants Network Guru Member

    Ah, I might know the issue. With entware, they split up all the different python packages. You need to search and also install the python openssl module.

    I can't remember what it's named, but search for that, and I think that will fix things for you.

    It needs the python openssl module to authenticate with google's servers.
     
  20. jjw7mc

    jjw7mc Serious Server Member

    I'll give that a try once I get home tonight. I just did a quick look at the Entware packages and see two that might be the package you suggested. I see "pyopenssl" and "python-openssl"...Should I go with the "python-openssl"?
     
  21. lancethepants

    lancethepants Network Guru Member

    Yep, that's the one.
     
  22. jjw7mc

    jjw7mc Serious Server Member

    I went home for lunch, installed the "python-openssl" package and came back to work..............SUCCESS!!!!

    Thanks lancethepants, I love this this script you created, this is something I always wanted to implement, I can't thank you enough for sharing this.
     
  23. lancethepants

    lancethepants Network Guru Member

    Glad you got it working, it is pretty nifty.
     
  24. stoza

    stoza Serious Server Member

    Hi Lance,

    I've got Open VPN setup on a Ubuntu VM, and it works nicely. I'd like to set an email notification, using our Exchange server, to notify when a VPN user connects. Is optware strictly necessary for what I'd like to achieve? As the VM is running smoothly, with a bare minimum of stuff on there, I don't want to risk disrupting it and/or increasing the attack surface (plus I'm a Linux noob and tend to break it beyond repair relatively easily).

    NM I've done a bit more research, and I think all I need is OpenVPN --client-connect script, and then just need to configure a bash script to fire off an email.
     
  25. lancethepants

    lancethepants Network Guru Member

    Exactly right, nothing special about our setup. You can pretty much use any language you want, a bash script will work fine. I used python because it was an easier solution for me.
     
  26. spanman

    spanman Reformed Router Member

    Thanks for great tuturial lancethepants. tried to get this working on ddwrt but I am stuck with the
    script-security 2 --client-connect "/opt/bin/python2.7 /opt/scripts/client-connect.py"
    I think the problem may be related to openvpn on ddwrt not sure.
    These are all the test I did see link http://www.dd-wrt.com/phpBB2/viewtopic.php?t=174158
    Any help is appreciated, even though I know it is not related to your script.


    If this is not the correct place to post this please delete.
     
  27. lancethepants

    lancethepants Network Guru Member

    One question I have, how did you install python? I don't see any mention of optware or entware (which I now prefer). Both package managers I believe install python to /opt/bin, but your other thread mentions /opt/usr/bin.

    Let's first make sure we can successfully send an email with python.
    mail.py should already be populated with your gmail username/password and destination email address. Make sure they are properly surrounded by singe quotes ('). My first post is missing one on the 'gmailUser' line, but I wasn't able to edit that post last I checked.

    Create another python script in the same directory as mail.py, with the following code. This is just the client-connect script really stripped down. You can't really just run client-connect on its own, it needs to be called by openvpn, but this script we can run just to make sure you actually get an email sent.

    Code:
    import mail
     
    subject = "OpenVPN Client Connection"
    message = "This is my message"
     
    mail.sendMail(subject, message)
    
    then run 'python ./scriptname.py' or '/path/to/python ./scriptname.py' to give it a test.
     
  28. spanman

    spanman Reformed Router Member

    Thanks for your response, yea I can send an email if i run python /opt/scripts/client-connect.py (I edited the client-connect.py script)
    That is why i believe the problem is with the --client-connect command



    EDIT
    I use optware, to installed python i use opkg install python

    If I run the --client-connect command directly in router via telnet i get this error

    root@WDR3600 root # openvpn --client-connect "/opt/usr/bin/python2.7 /opt/script
    s/client-connect.py"
    Options error: You must define TUN/TAP device (--dev)
    Use --help for more information.
    root@WDR3600 root #
     
  29. koitsu

    koitsu Network Guru Member

    And what exact version of OpenVPN are you using?
     
  30. spanman

    spanman Reformed Router Member

    when i enter openvpn --version in router i get

    openvpn --version
    OpenVPN 2.3.1 mips-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [MH] [IPv6] built on May 27 2013
     
  31. spanman

    spanman Reformed Router Member

    On openvpn client I see the Authenticating stage which it passes then it says getting client config then I get error

    Sat Jul 13 20:39:33 2013 Attempting to establish TCP connection with [AF_INET]myip:443
    Sat Jul 13 20:39:33 2013 TCP connection established with [AF_INET]myip:443
    Sat Jul 13 20:39:33 2013 TCPv4_CLIENT link local: [undef]
    Sat Jul 13 20:39:33 2013 TCPv4_CLIENT link remote: [AF_INET]myip:443
    Sat Jul 13 20:39:33 2013 [server] Peer Connection Initiated with [AF_INET]myip:443
    Sat Jul 13 20:39:35 2013 AUTH: Received control message: AUTH_FAILED
    Sat Jul 13 20:39:35 2013 SIGUSR1[soft,auth-failure] received, process restarting


    Please note openvpn works once I remove
    script-security 2
    --client-connect "/opt/usr/bin/python2.7 /opt/scripts/client-connect.py"

    Python is in /opt/usr/bin
     
  32. lancethepants

    lancethepants Network Guru Member

    Hmmm, I'm not sure what optware you're using. The one I just installed uses the command "ipkg", not "opkg", and installs python in to /opt/bin.
    Entware uses the command "opkg", but also installs python in to /opt/bin.
    Make sure you install the python-openssl package of whatever you're using, if it exists separate. That may be, entware is this way. Otherwise, I am stumped, I'm not familiar with the optware you are using.
     
  33. spanman

    spanman Reformed Router Member

  34. koitsu

    koitsu Network Guru Member

    Also, the errors you're posting are two separate things. The first clearly shows that you're needing to use the --dev flag (and this is why I asked what version of OpenVPN you were using), while the 2nd conflicts with the first (it indicates you're able to actually run/launch OpenVPN, while the first clearly shows it doesn't start/run).

    It would be beneficial if lancethepants could disclose what OpenVPN version he's using, as they may have changed the command line parsing code between versions, or the different Entware/Optware installations you two are using have OpenVPN that's built differently. Both are plausible explanations.

    Overall I would suggest trying Entware if you could, given that Optware isn't even maintained reliably any more, and the Entware folks understand what the hell they're doing. :) You'll need to completely nuke (or move out of the way) your existing /opt bits. Entware does have OpenVPN, as well as python-openssl.

    Code:
    root@gw:/tmp/home/root# opkg list | grep openvpn
    openvpn-easy-rsa - 2013-01-30-1 - Simple shell scripts to manage a Certificate Authority
    openvpn-nossl - 2.3.0-1 - Open source VPN solution using plaintext (no SSL)
    openvpn-openssl - 2.3.0-1 - Open source VPN solution using OpenSSL
    openvpn-polarssl - 2.3.0-1 - Open source VPN solution using PolarSSL
    root@gw:/tmp/home/root# opkg list | grep python-openssl
    python-openssl - 2.7.3-2 - Python support for OpenSSL
    
     
  35. spanman

    spanman Reformed Router Member

    Thanks koitsu the first error
    Options error: You must define TUN/TAP device (--dev) occurs when I try to run the command
    script-security 2
    --client-connect "/opt/usr/bin/python2.7 /opt/scripts/client-connect.py"
    in telnet on the router (openvpn server)

    The second error AUTH_FAILED is when I enter the command
    script-security 2
    --client-connect "/opt/usr/bin/python2.7 /opt/scripts/client-connect.py" in the GUI of the router (openvpn server)
    and then when i try to connect to the server from an openvpn client I see this error on the client.

    My openvpn works once I remove
    script-security 2
    --client-connect "/opt/usr/bin/python2.7 /opt/scripts/client-connect.py" from the router (openvpn server)


    I would have a look at entware today, who knows maybe I am using entware. I will also submit a bug ticket on my router's firmware website(ddwrt) letting them know that the --client-connect command in the openvpn binary they compiled on the firmaware may not be working as expected.

    Thanks guys for all your help
    spanman
     
  36. lancethepants

    lancethepants Network Guru Member

    I'm using version 2.3.2 of OpenVPN, and have used it since 2.1 variants. Perhaps support for this hasn't been compiled in. It may be part if the management interface, which can be excluded. I compile my own binaries of OpenVPN, and use the bind command in a startup script to mount it over Tomato's. This is supported in Tomato's included binaries too.
    Your optware page mentions somewhere big endienness I thought. Perhaps that is why you have to use a different optware.
     
  37. spanman

    spanman Reformed Router Member

    Thanks lancethepants I believe you are correct maybe its a problem with how ddwrt compiled openvpn in its firmware. I will look to see if I can figure out how to compile my own binaries as well. If you have any manuals or any instructions I can use to compile my own binary please don't hesitate to send me a PM. Till then I don't want to clutter this thread with issues not related to the topic.

    thanks
    spanman
     

Share This Page