Added SSL (Secure Sockets Layer) support, inspired by patch by rcaskey (issue 79). The automatic certificate creation does not work well; so the system instead instructs and gives example on how to create your own using third-party tools. I can connect to the server using a twisted-client instance, but not that many regular mud clients seem to support SSL at all (and if they do I don't know to configure them ...)
This commit is contained in:
parent
7c56c69cea
commit
a8a70e9f5e
3 changed files with 103 additions and 12 deletions
|
|
@ -1,11 +1,11 @@
|
||||||
"""
|
"""
|
||||||
This module implements the main Evennia server process, the core of
|
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
|
the game engine. Don't import this module directly! If you need to
|
||||||
server processes from code, instead import sessionhandler.SESSIONS
|
access the server processes from code, instead go via the session-
|
||||||
and use its 'server' property.
|
handler: src.sessionhandler.SESSIONS.server
|
||||||
|
|
||||||
This module should be started with the 'twistd' executable since it
|
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).
|
by game/evennia.py).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
@ -39,10 +39,12 @@ SERVERNAME = settings.SERVERNAME
|
||||||
VERSION = get_evennia_version()
|
VERSION = get_evennia_version()
|
||||||
|
|
||||||
TELNET_PORTS = settings.TELNET_PORTS
|
TELNET_PORTS = settings.TELNET_PORTS
|
||||||
|
SSL_PORTS = settings.SSL_PORTS
|
||||||
SSH_PORTS = settings.SSH_PORTS
|
SSH_PORTS = settings.SSH_PORTS
|
||||||
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
||||||
|
|
||||||
TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_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
|
SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS
|
||||||
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS
|
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS
|
||||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||||
|
|
@ -101,7 +103,7 @@ class Evennia(object):
|
||||||
|
|
||||||
# set a callback if the server is killed abruptly,
|
# set a callback if the server is killed abruptly,
|
||||||
# by Ctrl-C, reboot etc.
|
# 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
|
self.game_running = True
|
||||||
|
|
||||||
|
|
@ -153,6 +155,8 @@ class Evennia(object):
|
||||||
print " telnet: " + ", ".join([str(port) for port in TELNET_PORTS])
|
print " telnet: " + ", ".join([str(port) for port in TELNET_PORTS])
|
||||||
if SSH_ENABLED:
|
if SSH_ENABLED:
|
||||||
print " ssh: " + ", ".join([str(port) for port in SSH_PORTS])
|
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:
|
if WEBSERVER_ENABLED:
|
||||||
clientstring = ""
|
clientstring = ""
|
||||||
if WEBCLIENT_ENABLED:
|
if WEBCLIENT_ENABLED:
|
||||||
|
|
@ -196,32 +200,46 @@ EVENNIA = Evennia(application)
|
||||||
|
|
||||||
if TELNET_ENABLED:
|
if TELNET_ENABLED:
|
||||||
|
|
||||||
# start telnet game connections
|
# Start telnet game connections
|
||||||
|
|
||||||
from src.server import telnet
|
from src.server import telnet
|
||||||
|
|
||||||
for port in TELNET_PORTS:
|
for port in TELNET_PORTS:
|
||||||
factory = protocol.ServerFactory()
|
factory = protocol.ServerFactory()
|
||||||
factory.protocol = telnet.TelnetProtocol
|
factory.protocol = telnet.TelnetProtocol
|
||||||
telnet_service = internet.TCPServer(port, factory)
|
telnet_service = internet.TCPServer(port, factory)
|
||||||
telnet_service.setName('EvenniaTelnet%s' % port)
|
telnet_service.setName('EvenniaTelnet%s' % port)
|
||||||
EVENNIA.services.addService(telnet_service)
|
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:
|
if SSH_ENABLED:
|
||||||
|
|
||||||
|
# Start SSH game connections. Will create a keypair in evennia/game if necessary.
|
||||||
|
|
||||||
from src.server import ssh
|
from src.server import ssh
|
||||||
|
|
||||||
for port in SSH_PORTS:
|
for port in SSH_PORTS:
|
||||||
factory = ssh.makeFactory({'protocolFactory':ssh.SshProtocol,
|
factory = ssh.makeFactory({'protocolFactory':ssh.SshProtocol,
|
||||||
'protocolArgs':()})
|
'protocolArgs':()})
|
||||||
|
|
||||||
ssh_service = internet.TCPServer(port, factory)
|
ssh_service = internet.TCPServer(port, factory)
|
||||||
ssh_service.setName('EvenniaSSH%s' % port)
|
ssh_service.setName('EvenniaSSH%s' % port)
|
||||||
EVENNIA.services.addService(ssh_service)
|
EVENNIA.services.addService(ssh_service)
|
||||||
|
|
||||||
if WEBSERVER_ENABLED:
|
if WEBSERVER_ENABLED:
|
||||||
|
|
||||||
# a django-compatible webserver.
|
# Start a django-compatible webserver.
|
||||||
|
|
||||||
from twisted.python import threadpool
|
from twisted.python import threadpool
|
||||||
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
from src.server.webserver import DjangoWebRoot, WSGIWebServer
|
||||||
|
|
|
||||||
69
src/server/ssl.py
Normal file
69
src/server/ssl.py
Normal file
|
|
@ -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)
|
||||||
|
|
@ -36,10 +36,14 @@ WEBSERVER_PORTS = [8000]
|
||||||
# Start the evennia ajax client on /webclient
|
# Start the evennia ajax client on /webclient
|
||||||
# (the webserver must also be running)
|
# (the webserver must also be running)
|
||||||
WEBCLIENT_ENABLED = True
|
WEBCLIENT_ENABLED = True
|
||||||
# Activate SSH protocol
|
# Activate SSH protocol (SecureShell)
|
||||||
SSH_ENABLED = False
|
SSH_ENABLED = False
|
||||||
# Ports to use for SSH
|
# Ports to use for SSH
|
||||||
SSH_PORTS = [8022]
|
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
|
# Activate full persistence if you want everything in-game to be
|
||||||
# stored to the database. With it set, you can do typeclass.attr=value
|
# 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'.
|
# and value will be saved to the database under the name 'attr'.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue