[Twisted-Python] Tunneling using conch (with application.service)

I’ve got an app written that runs as a service using twisted.application.service that I need to tunnel through SSH. Right now, I use a script that makes use of Paramiko (and runs separate from my app) to set up a tunnel. That more or less works, but I’ve had some problems with the tunnel just going away and would like to integrate the tunneling into the app, using conch. I’ve found an example of tunneling via conch, at http://twistedmatrix.com/pipermail/twisted-python/2009-February/019196.html, that I think I can use as a base to add the code to my app. Right now my app is basically: class DataPuller(service.Service): ...Code for my app... ... The app pulls data from a database and I can only connect to the server via SSH... application = service.Application(‘Data_puller’) dpService = DataPuller() dpService.setServiceParent(application) My main problems are that I’m not sure whether or not the example linked to above is a good one for tunneling with conch and, if it is, how do I merge the example tunneling code with my app code. From the example, where the code is: class Connection(connection.SSHConnection): . . . def serviceStarted(self): Do I instantiate my DataPuller class there, in serviceStarted (and not subclass from service.Service)? If so, how do I wrap the tunneling code so that I can make it a service? If not, do I need put the tunneling code inside my DataPuller class? What would that look like? Any help will be greatly appreciated. Sean M Hollingsworth

On 6 Aug, 03:47 pm, smhollingsworth@gmail.com wrote:
The example you linked to sets up the traditional port forwarding behavior. A local port is opened and connections to it are tunneled over the SSH connection, where data is then delivered to some address accessible from the server on the other end of the SSH connection. This is fine and should work, and probably very closely mirrors what you're doing with Paramiko, so if you're happy with that, you should go for it. However, it's also possible for you to do this tunneling without opening the extra local port. Since your application and the SSH client code are all in the same process, you don't need the TCP connection to a local port to do this IPC to interact wiht the tunnel. You can set up the tunnel part of things, but instead of binding a local port to accept connections on, you can just open a connection over the tunnel and write bytes into it with API calls. I *don't* have an example of doing things this way, and I don't even know exactly what it involves. ;) However, the example you linked to gives a clue about where to start on this approach: when it binds the local port, it uses the forwarding.SSHListenForwardingFactory factory, so I'd start by looking at that class and see what it does. The rest is just mimicking its behavior without actually using reactor.listenTCP.
If you want things to happen when your program starts or when it is about to stop, then using service.Service is still a good idea. That's the easy way to hook into startup and shutdown events. However, you may not want to do anything other than set up (or tear down) the SSH connection in your service.Service subclass. Creating your new non-Service DataPuller in the Connection's serviceStarted (or maybe even a little later - after you actually set up the connection over the tunnel over the connection) then makes sense. Jean-Paul

On 6 Aug, 03:47 pm, smhollingsworth@gmail.com wrote:
The example you linked to sets up the traditional port forwarding behavior. A local port is opened and connections to it are tunneled over the SSH connection, where data is then delivered to some address accessible from the server on the other end of the SSH connection. This is fine and should work, and probably very closely mirrors what you're doing with Paramiko, so if you're happy with that, you should go for it. However, it's also possible for you to do this tunneling without opening the extra local port. Since your application and the SSH client code are all in the same process, you don't need the TCP connection to a local port to do this IPC to interact wiht the tunnel. You can set up the tunnel part of things, but instead of binding a local port to accept connections on, you can just open a connection over the tunnel and write bytes into it with API calls. I *don't* have an example of doing things this way, and I don't even know exactly what it involves. ;) However, the example you linked to gives a clue about where to start on this approach: when it binds the local port, it uses the forwarding.SSHListenForwardingFactory factory, so I'd start by looking at that class and see what it does. The rest is just mimicking its behavior without actually using reactor.listenTCP.
If you want things to happen when your program starts or when it is about to stop, then using service.Service is still a good idea. That's the easy way to hook into startup and shutdown events. However, you may not want to do anything other than set up (or tear down) the SSH connection in your service.Service subclass. Creating your new non-Service DataPuller in the Connection's serviceStarted (or maybe even a little later - after you actually set up the connection over the tunnel over the connection) then makes sense. Jean-Paul
participants (2)
-
exarkun@twistedmatrix.com
-
Sean Hollingsworth