mirror of
https://github.com/evennia/evennia.git
synced 2026-04-02 05:57:16 +02:00
Resolve merge conflicts
This commit is contained in:
commit
728b37e149
8 changed files with 211 additions and 161 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
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.
|
||||
|
|
|
|||
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.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(WebSocketClient, self).__init__(*args, **kwargs)
|
||||
self.protocol_key = "webclient/websocket"
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue