Resolve merge conflicts
This commit is contained in:
commit
2d606e8cfb
8 changed files with 211 additions and 161 deletions
|
|
@ -237,7 +237,7 @@ if SSL_ENABLED:
|
||||||
|
|
||||||
# Start Telnet+SSL game connection (requires PyOpenSSL).
|
# Start Telnet+SSL game connection (requires PyOpenSSL).
|
||||||
|
|
||||||
from evennia.server.portal import ssl
|
from evennia.server.portal import telnet_ssl
|
||||||
|
|
||||||
for interface in SSL_INTERFACES:
|
for interface in SSL_INTERFACES:
|
||||||
ifacestr = ""
|
ifacestr = ""
|
||||||
|
|
@ -245,18 +245,24 @@ if SSL_ENABLED:
|
||||||
ifacestr = "-%s" % interface
|
ifacestr = "-%s" % interface
|
||||||
for port in SSL_PORTS:
|
for port in SSL_PORTS:
|
||||||
pstring = "%s:%s" % (ifacestr, port)
|
pstring = "%s:%s" % (ifacestr, port)
|
||||||
factory = ssl.SSLServerFactory()
|
factory = telnet_ssl.SSLServerFactory()
|
||||||
factory.noisy = False
|
factory.noisy = False
|
||||||
factory.sessionhandler = PORTAL_SESSIONS
|
factory.sessionhandler = PORTAL_SESSIONS
|
||||||
factory.protocol = ssl.SSLProtocol
|
factory.protocol = telnet_ssl.SSLProtocol
|
||||||
ssl_service = internet.SSLServer(port,
|
ssl_context = telnet_ssl.getSSLContext()
|
||||||
factory,
|
if ssl_context:
|
||||||
ssl.getSSLContext(),
|
ssl_service = internet.SSLServer(port,
|
||||||
interface=interface)
|
factory,
|
||||||
ssl_service.setName('EvenniaSSL%s' % pstring)
|
telnet_ssl.getSSLContext(),
|
||||||
PORTAL.services.addService(ssl_service)
|
interface=interface)
|
||||||
|
ssl_service.setName('EvenniaSSL%s' % pstring)
|
||||||
|
PORTAL.services.addService(ssl_service)
|
||||||
|
|
||||||
INFO_DICT["telnet_ssl"].append("telnet+ssl%s: %s" % (ifacestr, port))
|
INFO_DICT["telnet_ssl"].append("telnet+ssl%s: %s" % (ifacestr, port))
|
||||||
|
print(" ssl%s: %s" % (ifacestr, port))
|
||||||
|
else:
|
||||||
|
INFO_DICT["telnet_ssl"].append(
|
||||||
|
"telnet+ssl%s: %s (deactivated - keys/cert unset)" % (ifacestr, port))
|
||||||
|
|
||||||
|
|
||||||
if SSH_ENABLED:
|
if SSH_ENABLED:
|
||||||
|
|
|
||||||
|
|
@ -52,12 +52,25 @@ from evennia.utils.utils import to_str
|
||||||
_RE_N = re.compile(r"\|n$")
|
_RE_N = re.compile(r"\|n$")
|
||||||
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
|
||||||
_GAME_DIR = settings.GAME_DIR
|
_GAME_DIR = settings.GAME_DIR
|
||||||
|
_PRIVATE_KEY_FILE = os.path.join(_GAME_DIR, "server", "ssh-private.key")
|
||||||
|
_PUBLIC_KEY_FILE = os.path.join(_GAME_DIR, "server", "ssh-public.key")
|
||||||
|
_KEY_LENGTH = 2048
|
||||||
|
|
||||||
CTRL_C = '\x03'
|
CTRL_C = '\x03'
|
||||||
CTRL_D = '\x04'
|
CTRL_D = '\x04'
|
||||||
CTRL_BACKSLASH = '\x1c'
|
CTRL_BACKSLASH = '\x1c'
|
||||||
CTRL_L = '\x0c'
|
CTRL_L = '\x0c'
|
||||||
|
|
||||||
|
_NO_AUTOGEN = """
|
||||||
|
Evennia could not generate SSH private- and public keys ({{err}})
|
||||||
|
Using conch default keys instead.
|
||||||
|
|
||||||
|
If this error persists, create the keys manually (using the tools for your OS)
|
||||||
|
and put them here:
|
||||||
|
{}
|
||||||
|
{}
|
||||||
|
""".format(_PRIVATE_KEY_FILE, _PUBLIC_KEY_FILE)
|
||||||
|
|
||||||
|
|
||||||
# not used atm
|
# not used atm
|
||||||
class SSHServerFactory(protocol.ServerFactory):
|
class SSHServerFactory(protocol.ServerFactory):
|
||||||
|
|
@ -75,6 +88,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
here.
|
here.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
noisy = False
|
||||||
|
|
||||||
def __init__(self, starttuple):
|
def __init__(self, starttuple):
|
||||||
"""
|
"""
|
||||||
|
|
@ -85,6 +99,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
starttuple (tuple): A (account, factory) tuple.
|
starttuple (tuple): A (account, factory) tuple.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self.protocol_key = "ssh"
|
||||||
self.authenticated_account = starttuple[0]
|
self.authenticated_account = starttuple[0]
|
||||||
# obs must not be called self.factory, that gets overwritten!
|
# obs must not be called self.factory, that gets overwritten!
|
||||||
self.cfactory = starttuple[1]
|
self.cfactory = starttuple[1]
|
||||||
|
|
@ -113,7 +128,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
# since we might have authenticated already, we might set this here.
|
# since we might have authenticated already, we might set this here.
|
||||||
if self.authenticated_account:
|
if self.authenticated_account:
|
||||||
self.logged_in = True
|
self.logged_in = True
|
||||||
self.uid = self.authenticated_account.user.id
|
self.uid = self.authenticated_account.id
|
||||||
self.sessionhandler.connect(self)
|
self.sessionhandler.connect(self)
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
|
|
@ -237,7 +252,7 @@ class SshProtocol(Manhole, session.Session):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if reason:
|
if reason:
|
||||||
self.data_out(text=reason)
|
self.data_out(text=((reason, ), {}))
|
||||||
self.connectionLost(reason)
|
self.connectionLost(reason)
|
||||||
|
|
||||||
def data_out(self, **kwargs):
|
def data_out(self, **kwargs):
|
||||||
|
|
@ -311,6 +326,9 @@ class SshProtocol(Manhole, session.Session):
|
||||||
|
|
||||||
|
|
||||||
class ExtraInfoAuthServer(SSHUserAuthServer):
|
class ExtraInfoAuthServer(SSHUserAuthServer):
|
||||||
|
|
||||||
|
noisy = False
|
||||||
|
|
||||||
def auth_password(self, packet):
|
def auth_password(self, packet):
|
||||||
"""
|
"""
|
||||||
Password authentication.
|
Password authentication.
|
||||||
|
|
@ -336,6 +354,7 @@ class AccountDBPasswordChecker(object):
|
||||||
useful for the Realm.
|
useful for the Realm.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
noisy = False
|
||||||
credentialInterfaces = (credentials.IUsernamePassword,)
|
credentialInterfaces = (credentials.IUsernamePassword,)
|
||||||
|
|
||||||
def __init__(self, factory):
|
def __init__(self, factory):
|
||||||
|
|
@ -371,6 +390,8 @@ class PassAvatarIdTerminalRealm(TerminalRealm):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
noisy = False
|
||||||
|
|
||||||
def _getAvatar(self, avatarId):
|
def _getAvatar(self, avatarId):
|
||||||
comp = components.Componentized()
|
comp = components.Componentized()
|
||||||
user = self.userFactory(comp, avatarId)
|
user = self.userFactory(comp, avatarId)
|
||||||
|
|
@ -392,6 +413,8 @@ class TerminalSessionTransport_getPeer(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
noisy = False
|
||||||
|
|
||||||
def __init__(self, proto, chainedProtocol, avatar, width, height):
|
def __init__(self, proto, chainedProtocol, avatar, width, height):
|
||||||
self.proto = proto
|
self.proto = proto
|
||||||
self.avatar = avatar
|
self.avatar = avatar
|
||||||
|
|
@ -426,33 +449,32 @@ def getKeyPair(pubkeyfile, privkeyfile):
|
||||||
|
|
||||||
if not (os.path.exists(pubkeyfile) and os.path.exists(privkeyfile)):
|
if not (os.path.exists(pubkeyfile) and os.path.exists(privkeyfile)):
|
||||||
# No keypair exists. Generate a new RSA keypair
|
# No keypair exists. Generate a new RSA keypair
|
||||||
print(" Generating SSH RSA keypair ...", end=' ')
|
|
||||||
from Crypto.PublicKey import RSA
|
from Crypto.PublicKey import RSA
|
||||||
|
|
||||||
KEY_LENGTH = 1024
|
rsa_key = Key(RSA.generate(_KEY_LENGTH))
|
||||||
rsaKey = Key(RSA.generate(KEY_LENGTH))
|
public_key_string = rsa_key.public().toString(type="OPENSSH")
|
||||||
publicKeyString = rsaKey.public().toString(type="OPENSSH")
|
private_key_string = rsa_key.toString(type="OPENSSH")
|
||||||
privateKeyString = rsaKey.toString(type="OPENSSH")
|
|
||||||
|
|
||||||
# save keys for the future.
|
# save keys for the future.
|
||||||
file(pubkeyfile, 'w+b').write(publicKeyString)
|
with open(privkeyfile, 'wt') as pfile:
|
||||||
file(privkeyfile, 'w+b').write(privateKeyString)
|
pfile.write(private_key_string)
|
||||||
print(" done.")
|
print("Created SSH private key in '{}'".format(_PRIVATE_KEY_FILE))
|
||||||
|
with open(pubkeyfile, 'wt') as pfile:
|
||||||
|
pfile.write(public_key_string)
|
||||||
|
print("Created SSH public key in '{}'".format(_PUBLIC_KEY_FILE))
|
||||||
else:
|
else:
|
||||||
publicKeyString = file(pubkeyfile).read()
|
with open(pubkeyfile) as pfile:
|
||||||
privateKeyString = file(privkeyfile).read()
|
public_key_string = pfile.read()
|
||||||
|
with open(privkeyfile) as pfile:
|
||||||
|
private_key_string = pfile.read()
|
||||||
|
|
||||||
return Key.fromString(publicKeyString), Key.fromString(privateKeyString)
|
return Key.fromString(public_key_string), Key.fromString(private_key_string)
|
||||||
|
|
||||||
|
|
||||||
def makeFactory(configdict):
|
def makeFactory(configdict):
|
||||||
"""
|
"""
|
||||||
Creates the ssh server factory.
|
Creates the ssh server factory.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
pubkeyfile = os.path.join(_GAME_DIR, "server", "ssh-public.key")
|
|
||||||
privkeyfile = os.path.join(_GAME_DIR, "server", "ssh-private.key")
|
|
||||||
|
|
||||||
def chainProtocolFactory(username=None):
|
def chainProtocolFactory(username=None):
|
||||||
return insults.ServerProtocol(
|
return insults.ServerProtocol(
|
||||||
configdict['protocolFactory'],
|
configdict['protocolFactory'],
|
||||||
|
|
@ -467,14 +489,11 @@ def makeFactory(configdict):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# create/get RSA keypair
|
# create/get RSA keypair
|
||||||
publicKey, privateKey = getKeyPair(pubkeyfile, privkeyfile)
|
publicKey, privateKey = getKeyPair(_PUBLIC_KEY_FILE, _PRIVATE_KEY_FILE)
|
||||||
factory.publicKeys = {'ssh-rsa': publicKey}
|
factory.publicKeys = {'ssh-rsa': publicKey}
|
||||||
factory.privateKeys = {'ssh-rsa': privateKey}
|
factory.privateKeys = {'ssh-rsa': privateKey}
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print("getKeyPair error: {err}\n WARNING: Evennia could not "
|
print(_NO_AUTOGEN.format(err=err))
|
||||||
"auto-generate SSH keypair. Using conch default keys instead.\n"
|
|
||||||
"If this error persists, create {pub} and "
|
|
||||||
"{priv} yourself using third-party tools.".format(err=err, pub=pubkeyfile, priv=privkeyfile))
|
|
||||||
|
|
||||||
factory.services = factory.services.copy()
|
factory.services = factory.services.copy()
|
||||||
factory.services['ssh-userauth'] = ExtraInfoAuthServer
|
factory.services['ssh-userauth'] = ExtraInfoAuthServer
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
"""
|
|
||||||
This is a simple context factory for auto-creating
|
|
||||||
SSL keys and certificates.
|
|
||||||
|
|
||||||
"""
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from twisted.internet import protocol
|
|
||||||
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 SSLServerFactory(protocol.ServerFactory):
|
|
||||||
"This is only to name this better in logs"
|
|
||||||
noisy = False
|
|
||||||
|
|
||||||
def logPrefix(self):
|
|
||||||
return "SSL"
|
|
||||||
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
@ -43,8 +43,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.protocol_name = "telnet"
|
|
||||||
super(TelnetProtocol, self).__init__(*args, **kwargs)
|
super(TelnetProtocol, self).__init__(*args, **kwargs)
|
||||||
|
self.protocol_key = "telnet"
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -58,8 +58,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
# this number is counted down for every handshake that completes.
|
# this number is counted down for every handshake that completes.
|
||||||
# when it reaches 0 the portal/server syncs their data
|
# when it reaches 0 the portal/server syncs their data
|
||||||
self.handshakes = 8 # suppress-go-ahead, naws, ttype, mccp, mssp, msdp, gmcp, mxp
|
self.handshakes = 8 # suppress-go-ahead, naws, ttype, mccp, mssp, msdp, gmcp, mxp
|
||||||
self.init_session(self.protocol_name, client_address, self.factory.sessionhandler)
|
|
||||||
|
|
||||||
|
self.init_session(self.protocol_key, client_address, self.factory.sessionhandler)
|
||||||
self.protocol_flags["ENCODING"] = settings.ENCODINGS[0] if settings.ENCODINGS else 'utf-8'
|
self.protocol_flags["ENCODING"] = settings.ENCODINGS[0] if settings.ENCODINGS else 'utf-8'
|
||||||
# add this new connection to sessionhandler so
|
# add this new connection to sessionhandler so
|
||||||
# the Server becomes aware of it.
|
# the Server becomes aware of it.
|
||||||
|
|
|
||||||
146
evennia/server/portal/telnet_ssl.py
Normal file
146
evennia/server/portal/telnet_ssl.py
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
"""
|
||||||
|
This allows for running the telnet communication over an encrypted SSL tunnel. To use it, requires a
|
||||||
|
client supporting Telnet SSL.
|
||||||
|
|
||||||
|
The protocol will try to automatically create the private key and certificate on the server side
|
||||||
|
when starting and will warn if this was not possible. These will appear as files ssl.key and
|
||||||
|
ssl.cert in mygame/server/.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import os
|
||||||
|
try:
|
||||||
|
from OpenSSL import crypto
|
||||||
|
from twisted.internet import ssl as twisted_ssl
|
||||||
|
except ImportError as error:
|
||||||
|
errstr = """
|
||||||
|
{err}
|
||||||
|
Telnet-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
|
||||||
|
|
||||||
|
_PRIVATE_KEY_LENGTH = 2048
|
||||||
|
_PRIVATE_KEY_FILE = os.path.join(_GAME_DIR, "server", "ssl.key")
|
||||||
|
_PUBLIC_KEY_FILE = os.path.join(_GAME_DIR, "server", "ssl-public.key")
|
||||||
|
_CERTIFICATE_FILE = os.path.join(_GAME_DIR, "server", "ssl.cert")
|
||||||
|
_CERTIFICATE_EXPIRE = 365 * 24 * 60 * 60 * 20 # 20 years
|
||||||
|
_CERTIFICATE_ISSUER = {"C": "EV", "ST": "Evennia", "L": "Evennia", "O":
|
||||||
|
"Evennia Security", "OU": "Evennia Department", "CN": "evennia"}
|
||||||
|
|
||||||
|
# messages
|
||||||
|
|
||||||
|
NO_AUTOGEN = """
|
||||||
|
Evennia could not auto-generate the SSL private- and public keys ({{err}}).
|
||||||
|
If this error persists, create them manually (using the tools for your OS). The files
|
||||||
|
should be placed and named like this:
|
||||||
|
{}
|
||||||
|
{}
|
||||||
|
""".format(_PRIVATE_KEY_FILE, _PUBLIC_KEY_FILE)
|
||||||
|
|
||||||
|
NO_AUTOCERT = """
|
||||||
|
Evennia's could not auto-generate the SSL certificate ({{err}}).
|
||||||
|
The private key already exists here:
|
||||||
|
{}
|
||||||
|
If this error persists, create the certificate manually (using the private key and
|
||||||
|
the tools for your OS). The file should be placed and named like this:
|
||||||
|
{}
|
||||||
|
""".format(_PRIVATE_KEY_FILE, _CERTIFICATE_FILE)
|
||||||
|
|
||||||
|
|
||||||
|
class SSLProtocol(TelnetProtocol):
|
||||||
|
"""
|
||||||
|
Communication is the same as telnet, except data transfer
|
||||||
|
is done with encryption set up by the portal at start time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(SSLProtocol, self).__init__(*args, **kwargs)
|
||||||
|
self.protocol_key = "telnet/ssl"
|
||||||
|
|
||||||
|
|
||||||
|
def verify_or_create_SSL_key_and_cert(keyfile, certfile):
|
||||||
|
"""
|
||||||
|
Verify or create new key/certificate files.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
keyfile (str): Path to ssl.key file.
|
||||||
|
certfile (str): Parth to ssl.cert file.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
If files don't already exist, they are created.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not (os.path.exists(keyfile) and os.path.exists(certfile)):
|
||||||
|
# key/cert does not exist. Create.
|
||||||
|
try:
|
||||||
|
# generate the keypair
|
||||||
|
keypair = crypto.PKey()
|
||||||
|
keypair.generate_key(crypto.TYPE_RSA, _PRIVATE_KEY_LENGTH)
|
||||||
|
|
||||||
|
with open(_PRIVATE_KEY_FILE, 'wt') as pfile:
|
||||||
|
pfile.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, keypair))
|
||||||
|
print("Created SSL private key in '{}'.".format(_PRIVATE_KEY_FILE))
|
||||||
|
|
||||||
|
with open(_PUBLIC_KEY_FILE, 'wt') as pfile:
|
||||||
|
pfile.write(crypto.dump_publickey(crypto.FILETYPE_PEM, keypair))
|
||||||
|
print("Created SSL public key in '{}'.".format(_PUBLIC_KEY_FILE))
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
print(NO_AUTOGEN.format(err=err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
try:
|
||||||
|
# create certificate
|
||||||
|
cert = crypto.X509()
|
||||||
|
subj = cert.get_subject()
|
||||||
|
for key, value in _CERTIFICATE_ISSUER.items():
|
||||||
|
setattr(subj, key, value)
|
||||||
|
cert.set_issuer(subj)
|
||||||
|
|
||||||
|
cert.set_serial_number(1000)
|
||||||
|
cert.gmtime_adj_notBefore(0)
|
||||||
|
cert.gmtime_adj_notAfter(_CERTIFICATE_EXPIRE)
|
||||||
|
cert.set_pubkey(keypair)
|
||||||
|
cert.sign(keypair, 'sha1')
|
||||||
|
|
||||||
|
with open(_CERTIFICATE_FILE, 'wt') as cfile:
|
||||||
|
cfile.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||||
|
print("Created SSL certificate in '{}'.".format(_CERTIFICATE_FILE))
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
print(NO_AUTOCERT.format(err=err))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
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 created on the fly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if verify_or_create_SSL_key_and_cert(_PRIVATE_KEY_FILE, _CERTIFICATE_FILE):
|
||||||
|
return twisted_ssl.DefaultOpenSSLContextFactory(_PRIVATE_KEY_FILE, _CERTIFICATE_FILE)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
@ -31,6 +31,9 @@ class WebSocketClient(Protocol, Session):
|
||||||
"""
|
"""
|
||||||
Implements the server-side of the Websocket connection.
|
Implements the server-side of the Websocket connection.
|
||||||
"""
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(WebSocketClient, self).__init__(*args, **kwargs)
|
||||||
|
self.protocol_key = "webclient/websocket"
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ class AjaxWebClientSession(session.Session):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.protocol_name = "ajax/comet"
|
self.protocol_key = "webclient/ajax"
|
||||||
super(AjaxWebClientSession, self).__init__(*args, **kwargs)
|
super(AjaxWebClientSession, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_client_session(self):
|
def get_client_session(self):
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,8 @@ class Session(object):
|
||||||
a new session is established.
|
a new session is established.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
protocol_key (str): By default, one of 'telnet', 'ssh',
|
protocol_key (str): By default, one of 'telnet', 'telnet/ssl', 'ssh',
|
||||||
'ssl' or 'web'.
|
'webclient/websocket' or 'webclient/ajax'.
|
||||||
address (str): Client address.
|
address (str): Client address.
|
||||||
sessionhandler (SessionHandler): Reference to the
|
sessionhandler (SessionHandler): Reference to the
|
||||||
main sessionhandler instance.
|
main sessionhandler instance.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue