Resolve merge conflicts

This commit is contained in:
Griatch 2018-01-27 22:01:43 +01:00
commit 728b37e149
8 changed files with 211 additions and 161 deletions

View file

@ -237,7 +237,7 @@ if SSL_ENABLED:
# 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:
ifacestr = ""
@ -245,18 +245,24 @@ if SSL_ENABLED:
ifacestr = "-%s" % interface
for port in SSL_PORTS:
pstring = "%s:%s" % (ifacestr, port)
factory = ssl.SSLServerFactory()
factory = telnet_ssl.SSLServerFactory()
factory.noisy = False
factory.sessionhandler = PORTAL_SESSIONS
factory.protocol = ssl.SSLProtocol
ssl_service = internet.SSLServer(port,
factory,
ssl.getSSLContext(),
interface=interface)
ssl_service.setName('EvenniaSSL%s' % pstring)
PORTAL.services.addService(ssl_service)
factory.protocol = telnet_ssl.SSLProtocol
ssl_context = telnet_ssl.getSSLContext()
if ssl_context:
ssl_service = internet.SSLServer(port,
factory,
telnet_ssl.getSSLContext(),
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:

View file

@ -52,12 +52,25 @@ from evennia.utils.utils import to_str
_RE_N = re.compile(r"\|n$")
_RE_SCREENREADER_REGEX = re.compile(r"%s" % settings.SCREENREADER_REGEX_STRIP, re.DOTALL + re.MULTILINE)
_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_D = '\x04'
CTRL_BACKSLASH = '\x1c'
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
class SSHServerFactory(protocol.ServerFactory):
@ -75,6 +88,7 @@ class SshProtocol(Manhole, session.Session):
here.
"""
noisy = False
def __init__(self, starttuple):
"""
@ -85,6 +99,7 @@ class SshProtocol(Manhole, session.Session):
starttuple (tuple): A (account, factory) tuple.
"""
self.protocol_key = "ssh"
self.authenticated_account = starttuple[0]
# obs must not be called self.factory, that gets overwritten!
self.cfactory = starttuple[1]
@ -113,7 +128,7 @@ class SshProtocol(Manhole, session.Session):
# since we might have authenticated already, we might set this here.
if self.authenticated_account:
self.logged_in = True
self.uid = self.authenticated_account.user.id
self.uid = self.authenticated_account.id
self.sessionhandler.connect(self)
def connectionMade(self):
@ -237,7 +252,7 @@ class SshProtocol(Manhole, session.Session):
"""
if reason:
self.data_out(text=reason)
self.data_out(text=((reason, ), {}))
self.connectionLost(reason)
def data_out(self, **kwargs):
@ -311,6 +326,9 @@ class SshProtocol(Manhole, session.Session):
class ExtraInfoAuthServer(SSHUserAuthServer):
noisy = False
def auth_password(self, packet):
"""
Password authentication.
@ -336,6 +354,7 @@ class AccountDBPasswordChecker(object):
useful for the Realm.
"""
noisy = False
credentialInterfaces = (credentials.IUsernamePassword,)
def __init__(self, factory):
@ -371,6 +390,8 @@ class PassAvatarIdTerminalRealm(TerminalRealm):
"""
noisy = False
def _getAvatar(self, avatarId):
comp = components.Componentized()
user = self.userFactory(comp, avatarId)
@ -392,6 +413,8 @@ class TerminalSessionTransport_getPeer(object):
"""
noisy = False
def __init__(self, proto, chainedProtocol, avatar, width, height):
self.proto = proto
self.avatar = avatar
@ -426,33 +449,32 @@ def getKeyPair(pubkeyfile, privkeyfile):
if not (os.path.exists(pubkeyfile) and os.path.exists(privkeyfile)):
# No keypair exists. Generate a new RSA keypair
print(" Generating SSH RSA keypair ...", end=' ')
from Crypto.PublicKey import RSA
KEY_LENGTH = 1024
rsaKey = Key(RSA.generate(KEY_LENGTH))
publicKeyString = rsaKey.public().toString(type="OPENSSH")
privateKeyString = rsaKey.toString(type="OPENSSH")
rsa_key = Key(RSA.generate(_KEY_LENGTH))
public_key_string = rsa_key.public().toString(type="OPENSSH")
private_key_string = rsa_key.toString(type="OPENSSH")
# save keys for the future.
file(pubkeyfile, 'w+b').write(publicKeyString)
file(privkeyfile, 'w+b').write(privateKeyString)
print(" done.")
with open(privkeyfile, 'wt') as pfile:
pfile.write(private_key_string)
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:
publicKeyString = file(pubkeyfile).read()
privateKeyString = file(privkeyfile).read()
with open(pubkeyfile) as pfile:
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):
"""
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):
return insults.ServerProtocol(
configdict['protocolFactory'],
@ -467,14 +489,11 @@ def makeFactory(configdict):
try:
# create/get RSA keypair
publicKey, privateKey = getKeyPair(pubkeyfile, privkeyfile)
publicKey, privateKey = getKeyPair(_PUBLIC_KEY_FILE, _PRIVATE_KEY_FILE)
factory.publicKeys = {'ssh-rsa': publicKey}
factory.privateKeys = {'ssh-rsa': privateKey}
except Exception as err:
print("getKeyPair error: {err}\n WARNING: Evennia could not "
"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))
print(_NO_AUTOGEN.format(err=err))
factory.services = factory.services.copy()
factory.services['ssh-userauth'] = ExtraInfoAuthServer

View file

@ -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)

View file

@ -43,8 +43,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
"""
def __init__(self, *args, **kwargs):
self.protocol_name = "telnet"
super(TelnetProtocol, self).__init__(*args, **kwargs)
self.protocol_key = "telnet"
def connectionMade(self):
"""
@ -58,8 +58,8 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
# this number is counted down for every handshake that completes.
# when it reaches 0 the portal/server syncs their data
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'
# add this new connection to sessionhandler so
# the Server becomes aware of it.

View 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

View file

@ -31,6 +31,9 @@ class WebSocketClient(Protocol, Session):
"""
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):
"""

View file

@ -298,7 +298,7 @@ class AjaxWebClientSession(session.Session):
"""
def __init__(self, *args, **kwargs):
self.protocol_name = "ajax/comet"
self.protocol_key = "webclient/ajax"
super(AjaxWebClientSession, self).__init__(*args, **kwargs)
def get_client_session(self):

View file

@ -46,8 +46,8 @@ class Session(object):
a new session is established.
Args:
protocol_key (str): By default, one of 'telnet', 'ssh',
'ssl' or 'web'.
protocol_key (str): By default, one of 'telnet', 'telnet/ssl', 'ssh',
'webclient/websocket' or 'webclient/ajax'.
address (str): Client address.
sessionhandler (SessionHandler): Reference to the
main sessionhandler instance.