[Twisted-Python] twistd and passphrase
Hi, I'm using twistd to run my server as a daemon but I couldn't find a way to prompt the user for a passphrase (such passphrase is used by the server to read its SSL key). I tried getpass() but it doesn't work because /dev/stdin is already redirected to /dev/null. How can I ask for a passphrase using twistd? Thank you
On Tuesday 05 September 2006 12:21, Lorenzo Allegrucci wrote:
Hi,
I'm using twistd to run my server as a daemon but I couldn't find a way to prompt the user for a passphrase (such passphrase is used by the server to read its SSL key). I tried getpass() but it doesn't work because /dev/stdin is already redirected to /dev/null. How can I ask for a passphrase using twistd? Thank you
Hi, Lorenzo. I'm going to assume you are completely new to Twisted. Apologies if this is not so; just skip ahead 2-4 paragraphs. Apologies also for my awkward writing. Abstractly, the way to do this is to write a Protocol that uses a Transport to prompt the user and receive their response, and wire it all together with a Factory. (Though in the case of stdio, the Factory is purely conceptual as explained below.) A Protocol is responsible for the reading and writing of a connection, without concern for the exact nature of that connection. A Transport is concerned with the nature of a connection without having to know anything about where the connection came from. And a Factory is responsible for the creation of connections (either by originating or accepting them), creating the Transport object that will represent and manage that connection, and attaching a Protocol to the Transport. Though I think that for your immediate purposes, you can ignore Factories, since when twisted starts up stdio is already "connected". Less abstractly, what you need to do is instantiate twisted.internet.stdio.StandardIO, which is a Transport, passing the constructor an instance of your password-prompting Protocol. In doing this, you are acting as the Factory by "accepting" the stdio connection, creating a Transport to deal with the connection, and associating the Transport with a Protocol. As I said at the beginning, you will need to write the Protocol class yourself. There is an example of a Protocol that uses the StandardIO Transport in twisted.test.process_twisted which you can use as a model. I kinda think there already exists a Protocol that does what you need (including turning off character echoing, etc) but I cannot recall where I might have encountered it. Perhaps someone else can help there. If you want your server to be able to start up unattended (for instance, whenever the system is rebooted) you might want to consider using telnet or SSH rather than stdin for reading the password. You should be able to re-use the same Protocol (except maybe for the part that turns off echoing). You are never going to be able to read a password from stdin if twisted is starting up in daemon mode. As you noticed, in daemon mode stdin has been closed before you have a chance to do anything with it. You would instead have to start it in "foreground" mode, then read the password with your Protocol, and once the password has been validated ask twistd to switch to daemon mode. (If indeed there even exists an interface for daemonizing after the fact; I've never looked.) And of course, if you launch twistd in foreground mode from a boot script, the boot process will block until someone comes along and types the password, which is usually a Bad Thing. Gosh, that was even an more awkward explanation than I'd expected. I hope you manage to get something out of it. Mike.
On Tue, 2006-09-05 at 13:18 -0400, Mike Pelletier wrote:
On Tuesday 05 September 2006 12:21, Lorenzo Allegrucci wrote:
Hi,
I'm using twistd to run my server as a daemon but I couldn't find a way to prompt the user for a passphrase (such passphrase is used by the server to read its SSL key). I tried getpass() but it doesn't work because /dev/stdin is already redirected to /dev/null. How can I ask for a passphrase using twistd? Thank you
Hi, Lorenzo. I'm going to assume you are completely new to Twisted.
Yes, I'm new to Twisted and I'm using it for a project of my degree thesis :) Your explanations have been very helpful and gave me some ideas, in the meantime I post my actual code. (non important parts taken away) ---myserver.py--- class SCF(ssl.ContextFactory): """Server context factory.""" def __init__(self, passphraseCB, cacert, cert, key): self.passphraseCB = passphraseCB self.cacert = cacert self.cert = cert self.key = key def verify(self, conn, cert, errnum, depth, ok): """Check the certificate of an incoming connection.""" # snip... return ok def getContext(self): """Return an SSL context.""" context = SSL.Context(SSL.TLSv1_METHOD) context.set_passwd_cb(self.passphraseCB) # snip... return context class MyService(internet.SSLServer): def __init__(self): root = XMLRPCServer() key = config.getOption("SSL", "key") cert = config.getOption("SSL", "cert") cacert = config.getOption("SSL", "cacert") port = config.getOption("daemon", "port") host = config.getOption("daemon", "host") context = SCF(self.getPassphraseCB, cacert, cert, key) internet.SSLServer.__init__(self, port, server.Site(root), context, interface=host) def getPassphraseCB(self, repeat=False, *data): return "secret" application = service.Application("MyApp") myService = MyService() myService.setServiceParent(application) ---myserver.py-- To start my daemon I use 'twistd -y myserver.py' and everything works fine except for the fact that I have to "hardwire" the passphrase in 'getPassphraseCB'. Of course this in not what I want and I would like to rewrite getPassphraseCB as: def getPassphraseCB(self, repeat=False, *data): return self.passphrase where self.passphrase should be set (somehow) _before_ twistd makes my application a daemon, but I couldn't find a way to do it yet.
You are never going to be able to read a password from stdin if twisted is starting up in daemon mode. As you noticed, in daemon mode stdin has been closed before you have a chance to do anything with it. You would instead have to start it in "foreground" mode, then read the password with your Protocol, and once the password has been validated ask twistd to switch to daemon mode.
Exactly.
(If indeed there even exists an interface for daemonizing after the fact; I've never looked.)
Looking at the twistd.py source I would say no..
On Thu, 07 Sep 2006 22:39:05 +0200, Lorenzo Allegrucci <l.allegrucci@gmail.com> wrote:
On Tue, 2006-09-05 at 13:18 -0400, Mike Pelletier wrote:
On Tuesday 05 September 2006 12:21, Lorenzo Allegrucci wrote:
Hi,
I'm using twistd to run my server as a daemon but I couldn't find a way to prompt the user for a passphrase (such passphrase is used by the server to read its SSL key). I tried getpass() but it doesn't work because /dev/stdin is already redirected to /dev/null. How can I ask for a passphrase using twistd? Thank you
Hi, Lorenzo. I'm going to assume you are completely new to Twisted.
Yes, I'm new to Twisted and I'm using it for a project of my degree thesis :) Your explanations have been very helpful and gave me some ideas, in the meantime I post my actual code. (non important parts taken away)
---myserver.py--- class SCF(ssl.ContextFactory): """Server context factory.""" def __init__(self, passphraseCB, cacert, cert, key): self.passphraseCB = passphraseCB self.cacert = cacert self.cert = cert self.key = key
def verify(self, conn, cert, errnum, depth, ok): """Check the certificate of an incoming connection.""" # snip... return ok
def getContext(self): """Return an SSL context.""" context = SSL.Context(SSL.TLSv1_METHOD) context.set_passwd_cb(self.passphraseCB) # snip... return context
class MyService(internet.SSLServer): def __init__(self): root = XMLRPCServer()
key = config.getOption("SSL", "key") cert = config.getOption("SSL", "cert") cacert = config.getOption("SSL", "cacert") port = config.getOption("daemon", "port") host = config.getOption("daemon", "host")
context = SCF(self.getPassphraseCB, cacert, cert, key) internet.SSLServer.__init__(self, port, server.Site(root), context, interface=host)
def getPassphraseCB(self, repeat=False, *data): return "secret"
def getPassphraseCB(self, repeat=False, *data): return passphrase import getpass passphrase = getpass.getpass()
application = service.Application("MyApp") myService = MyService() myService.setServiceParent(application) ---myserver.py--
To start my daemon I use 'twistd -y myserver.py' and everything works fine except for the fact that I have to "hardwire" the passphrase in 'getPassphraseCB'. Of course this in not what I want and I would like to rewrite getPassphraseCB as:
def getPassphraseCB(self, repeat=False, *data): return self.passphrase
where self.passphrase should be set (somehow) _before_ twistd makes my application a daemon, but I couldn't find a way to do it yet.
You are never going to be able to read a password from stdin if twisted is starting up in daemon mode. As you noticed, in daemon mode stdin has been closed before you have a chance to do anything with it. You would instead have to start it in "foreground" mode, then read the password with your Protocol, and once the password has been validated ask twistd to switch to daemon mode.
Exactly.
(If indeed there even exists an interface for daemonizing after the fact; I've never looked.)
Looking at the twistd.py source I would say no..
Jean-Paul
On Thu, 2006-09-07 at 17:40 -0400, Jean-Paul Calderone wrote:
class MyService(internet.SSLServer): def __init__(self): root = XMLRPCServer()
key = config.getOption("SSL", "key") cert = config.getOption("SSL", "cert") cacert = config.getOption("SSL", "cacert") port = config.getOption("daemon", "port") host = config.getOption("daemon", "host")
context = SCF(self.getPassphraseCB, cacert, cert, key) internet.SSLServer.__init__(self, port, server.Site(root), context, interface=host)
def getPassphraseCB(self, repeat=False, *data): return "secret"
def getPassphraseCB(self, repeat=False, *data): return passphrase
import getpass passphrase = getpass.getpass()
It works, thanks for your hint.
Lorenzo Allegrucci ha scritto:
[...]
To start my daemon I use 'twistd -y myserver.py' and everything works fine except for the fact that I have to "hardwire" the passphrase in 'getPassphraseCB'. Of course this in not what I want and I would like to rewrite getPassphraseCB as:
def getPassphraseCB(self, repeat=False, *data): return self.passphrase
where self.passphrase should be set (somehow) _before_ twistd makes my application a daemon, but I couldn't find a way to do it yet.
Just use twisted.python.util.getPassword(forceTTY=True). However (I ignore why) in this way no prompt is written on the screen. Note that getPassword does not return a deferred, so you should not call it when the reactor is running (not sure about this). Regards Manlio Perillo
participants (4)
-
Jean-Paul Calderone
-
Lorenzo Allegrucci
-
Manlio Perillo
-
Mike Pelletier