115 lines
3.3 KiB
Python
115 lines
3.3 KiB
Python
"""
|
|
This is a simple context factory for auto-creating
|
|
SSL keys and certificates.
|
|
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import sys
|
|
try:
|
|
import OpenSSL
|
|
from twisted.internet import ssl as twisted_ssl
|
|
except ImportError as error:
|
|
errstr = """
|
|
{err}
|
|
SSL requires the PyOpenSSL library and dependencies:
|
|
|
|
pip install pyopenssl pycrypto enum pyasn1 service_identity
|
|
|
|
Stop and start Evennia again. If no certificate can be generated, you'll
|
|
get a suggestion for a (linux) command to generate this locally.
|
|
|
|
"""
|
|
raise ImportError(errstr.format(err=error))
|
|
|
|
from django.conf import settings
|
|
from evennia.server.portal.telnet import TelnetProtocol
|
|
|
|
_GAME_DIR = settings.GAME_DIR
|
|
|
|
# messages
|
|
|
|
NO_AUTOGEN = """
|
|
|
|
{err}
|
|
Evennia could not auto-generate the SSL private key. If this error
|
|
persists, create {keyfile} yourself using third-party tools.
|
|
"""
|
|
|
|
NO_AUTOCERT = """
|
|
|
|
{err}
|
|
Evennia's SSL context factory could not automatically, create an SSL
|
|
certificate {certfile}.
|
|
|
|
A private key {keyfile} was already created. Please create {certfile}
|
|
manually using the commands valid for your operating system, for
|
|
example (linux, using the openssl program):
|
|
{exestring}
|
|
"""
|
|
|
|
|
|
class SSLProtocol(TelnetProtocol):
|
|
"""
|
|
Communication is the same as telnet, except data transfer
|
|
is done with encryption.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(SSLProtocol, self).__init__(*args, **kwargs)
|
|
self.protocol_name = "ssl"
|
|
|
|
|
|
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 ... ", end=' ')
|
|
|
|
try:
|
|
# 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)
|
|
except Exception as err:
|
|
print(NO_AUTOGEN.format(err=err, keyfile=keyfile))
|
|
sys.exit(5)
|
|
|
|
# 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)
|
|
try:
|
|
subprocess.call(exestring)
|
|
except OSError as err:
|
|
raise OSError(NO_AUTOCERT.format(err=err, certfile=certfile, keyfile=keyfile, exestring=exestring))
|
|
print("done.")
|
|
|
|
|
|
def getSSLContext():
|
|
"""
|
|
This is called by the portal when creating the SSL context
|
|
server-side.
|
|
|
|
Returns:
|
|
ssl_context (tuple): A key and certificate that is either
|
|
existing previously or or created on the fly.
|
|
|
|
"""
|
|
keyfile = os.path.join(_GAME_DIR, "server", "ssl.key")
|
|
certfile = os.path.join(_GAME_DIR, "server", "ssl.cert")
|
|
|
|
verify_SSL_key_and_cert(keyfile, certfile)
|
|
return twisted_ssl.DefaultOpenSSLContextFactory(keyfile, certfile)
|