diff --git a/src/server/server.py b/src/server/server.py index 4e94a59043..31bde0dab3 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -1,11 +1,11 @@ """ This module implements the main Evennia server process, the core of -the game engine. Don't import this module! If you need to access the -server processes from code, instead import sessionhandler.SESSIONS -and use its 'server' property. +the game engine. Don't import this module directly! If you need to +access the server processes from code, instead go via the session- +handler: src.sessionhandler.SESSIONS.server This module should be started with the 'twistd' executable since it -sets up all the networking features. (this is done by automatically +sets up all the networking features. (this is done automatically by game/evennia.py). """ @@ -39,10 +39,12 @@ SERVERNAME = settings.SERVERNAME VERSION = get_evennia_version() TELNET_PORTS = settings.TELNET_PORTS +SSL_PORTS = settings.SSL_PORTS SSH_PORTS = settings.SSH_PORTS WEBSERVER_PORTS = settings.WEBSERVER_PORTS TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS +SSL_ENABLED = settings.SSL_ENABLED and SSL_PORTS SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED @@ -101,7 +103,7 @@ class Evennia(object): # set a callback if the server is killed abruptly, # by Ctrl-C, reboot etc. - reactor.addSystemEventTrigger('before', 'shutdown',self.shutdown, _abrupt=True) + reactor.addSystemEventTrigger('before', 'shutdown', self.shutdown, _abrupt=True) self.game_running = True @@ -153,6 +155,8 @@ class Evennia(object): print " telnet: " + ", ".join([str(port) for port in TELNET_PORTS]) if SSH_ENABLED: print " ssh: " + ", ".join([str(port) for port in SSH_PORTS]) + if SSL_ENABLED: + print " ssl: " + ", ".join([str(port) for port in SSL_PORTS]) if WEBSERVER_ENABLED: clientstring = "" if WEBCLIENT_ENABLED: @@ -196,32 +200,46 @@ EVENNIA = Evennia(application) if TELNET_ENABLED: - # start telnet game connections + # Start telnet game connections from src.server import telnet - for port in TELNET_PORTS: + for port in TELNET_PORTS: factory = protocol.ServerFactory() factory.protocol = telnet.TelnetProtocol telnet_service = internet.TCPServer(port, factory) telnet_service.setName('EvenniaTelnet%s' % port) EVENNIA.services.addService(telnet_service) +if SSL_ENABLED: + + # Start SSL game connection (requires PyOpenSSL). + + from src.server import ssl + + for port in SSL_PORTS: + factory = protocol.ServerFactory() + factory.protocol = ssl.SSLProtocol + ssl_service = internet.SSLServer(port, factory, ssl.getSSLContext()) + ssl_service.setName('EvenniaSSL%s' % port) + EVENNIA.services.addService(ssl_service) + if SSH_ENABLED: + # Start SSH game connections. Will create a keypair in evennia/game if necessary. + from src.server import ssh for port in SSH_PORTS: factory = ssh.makeFactory({'protocolFactory':ssh.SshProtocol, - 'protocolArgs':()}) - + 'protocolArgs':()}) ssh_service = internet.TCPServer(port, factory) ssh_service.setName('EvenniaSSH%s' % port) EVENNIA.services.addService(ssh_service) if WEBSERVER_ENABLED: - # a django-compatible webserver. + # Start a django-compatible webserver. from twisted.python import threadpool from src.server.webserver import DjangoWebRoot, WSGIWebServer diff --git a/src/server/ssl.py b/src/server/ssl.py new file mode 100644 index 0000000000..205e49b0ad --- /dev/null +++ b/src/server/ssl.py @@ -0,0 +1,69 @@ +""" +This is a simple context factory for auto-creating +SSL keys and certificates. +""" + +import os, sys +from twisted.internet import ssl as twisted_ssl +try: + import OpenSSL +except ImportError: + print " SSL_ENABLED requires PyOpenSSL." + sys.exit() + +from src.server.telnet import TelnetProtocol + +class SSLProtocol(TelnetProtocol): + """ + Communication is the same as telnet, except data transfer + is done with encryption. + """ + pass + +def verify_SSL_key_and_cert(keyfile, certfile): + """ + This function looks for RSA key and certificate in the current + directory. If files ssl.key and ssl.cert does not exist, they + are created. + """ + + if not (os.path.exists(keyfile) and os.path.exists(certfile)): + # key/cert does not exist. Create. + import subprocess + from Crypto.PublicKey import RSA + from twisted.conch.ssh.keys import Key + + print " Creating SSL key and certificate (this need only be done once)." + + # create the RSA key and store it. + KEY_LENGTH = 1024 + rsaKey = Key(RSA.generate(KEY_LENGTH)) + keyString = rsaKey.toString(type="OPENSSH") + file(keyfile, 'w+b').write(keyString) + + # try to create the certificate + CERT_EXPIRE = 365 * 20 # twenty years validity + # default: + #openssl req -new -x509 -key ssl.key -out ssl.cert -days 7300 + exestring = "openssl req -new -x509 -key %s -out %s -days %s" % (keyfile, certfile, CERT_EXPIRE) + #print "exestring:", exestring + try: + err = subprocess.call(exestring)#, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except OSError, e: + print " %s\n" % e + print " Evennia's SSL context factory could not automatically create an SSL certificate game/%s." % certfile + print " A private key 'ssl.key' was already created. Please create %s manually using the commands valid " % certfile + print " for your operating system." + print " Example (linux, using the openssl program): " + print " %s" % exestring + sys.exit() + +def getSSLContext(): + """ + Returns an SSL context (key and certificate). This function + verifies that key/cert exists before obtaining the context, and if + not, creates them. + """ + keyfile, certfile = "ssl.key", "ssl.cert" + verify_SSL_key_and_cert(keyfile, certfile) + return twisted_ssl.DefaultOpenSSLContextFactory(keyfile, certfile) diff --git a/src/settings_default.py b/src/settings_default.py index f63278763b..a648c08959 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -36,10 +36,14 @@ WEBSERVER_PORTS = [8000] # Start the evennia ajax client on /webclient # (the webserver must also be running) WEBCLIENT_ENABLED = True -# Activate SSH protocol +# Activate SSH protocol (SecureShell) SSH_ENABLED = False -# Ports to use for SSH +# Ports to use for SSH SSH_PORTS = [8022] +# Actiave SSL protocol (SecureSocketLibrary) +SSL_ENABLED = False +# Ports to use for SSL +SSL_PORTS = [4001] # Activate full persistence if you want everything in-game to be # stored to the database. With it set, you can do typeclass.attr=value # and value will be saved to the database under the name 'attr'.