From 4aea82a1aef53c28110cc45c734b835efdc361b3 Mon Sep 17 00:00:00 2001 From: Simeon Simeonov Date: Tue, 14 Apr 2015 10:38:43 +0200 Subject: beinc_config_sample.json beinc_config_sample.json.readme and beinc_server_xmlrpc.py almost complete --- beinc_config_sample.json | 7 ++-- beinc_config_sample.json.readme | 26 +++++++++----- beinc_server_xmlrpc.py | 75 +++++++++++++++++++++++++++++------------ 3 files changed, 76 insertions(+), 32 deletions(-) diff --git a/beinc_config_sample.json b/beinc_config_sample.json index f9423a2..c61a033 100644 --- a/beinc_config_sample.json +++ b/beinc_config_sample.json @@ -1,9 +1,10 @@ { "server": { "general": { - "ssl_module": "pyopenssl", "ssl_private_key": "key.pem", "ssl_certificate": "cert.crt", + "ssl_acceptable_ciphers": "auto", + "ssl_method": "auto", "daemonize": 1 }, "instances": [ @@ -27,7 +28,7 @@ "targets": [ { "name": "weechat_main", - "target_url": "https://10.0.0.2:9898/push/secondtest", + "target_url": "https://10.0.0.2:9898", "target_password": "changeme", "target_cert_file": "", "target_timestamp_format": "%H:%M:%S", @@ -52,5 +53,5 @@ } ] }, - "config_version": 1 + "config_version": 2 } diff --git a/beinc_config_sample.json.readme b/beinc_config_sample.json.readme index 12a1b2d..3ac85a9 100644 --- a/beinc_config_sample.json.readme +++ b/beinc_config_sample.json.readme @@ -16,17 +16,27 @@ documentation to better illustrate the comments. # It is only used by beinc_server.py and ignored by all other # components - "general": { # Applies to the whole server (cherrypy) For more info: - # http://docs.cherrypy.org/en/latest/refman/_cpserver.html - - # The name of a registered SSL adaptation module - "ssl_module": "pyopenssl", + "general": { # Applies to the whole server # The filename of the private key to use with SSL. "ssl_private_key": "/home/user/keys/key.pem", # default: None # The filename of the SSL certificate to use - "ssl_certificate": "/home/user/certs/cert.crt" # default: None + "ssl_certificate": "/home/user/certs/cert.crt", # default: None + + # A list of allowed ciphers in the format of 'openssl ciphers' + # "auto" - use the Twisted / OpenSSL defaults + "ssl_acceptable_ciphers": "auto", # default: "auto" + + # Encryption method for the BEINC server + # Possible values: + # "auto" - Let Twisted decide (use the newest possible method) + # "SSLv3" - SSLv3 (weak) + # "TLSv1" - TLSv1.0 + # "TLSv1_1" - TLSv1.1 + # "TLSv1_2" - TLSv1.2 (most secure, but requires OpenSSL >= 1.0.1e) + "ssl_method": "auto", # default: "auto" + }, # A list of instances. Check README for general information about them! @@ -185,10 +195,10 @@ documentation to better illustrate the comments. ] }, # The version of this config file - # Default: 1 (you should really never leave it undefined) + # Default: 2 (you should really never leave it undefined) # Used by beinc_server and beinc_weechat to check whether # the config (.json) file is outdated. # Every future version of BEINC will be shipped with # beinc_config_sample.json and beinc_config_sample.json.readme - "config_version": 1 + "config_version": 2 } diff --git a/beinc_server_xmlrpc.py b/beinc_server_xmlrpc.py index a9d0fa5..39cf422 100755 --- a/beinc_server_xmlrpc.py +++ b/beinc_server_xmlrpc.py @@ -27,6 +27,8 @@ import sys import OpenSSL +from functools import wraps + from twisted.web import xmlrpc, server from twisted.internet import protocol, reactor, ssl from twisted.python.filepath import FilePath @@ -49,7 +51,7 @@ except ImportError as e: __author__ = 'Simeon Simeonov' -__version__ = '1.1' +__version__ = '2.0' __license__ = 'GPL3' @@ -57,6 +59,17 @@ BEINC_OSD_TYPE_NONE = 0 BEINC_OSD_TYPE_PYNOTIFY = 1 BEINC_OSD_TYPE_PYOSD = 2 +BEINC_SSL_METHODS = {'SSLv3': OpenSSL.SSL.SSLv3_METHOD, + 'TLSv1': OpenSSL.SSL.TLSv1_METHOD} +try: + errstr = "Warning: Current Twisted / OpenSSL version doesn't support TLSv1.1" + BEINC_SSL_METHODS.update({'TLSv1_1': OpenSSL.SSL.TLSv1_1_METHOD}) + errstr = "Warning: Current Twisted / OpenSSL version doesn't support TLSv1.2" + BEINC_SSL_METHODS.update({'TLSv1_2': OpenSSL.SSL.TLSv1_2_METHOD}) +except: + sys.stderr.write(errstr + '\n') + + class BEINCInstance(object): """ Represents a single server-instance @@ -201,6 +214,7 @@ def beinc_login_required(method): decorator for checking login credentials """ + @wraps(method) def wrapper(self, resource_name, password, *args, **kwargs): try: instance = self.instances[resource_name] @@ -268,11 +282,12 @@ class XMLRPCNotifyServer(xmlrpc.XMLRPC): e)) raise xmlrpc.Fault(500, 'Unable to send message') + @beinc_login_required def xmlrpc_pull(self, resource_name, password): """ Return sum of arguments. """ - instance = self.__instances[args[0]] + instance = self.__instances[resource_name] if not instance.queueable: raise xmlrpc.Fault( 405, @@ -323,30 +338,48 @@ def main(): with open(args.config_file, 'r') as fp: config_dict = json.load(fp) except Exception as e: - sys.stderr.write('Unable to parse {0}: {1}'.format(args.config_file, + sys.stderr.write('Unable to parse {0}: {1}\n'.format(args.config_file, e)) sys.exit(errno.EIO) - ssl_certificate = config_dict['server']['general']['ssl_certificate'] - ssl_private_key = config_dict['server']['general']['ssl_private_key'] - try: + try: + if config_dict.get(config_version) != 2: + sys.stderr.write( + 'Incompatible or missing config-file version for {0}\n'.format( + args.config_file)) + sys.exit(1) + ssl_certificate = config_dict['server']['general'].get( + 'ssl_certificate') + ssl_private_key = config_dict['server']['general'].get( + 'ssl_private_key') + ssl_method_str = config_dict['server']['general'].get( + 'ssl_method', 'auto') + ssl_acceptable_ciphers_str = config_dict['server']['general'].get( + 'ssl_acceptable_ciphers', 'auto') beinc_server = XMLRPCNotifyServer(config_dict) - cert_path = FilePath( - config_dict['server']['general']['ssl_certificate']) - key_path = FilePath( - config_dict['server']['general']['ssl_private_key']) - private_certificate = ssl.PrivateCertificate.loadPEM( - key_path.getContent() + cert_path.getContent()) - options = private_certificate.options() - options.method = OpenSSL.SSL.TLSv1_2_METHOD - # options.acceptableCiphers = - # ssl.AcceptableCiphers.fromOpenSSLCipherString('EXP-RC4-MD5') - reactor.listenSSL(args.port, - server.Site(beinc_server), - options, - interface=args.hostname) + if ssl_certificate and ssl_private_key: + # SSL connection + cert_path = FilePath(ssl_certificate) + key_path = FilePath(ssl_private_key) + private_certificate = ssl.PrivateCertificate.loadPEM( + key_path.getContent() + cert_path.getContent()) + options = private_certificate.options() + ssl_method = BEINC_SSL_METHODS.get(ssl_method_str) + if ssl_method: + options.method = ssl_method + if ssl_acceptable_ciphers_str.lower() != 'auto': + options.acceptableCiphers = ssl.AcceptableCiphers.fromOpenSSLCipherString( + ssl_acceptable_ciphers_str) + reactor.listenSSL(args.port, + server.Site(beinc_server), + options, + interface=args.hostname) + else: + reactor.listenTCP(args.port, + server.Site(beinc_server), + interface=args.hostname) reactor.run() except Exception as e: - sys.stderr.write("WebServer error: {0}".format(e)) + sys.stderr.write('WebServer error: {0}\n'.format(e)) sys.exit(1) sys.exit(0) -- cgit v1.3