Sunday, June 15, 2008

Python + YAMI = 3 Tier

Background

I have spent most of last night and this afternoon working out how to implement a website for my local LAN that would enable use of my DVD writer from a remote host over a web interface. I need to provide a small web application that can be used to burn ISO images onto CDs or DVDs. The application should also verify the CD or DVD once it has been burnt.

Security

To start with I needed to find out how to control the CD or DVD burner from the website. There is the small problem of security here. I could not simply add the Apache user access to /dev/sr0 ( the cd device ) because then it is conceivable that anyone or any rouge application might be able to use the Apache service to monkey with my device. I had to provide some kind of abstraction which could authenticate / authorise the request prior to performing it.

Python

Python is fast becoming my favourite scripting language for working in Linux. It has some very nice libraries that makes things like network programming very easy. It also has great SYS and OS libraries that are useful for working with the native operating system and environments.

YAMI ( Yet Another Messaging Infrastructure )

YAMI makes the nuts and bolts of client server communication very easy. Read up on it here. It can be compiled with support for c/c++, java, tcl and python. I only bothered with support for python. I had to ensure that the yamipyc.so module was located in the default python search path for my machine so mod_python could find it.


Apache

It is a reactively simple procedure to add a python handler to a website. Lookup mod_python. I will just say that you can configure mod_python in the Apache config to use a specific python file to handle all python requests. In my case I used the mod_python.publisher handler which is a built-in handler that is geared for reading post and get vars as well as publishing responses. I could have done all this in PHP, but seeing as though my plan was to use python for the application layer, I thought a connector to python was the easiest.

In the background the plan is to have a python server listening for connections on a specific port. The client will send it commands and it will respond appropriately. AS the service is executed under a user with permissions to the CD device and there is authentication and sanitisation going on in front of the device, we have extra security. I also plan to implement controls on the firewall to allow only one specific machine on my LAN to connect to it.

Flow

SO here is how it should all work:

  1. Website posts form to python handler ( handler.py )
  2. Apache mod_python knows how to manage this.
  3. handler.py Authenticates the request
  4. handler.py establishes a client connection to server.py
  5. handler.py sends commands based on the post it has received to server.py
  6. server.py sanitises the commands and executes an os.system call to the device. OR it rejects the commands.
  7. server.py responds with status messages and results.
  8. handler.py receives the results or status messages and reports back to the website.
To do: Think about asynchronous calls from the website so that the status of a burn can be presented incrementally.

Here are the scripts: ( source code highlighting found here.)

handler.py

#!/usr/bin/env python
from mod_python import apache
from YAMI import *
import os

def eject(req):
agent = Agent()
agent.domainRegister('cdburner', '127.0.0.1', 12340, 2)
agent.sendOneWay('cdburner', 'cd', 'eject', [''])
del agent

def shutdown(req):
agent = Agent()
agent.domainRegister('cdburner', '127.0.0.1', 12340, 2)
agent.sendOneWay('cdburner', 'cd', 'shutdown', [''])
del agent


Server.py

#!/usr/bin/env python
from YAMI import *
import os

agent = Agent(12340)
agent.objectRegister('cd')

print 'server started'

while 1:
im = agent.getIncoming('cd', 1)
src = im.getSourceAddr()
msgname = im.getMsgName()
if msgname == 'eject':
print 'Ejecting'
os.system("eject")
elif msgname == 'shutdown':
print 'Shutting down'
del im
break

del im

del agent


So, a request to http://localhost/cdburner/cdburner.py/eject will call the eject function ( this functionality is provided by mod_python.publisher ) and the cd tray is ejected. ( so long as the server.py script is running. )
A request to http://localhost/cdburner/cdburner.py/shutdown will stop the server.py service altogether.

I will also be looking at logging and all sorts of other things.

Conclusion

I have looked at a very simple web layer to application layer messaging system provided by mod_python and the mod_python.publisher handler, and YAMI compiled with support for python. The thing to note here is that the web server can make calls to the application server ( which, incidentally can be on a different physical server ) and the application server responds to the client which then reports back to the website. All this without changing any security permissions of the underlying operating system.

Where's the third tier?

Well that's the database. Python has excellent support for databases. This application will be no different. I intend to use the python connector so that access to the database is managed by the application and not the web server. Unfortunately I only have one machine so all three tiers will be on the same physical hardware. I accept this blatant security risk because a) I am cheap and b) this is a LAN application only. It will have no access from the world wide web. I control that little nugget with a real firewall in front of my LAN.
Post a Comment