Most operating systems reserve ports below 1024 for system-wide servers. To enforce this reservation, only users with root permissions are allowed to run programs that bind to these ports. But running a server with root permissions tends to greatly magnify security issues. Security breaches happen when a malicious user tricks your application into doing something you didn't intend. This problem is never desirable, but the possibility for damage is especially high if your application has access to every file on the computer.
To limit the potential for damage, a well-behaved Unix server will run under a privileged account for just long enough to listen on the necessary ports, and then switch its identity to that of another user with minimal permissions. twistd makes it easy to use this technique for running your application.
11.2.1. How Do I Do That?
Pass uid and gid keyword arguments when you call twisted.application.service.Application. Once your application has started listening on the ports it needs, twistd will change your application's user and group to those you specified. Example 11-3 creates a server that runs on the reserved Telnet port, 23, and reads the contents of files from disk. By running it with a set user ID and group ID, it's limited to reading files that are available to its user and group.
Example 11-3. catserver.py
from twisted.application import service, internet from twisted.internet import protocol, reactor from twisted.protocols import basic import os class FileDumpProtocol(basic.LineReceiver): def lineReceived(self, line): if hasattr(self, 'handle_' + line): getattr(self, 'handle_' + line)( ) else: self.listDir(line) def listDir(self, filename): try: f = open(filename) for line in f: self.transport.write(line) except Exception, e: self.sendLine("ERROR: %s" % e) def handle_quit(self): self.transport.loseConnection( ) class FileDumpFactory(protocol.ServerFactory): protocol = FileDumpProtocol UID = 1000 GID = 1000 fileDumpService = internet.TCPServer(23, FileDumpFactory( )) application = service.Application('Cat Server', uid=UID, gid=GID) fileDumpService.setServiceParent(application)
Modify the UID and GID values in Example 11-3 to match a valid user and group on your system. Start catserver.py as root using twistd with the -n option to keep it in the foreground. It will start a server on port 23. (If you're already running a Telnet server on port 23, modify Example 11-3 to use a different port.) You'll see a log message informing you that twistd has switched to your user and group:
$ twistd -noy catserver.py 2005/06/25 17:32 EDT [-] Log opened. 2005/06/25 17:32 EDT [-] twistd 2.0.0 (/usr/bin/python2.3 2.3.5) starting up 2005/06/25 17:32 EDT [-] reactor class: twisted.internet.selectreactor.SelectReactor 2005/06/25 17:32 EDT [-] Loading catserver.py... 2005/06/25 17:32 EDT [-] Loaded. 2005/06/25 17:32 EDT [-] _ _builtin_ _.FileDumpFactory starting on 2323 2005/06/25 17:32 EDT [-] Starting factory <_ _builtin_ _.FileDumpFactory instance at 0xb7b2cd2c> 2005/06/25 17:32 EDT [-] set uid/gid 1000/1000
Telnet to localhost and enter some filenames. You will be able to access only files that your application's user has the right to read:
$ telnet localhost Trying 127.0.0.1... Connected to sparky. Escape character is '^]'. /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh ... sshd:x:110:65534::/var/run/sshd:/bin/false bind:x:112:117::/var/cache/bind:/bin/false /etc/shadow ERROR: [Errno 13] Permission denied: '/etc/shadow' quit Connection closed by foreign host.
11.2.2. How Does That Work?
Example 11-3 calls twisted.application.service.Application with uid and gid keyword arguments. twistd uses these values to set the process owner of the application after it has opened the ports. Behind the scenes, this process works by using the privilegedStartService method of IService. If a service provides the method privilegedStartService, it will be called at startup before the application switches users. After it switches to the unprivileged user, the application calls the Service's regular startService method. In the twisted.application.internet.TCPServer class, the code for listening on a port is in the privilegedStartService method, so it's able to bind to the port as root before the process switches to an unprivileged user.
Typically, you wouldn't design a protocol like FileDumpProtocol in Example 11-3, which lets any anonymous user dump the contents of any file on your system. But it's possible that you'd accidentally create a security hole in your application that would let a malicious user trick it into reading arbitrary files. By running it as an unprivileged user, you prevent such a security hole from revealing sensitive files on your system, such as /etc/shadow, which contains user/password hashes.
11.2.3. What About...
...running the application in a chroot environment? An alternative way to limit the potential damage of a security flaw in your application is to run it in a chroot "jail," where the operating system makes it impossible for the process to see or access any files outside of one specific directory. twistd has built-in support for this: just run it with the flag chroot directory.
Building Simple Clients and Servers
Web Services and RPC
NNTP Clients and Servers
Services, Processes, and Logging