Showing posts with label WebUI. Show all posts
Showing posts with label WebUI. Show all posts

Sunday, September 20, 2015

5 Ways to Secure Your Raspberry Pi's Websocket Server.



Websocket's are a great way to transmit real time data.  I use them quite often from transfering picam to the web, controlling lights, controlling a raspberry pi picture frame, and controlling my sprinklers(pending post).  ALL with the Raspberry Pi.  But if it's done without security someone somewhere can tap in and take control.  Things could end up pretty bad if it's used to stream video and control lighting.

1. WSS (WebSockets over SSL/TLS).

First I strongly strongly recommend SSL/TLS encryption.  Just like https it encrypts the traffic between the client and server.  Nothing should be transmitted in plain text to a client. Anyone smart enough to be listening on the connection can probably find out how your websocket protocol is working and take control.  For examples on how to create a wss capable server refer to:
Some problems arise when using self-signed certificates with some clients/browsers. See here.


2.  Query String Authentication

Creating a connection connection to your websocket server by your connection parameters (sometimes a username/password) and appending it as a Query String to the end of the connection:

    var websocket = new Websocket("wss://rpi?user=Eben&password=Upton");

I don't recommend doing it that way especially if you don't have a secure SSL/TLS connection.

3. CHAP Authentication (Challenge Response Authentication)

I was not liking the query string method and knowing that just securing the channel using SSL/TLS wasn't enough I tried implementing a CHAP authentication routine for my Autobahn WS server on my Pi.
CHAP explained on wikipedia.  CHAP is a 3 way handshake:

  1. Client Connects to the websocket server, server then sends a challenge string (random characters of random or set length) to client.  
  2. Client responds with the hash of the challenge+'shared secret' 
  3. Server calculates the challenge it sent and the 'shared secret' it has locally and compares the client's hash to it's own and either authenticates (adds it to approved clients) or drops the client.
I went a bit further in my code and if the client was dropped I added him to a blocked list with a timestamp.  If the client tries to connect I check if he is in the blocked list and if his timestamp is old enough. Then continue with the CHAP auth for the client again.  I use a SHA256 one-way hash in my example.  Since I don't use nodejs I only have an autobahn example:

The javascript library I used for SHA256 is found here

4. Basic/Digest/Forms Authentication

Basic/Digest is a common way the web authenticates it's clients.  Uses a username and password authentication and there several APIs that support it.  Explanation of Digest Auth 

This can be done before a connection to the websocket server is made using a post to the server.  The server can then add the client to the list of accepted connections. 

5. Auth Header

*This only works if the client is NOT a webpage.  Just like basic/digest authentication some websocket servers can read custom headers.  Through the current web api for websockets you are not able to modify the headers in any way.  But your client may able to change his auth header through nodejs, autobahn, and socketrocket

Bonus. Using A Third Party Authentication

One way to authenticate it use a third party authentication service.  
With websockets becoming more and more popular for a easy-to-make realtime feed for your Raspberry pi projects it's important to keep your pi safe from all sides.  It's possible to layer these options to improve security.  I strongly suggest using websocket SSL/TLS always and at least one of the authentication methods.  

Please share

Sunday, September 6, 2015

Raspberry Pi: How to create your own WebSocket API - Part 1



A while I posted my very first raspberry pi home automation project (LightSwitchPi) and since then I've learned a lot more python and circuitry.  I, being one of many, achieving exactly what the Raspberry Pi Foundation wanted, Education.

Here's a quick tutorial on how to create and form your own home automation api for expansion.  I am going to use Autobahn Python for the WebSocket backend and in Part 2 going to build a simple iOS app to communicate with the Pi via WebSockets (SocketRocket).

Part 1: Custom Sensor/Control Modules:

Install the following if you don't have it:

  • For WebSockets, Autobahn Python :
    • sudo apt-get install python-twisted python-pip 
    • sudo pip install autobahn
    • github example
In the github example there are several files to note:
  • websocket.py
    • The autobahn python based websocket broadcast server with ssl encryption.
  • ButtonListener.py
    • The lightswitch (module) using the first gen PiFace Relay Board.  
  • PlugPoller.py
    • A wifi plug I built (link here, forgive the sloppy design :-P ) with a uart-to-wifi interface.
  • web folder
    • simple webpage showing the broadcasts from the websocket server for testing.
First we need to set up our modules.  I'm using the term modules to clarify that you can at anytime add to your websocket server "things" your Raspberry pi can do (I.E temperature sensing, motion, etc...).  In each module you set it up as a separate thread.  

For example ButtonListener is as Follows:

import threading
import os
import pifacedigitalio
from sys import exit
from time import sleep
import time

class Buttons(threading.Thread):
    cmd = None
    loop = True
    button1Callback = None
    button2Callback = None #set up a callback for each button
    button2LongPressCallback = None #set up custom stuff if needed

We start by importing our needed libraries and creating a class of the "threading.Thread) type. I am creating several callback variables so that when we instantiate our class in the websocket server we can assign those callbacks to broadcast to our clients the status of the button presses.  We continue with the init and the run.  Take note that when input[x] is pressed it sets a variable to true and then doesn't react till you release the button. A way to programmatically set a rising/falling edge detection for button presses.  

def __init__(self):
        threading.Thread.__init__(self)
        self.cmd = pifacedigitalio.PiFaceDigital()
        self.loop = True

    def run(self):
        time_pushed = 0
        pushed_1 = False
        pushed_2 = False
        inputs = None
        # toggle button turn relay 1 on
        while self.loop:
            sleep(0.05)
            inputs = self.input_status()
            outputs = self.output_status()
            # print inputs, ' ', outputs
            if inputs[0] == '1' and pushed_1 is not True:
                pushed_1 = True
                if self.button1Callback:  # json callback for button press
                    self.button1Callback('{"Inputs":"' + self.input_status() + '"}')
            if inputs[0] == '0' and pushed_1:
                pushed_1 = False
                self.cmd.relays[0].toggle() 
                if self.button1Callback:  # json callback for button press
                    self.button1Callback('{"Outputs":"' + self.output_status() + '"}')
                    self.button1Callback('{"Inputs":"' + self.input_status() + '"}')
            if inputs[1] == '1' and pushed_2 is not True:
                pushed_2 = True
                time_pushed = time.time()
                if self.button2Callback:
                    self.button2Callback('{"Inputs":"' + self.input_status() + '"}')
            if inputs[1] == '0' and pushed_2:
                time_taken = time.time() - time_pushed
                pushed_2 = False
                if(self.button2Callback):
                    self.button2Callback('{"Inputs":"' + self.input_status() + '"}')
                if time_taken < .5:
                    self.cmd.relays[1].toggle()
                    if self.button2Callback:
                        self.button2Callback('{"Outputs":"' + self.output_status() + '"}')
                    time_pushed = 0
                if (time_taken > .5):
                    try:
                        if self.button2LongPressCallback:
                            self.button2LongPressCallback()
                    except:
                        pass
                time_pushed = 0

We also need to create a stop function so we can exit cleanly from the thread if needed. "def output_status" and "def input_status" are reading the inputs and outputs of the PiFace board and outputting them to a string for our JSON broadcast. "def output_cmd" is a quick method for changing outputs along with outputting the result to the callbacks.

    def stop(self):
        self.loop = False

    def output_status(self):
        list1 = self.cmd.output_port.value
        status = '{0:08b}'.format(list1)[::-1]
        return status

    def output_cmd(self, pin, value, local=True):
        self.cmd.leds[pin].value=value
        if self.button2Callback and not local:
            self.button2Callback('{"Outputs":"' + self.output_status() + '"}')
        return self.output_status()

    def input_status(self):
        list1 = self.cmd.input_port.value
        status = '{0:08b}'.format(list1)[::-1]
        return status


Part 2: WebSocket Broadcast Server Broadcasting

*(For the complete github code see here)
In a broadcast server you need to keep track of the following:
  • Registering clients
  • UnRegistering clients
  • Broadcasting to clients
  • When to broadcast
In my previous post I usually have a polling timer in my websocket server having the server check status of each item and sending it out on change. In this example the "modules" are doing that anyway so we will only need to assign what the callback methods do.
We will put the light switch button module in the init part of the broadcast portion of the server:

class BroadcastServerFactory(WebSocketServerFactory):

    def __init__(self, url, debug=False, debugCodePaths=False):
        WebSocketServerFactory.__init__(self, url, debug=debug, debugCodePaths=debugCodePaths)
        self.clients = []
        self.lighting = Buttons()
        self.lighting.button1Callback = self.broadcast
        self.lighting.button2Callback = self.broadcast
        self.lighting.start()
        self.plugs = PlugPoller(plugIp, 8080)
        self.plugs.statusChangeCallback = self.broadcast
        self.lighting.button2LongPressCallback = self.plugs.toggleAll

Note what we did: 

  1.  We instantiate our Button class and assign the two regular button press callbacks.  Since self.broadcast only takes one argument self.broadcast(msg) it works great for how we set it up in the button class. 
  2. Set up the plugs module along with it's callbacks. 
  3.  We also set up the long button press callback to toggle some plugs.
With those we will broadcast to our clients status changes for inputs and outputs in a JSON format. {"Outputs":"00000000"},{"Inputs":"00000000"}

Since we are not polling periodically we need to let the clients know the status of all the modules when they are registered as a client:

    def register(self, client):
        if client not in self.clients:
            client.sendMessage(json.dumps([{"Outputs":self.lighting.output_status()},
                                {"Inputs":self.lighting.input_status()},
                                {"Lamps":self.plugs.getstatus()}]))
            print("registered client {}".format(client.peer))
            self.clients.append(client)

Part 3: WebSocket Commands from clients:

When our server receives a message from a client we need to handle it if is supposed to control something.  In mine I decided since it's an output I'm toggling I just send a JSON string {"Output",pinNum}.  If it was an analog device you'd need to send a bit more.  With python you can convert json to a dictionary using json.loads(json_string).

    def onMessage(self, payload, isBinary):
        if not isBinary:
            data = json.loads(payload)
            if(data["Output"]):
                output = data["Output"]
                #get current status to toggle the output. 
                current = factory.lighting.output_status()[int(output)]
                newval = not int(current)
                #print(output,' ',current,' ',newval)
                factory.lighting.output_cmd(int(output), newval, False)
                
Remember we put a status change callback whenever we call the output_cmd.  That way all clients get the new status change.


For the iOS portion continue here

Friday, April 17, 2015

Raspberry Pi Digital Picture Frame: Part 2 Adding an Accelerometer and some more!

UPDATE (4-17-2015): 

To see "Part 1" of this project please go here
So we have few new features!!

  • Ability to hide/show clock and temperature via phone webapp 
  • Change time between image transitions via webapp
  • Uploading multiple images from the web interface now works.
  • Deleting Images now works.
  • Lazy loading of images when looking through them on the webapp. (loads as you scroll)
  •  I just added a 3-axis sensor to the 7" frame and have it broadcast to the HTML side the angle of rotation.

TODO(Coming Soon)

  • Weather feed option in the config. (see weather every 10 slides, and or outside temperature in right hand corner)
  • DNLA Support for those who have NAS servers with millions of images.
  • Option to only show images of the same aspect ratio as your picture frame rotation.(if you frame is vertical show vertical taken images and visa vera)
  • piframe image for download so no setup is required (except hardware)

Details on the new webapp features

To make it more handy to have a IoT picture frame I've added some handy configurations that load when the frame starts up. 



Details on the Acceleromter

I updated the git repository with a class to communicate via i2c to a MMA7455 accelerometer.
https://github.com/SimplyAutomationized/piframe/blob/master/MMA7455.py
The class has a callback method within it so it can broadcast a tilt change to whomever instantiates the class. Which I'm calling it within the websocket code and broadcasting to the client(the frame) the correct angle.

#I check the config file to see if you have enabled the tilt sensor
if(config['tiltsensor']):
 self.tiltSensor=tilt.TiltSensor(self.rotateFrame)
 self.tiltSensor.start()
#...later in the code we define the callback rotateFrame when rotation changes.
#(when the X axis inverts)
def rotateFrame(self,data):
 rotation=data*270
 self.frameconfig.changeKey('rotation',rotation)
 self.sendNewConfig(self,all=True)
#the code then broadcasts it's new rotation, the CSS on the frame then rotate's the images based off of the new rotation :-) win!




And rotation:

Saturday, April 4, 2015

Raspberry Pi Digital Picture Frame (Picture frame, websocket controlled, and more)

To see Part 2 of this project click here

Inside the Pi Frame

In my search (hours upon hours upon days..) through the many picture frame pi tutorials I didn't quite find one that was fine tuned for what I wanted.  I wanted to control it with my phone (upload, change , remove, and modify images).  I tried the following (Pros/Cons):
  •  FBI, Frame Buffer Imageviewer. 
    • Pro: easy to setup and had slideshow modes.
    • Cons:
      • No layering if you wanted to put a clock or temperature on the top.
      • Took lots of ram to buffer all the images up.
      • No web interface.
  • Pygame:
    • Pros:
      • Python based, and I like python, which could give you a web interface. 
      • Can do layering (add a clock layer.
      • Interface with web
    • Cons:
      • Slow
      • Not great for animations
  • HTML5 Web page Kiosk with Slideshow frontend and websocket server backend.
    • Pros:
      • Python websocket API for connecting Phone Apps and web apps
      • Quick animations
      • Layering upon layers upon layers
      • Easy programming
      • Connect it to other things (Weather, Home Automation,etc...)
    • Cons:
      • Takes lots of RAM.


So I took a bit of the FBI, and used some Raspberry Pi kiosk tutorials (can't think of which ones I used, just google some to find it) and mixed it into the mighty HTML5 one!!. Here's the parts list if this is your first RPicFrame:
  • LCD Screen with either Video or HDMI (Ebay/Amazon)
  • A Picture frame the you can attach your screen to. (May be difficult to find)
    • I luckily had a philips 10" digital picframe that I found at a garage sale for $15, gutted the lcd driver board and ordered one compatible with the lcd screen. (not always going to work) Also found the 7" digital picture frame at goodwill and gutted it and found a compatible driver (which barely fit with the Model A+).
    • Some people are skilled with woodworking as I've seen in other rpi pictureframe tutorials. You may be also.
  • Temperature Sensor (DS18B20)
    • I always add these to my pi, very handy, cheap, and gives me an idea of the temperature of that room.
  • Resistive Touch screen.
    • This may be included with your ebay purchase 
  • A Raspberry Pi, Model A, B, B+, A+, and Rpi 2 B.

Steps of creation:

sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install python-dev python-pip fbi mplayer matchbox libjpeg-dev
sudo pip install autobahn[twisted]
sudo pip install -I pillow
mkdir ~/.config


Now we are going to be creating the booting sequence:

Boot screen: follow tutorial here but replace the fbi cli to this appending mplayer underneath.

/usr/bin/fbi -T 30 -noverbose -a /etc/fbiImage.png & 
/usr/bin/mplayer /etc/pilogo.gif -speed .5 -loop 2 -fs -vo fbdev &

**This will add this spinning raspberry pi to your screen. (you'll need to save this gif to /etc/pilogo.gif)
The fbiImage.png is whatever you want to show after this gif finishes and the kiosk hasn't loaded. I just had a solid black image for mine. 



Controlling the frame with your mobile device



Ok... A couple more things and we're almost there. Create a file called go.sh and refernce it in your rc.local file:

go.sh:
#/bin/bash

# Disable DPMS / Screen blanking
 xset -dpms
 xset s off
 xset s noblank

while true; do
 sudo matchbox-window-manager -use_cursor no -use_titlebar no &
 sudo -u pi epiphany-browser -a --profile ~/.config http://localhost/index.html
 sleep 2s
done;
Append this before exit 0 in your rc.local:
sudo python /home/pi/piframe/piframestreamer.py &
sleep 5s
sudo -u pi xinit /home/pi/go.sh &

Here we are booting matchbox-window-manager as our desktop with epiphany our pictureframe webpage in an app kiosk mode with no title bar.

Now get the git clone of my webpage/websocket stuff and fill the pics folder with your images.
You'll need to make thumbnails to view the image selecting menu on the webpage:

git clone https://github.com/SimplyAutomationized/piframe.git
cd piframe 
python thumbMaker.py www/pics www/thumb

After that it should be ready to roll. I just make a piframe image for the sd card. Let me know if that is what some of you would like. Enjoy!!!
To see Part 2 of this project see here









The ones below are the 10" digital picture frame I can control my lights with. Along with having a raspberry pi camera in it. 









Monday, January 12, 2015

Stream The Raspberry Pi Camera over Websockets HTML5


I've been going through all the tutorials on how to get your pi camera to a webpage and hadn't found one that uses Websockets. So I tried it out and it worked quit well.

what you need:

  • Autobahn python websocket server
    • pip install autobahn[twisted]
  • picamera python library
    • http://www.raspberrypi.org/learning/python-picamera-setup/
  • My source at github:
    • source
    • You will have to copy the www directory to your '/usr/local/www' or change the source code to fit

The sample webpage on the github is a basic websocket client webpage that provides a couple of features.

Since my cam is hidden within a rpi picture frame (will blog soon on that project) there's a little black around the edges.  I circled the controls I wired into the websocket server. The buttons show a preview on the raspberry pi's screen. The slider changes the framerate (0-30 fps).

The stream is smooth but still delayed like most streams to the web.

I may make updates depending on interest but its not a priority on my list of pi projects.

Let me know in the comments :-)


Tuesday, September 24, 2013

Home Automation Project #3 Light Switch Cont. - WebUI-Source with WebSockets!

For the best and quickest response times with my webgui I decided to go with websockets. This was my first websocket experiment and it took many, many tutorials and websocket engines for me to finally come to this conclusion. I went with autobahn websockets because of its ease of use and ability to do reactor.callLater() which is the equivelent of a setTimout in javascript from what I understand. With that I used it to constantly check the outputs of my PiFace and send the socket message to the client webapp based off of its changes. So if anyone else in your house turns the lights on or off physically, you can instantly see it change on your phone. Now the only problem is older devices (like my wife's phone) are not all html5 websocket compatible. You may have to create a python cgi script to use as a fallback.

If you are kinda lost and need to recap on the lightswitch series here are the links and summaries:
Setting up the Piface communication listener and the button listener scripts HERE
Pics and description of the WebUI HERE

Setup:
Need to install Twisted, AutoBahnPython, and for the static pages I use lighttpd.

sudo apt-get install python-pip
sudo easy_install autobahn
sudo apt-get install twisted lighttpd


Websocket Server Code:

needs to run on boot after out PifaceListener script (source found here) has started.

Webpage Code:

I copied to my /var/www/ directory:

Sunday, September 22, 2013

Home Automation Project #3 - WebApp

I posted last week about the Raspberry Pi Light Switch (link here) and wanted to share the web pages I've developed to controlling these home automation devices. Starting with the Lighting:



As you see in the pics the status light changed on the Lamps button.  It happened when I pushed the Lamps button on the web app which then turned the Lamps on and indicated they were on the web app.  If I were to physically press the button the webapp would indicate the change within a second of the light coming on (thanks to html5 server push and AJAX).  I've made my router a vpn server so I can securely do this from anywhere I have an internet connection or cell service.  (And now the wife's asking me why I keep playing with the lights....I am writing this at work, oops. )

If you have questions or comments don't be afraid to ask.



 Here's a sneak peak of what's coming up in the series:



Saturday, September 1, 2012

Home Automation Project #1 (Sprinklers)

Home Automation Project #1 (Sprinklers)

I finally got out of the apartment business and decided as my first project for "actual" home automation would be to get my sprinkler controlled by the Koyo DL06.  I know I wanted to be able to manually turn them on and have them on a schedule at the same time. 

Part 1:
using jQuery Mobile, Asp.net forms w/ C#, and a PLC I get to turn on my sprinklers on manually.  With html5 server side event pushing live updates every couple seconds to my web app.  Now when some unexpected victim walks near my front yard. ZAP! Squirt!!

Part 2 (4hrs later): After coding all day (at work hehe) I made it so i can access and change the scheduling of the sprinkling system. Changing the PLC's schedule based on the ladder program i built. works like a charm!