Sunday, 23 December 2012

Sending remote commands to our Pi thanks to Pastebin

I don't really know what this could be useful for, but I had the idea a couple of days ago of needing to send a command to my Pi system while being at work but have no direct access to it or nor the ip, or maybe the Pi is behind a firewall/NAT so I won't connect to it even with the IP.

So I thougth, what about a script that checks a page which I can acces from anywhere, downloads a file with the commands, executes them and sends me the result?

At first I thougth of doing this with my own server. Pretty easy, you upload to your server a file, let's call it commands.txt, and via cron the RPi checks to see if the file exists, downloads, executes, saves the output to the file and then reuploads it.

But there is a problem with this. I don't have a server. I had a VPS which expired a couple of weeks ago so I had to improvise.

Enter remote-code.py


Im gonna paste the Pastebin link and then go part by part with the script.

For this to work you need 2 extra packages. For easy access to Pastebin I used PastebinApi which is in Pypi and to parse the xml I used BeautifulSoup4, which is on Pypi and as a Debian package.

If you don't have pip installed thats the first thing we are gonna do, plus we will install BeautifulSoup4 at the same time.


 sudo apt-get install python-pip python-bs4  

Now, after the installation we are gonna install pastebin from pip:


 sudo pip install pastebin  

Now we are ready to see the python code.


 from pastebin import PastebinAPI  
 from bs4 import BeautifulSoup  
 import urllib2, os, logging, datetime  
    
 # logging config  
 logging.basicConfig(filename='remote-order.log',level=logging.DEBUG, format='%(levelname)s-%(asctime)s-%(message)s')  
 logging.info("Script started")  
    
 # variables to configure  
 developer_key = ""  
 user_key = ""  
 user_login = ""  
 user_password = ""  

As usual with everything on python, the imports go at the start.
In this case, Im gonna use the logging lib available from python, to log events to a file.

We set up the variables at the start. Now the developer key is easy to get in pastebin page but what is the user_key,login and password?

Well, to list the pastes and access them, we need a user key, and the only way of getting it is to create one using the user/password of the user we want to access. I still have no idea of when the user_key you get expires so there is the posibility of generating a new key each time the program is run, but I believe that would end up with your developer key banned if you keep creating user keys every 5 minutes.

Anyway, if you need a key just uncomment the couple of lines marked in the code, then run the program once so it prints your key.

While I was writing this I just modified the original code. If no user_key is added it will print one and exit so you can copy and paste into the code.

ok, so the whole program is wrapped in a try,except block in order to catch exceptions and errors and log them. I think its pretty straighforward:


 try:  
     x = PastebinAPI()  
    
     # create the user_key. Not sure if we need to create one every X hours or just one works forever.  
    
     if user_key == "":  
         user_key = x.generate_user_key(developer_key, user_login, user_password)  
         print "Your user key is: ", user_key  
         sys.exit(0)  
     else:  
         continue  
    
     # get the pastes  
     data = x.pastes_by_user(developer_key, user_key,10)  
    
     # parse the data to extract  
     soup = BeautifulSoup(data)  
     pastes = soup.find_all("paste_key")  
    
     # this is needed to get the first paste we made, as we are using it for the commands  
     pastes = pastes[len(pastes) - 1]  
    
     # download the raw pastebin  
     url = "http://pastebin.com/raw.php?i=" + pastes.text  
     cmd = urllib2.urlopen(url)  
     cmd = cmd.read()  
    
     # execute and store the output to a file  
     os.system(cmd + " > output.txt")  
    
     with open("output.txt","r") as f:  
         output = f.read()  
    
     # we paste the output to a new pastebin, private, that expires in 10 minutes  
     x.paste(developer_key, output, paste_name = "Result of command " + cmd, \  
     api_user_key = user_key, paste_private = "private", paste_format=None, paste_expire_date = "10M")  
    
     # log the finish  
     logging.info("Program finished ok")  
    
 except Exception as e:  
     logging.critical(e)  
 except TypeError as e:  
     logging.critical(e)  
 finally:  
     # remove the output.txt file  
     os.remove("output.txt")  


Ok, as the code is commented, I don't think it needs more explanation that a overview of what it does.

1 - It get all the pastes by the user 
2 - Discards all the pastes except the oldest one
3 - Downloads the raw paste
4 - Executes the command found and redirects the output to a temporal file
5 - Creates a new paste with teh title of the command executed, and the output in the body. The paste expires in 10 minutes

A couple of things to note. The script checks the oldest paste in the account ALWAYS, so make sure that you edit the oldest paste to make new commands instead of creating new ones. Also, the temporal file we use is useless. The command call should be executed with subprocess.check_output instead but I haven't had time to change it yet. Maybe in version 2 I will?

So here is an example:

Here is the pastebin I use for my Raspberry Pi:




As you can see, the first one is called command (name is not important, it's easier to name it something like that)

Now we enter it, as you can see there is a dot only, that is what I put in there when I don't want any command to be executed, the script just fails. I should improve this to not execute anything and log it.















Now we just edit it and add the command we want to execute, in this case a ls of the dir /home/pi














Now I only have to wait until the script is executed by cron, which is every ten minutes.

To add it to your cron, just execute crontab -e and add the following line (change the path and name of the script if necessary) to have it executed every ten minutes:

 */10 * * * * python /home/pi/scripts/remote-order.py  

Ok, now let's check if we got the new paste...













Yep, there it is. A private paste with the correct title that expires in 10 minutes.


Let's see what's inside...
























Well, what did we expect? Of course there is the command results in there!

Okay, now don't forget to go back and edit your "command" paste or else the command will be executed every 10 minutes! Put it back to a dot and call it a day.


Well, there it is. No idea if it could be useful for anybody or not. But at least it's there. For example, I used it today when I arrived to work and connected the Pi to the network, I had no idea which IP it was on. I have a script that launches every boot that sends an email with the internal and external IP but in this case it didn't launch (the dhcp server at work takes about 3 minutes to kick in and give you an ip) so I used this to re-launch the script and 10 minutes later I got the results. If I didn't have the script I could have send a "ifconfig -a" so I would have get the IP back in a pastebin.

I hope someone can find a good use for it! Enjoy!