@@ -677,11 +677,258 @@ password, it's the same one for each private key.
677677Configuration
678678+++++++++++++
679679
680- **ZzzzzZzz ** All the config options go here..
680+ The two main classes to use in secure XML-RPC communications are
681+ :ref: `springpython.remoting.xmlrpc.SSLServer <remoting-secure-xml-config-sslserver >`
682+ and
683+ :ref: `springpython.remoting.xmlrpc.SSLClient <remoting-secure-xml-config-sslclient >`
684+ both of which support a number of options discussed below.
685+ Keep in mind that those classes are thin wrappers around the base objects found
686+ in Python's standard library and as such they always accept all the default arguments
687+ of their super-classes along with those specific to Spring Python's secure XML-RPC
688+ implementation.
689+
690+ .. _remoting-secure-xml-config-sslserver :
691+
692+ SSLServer
693+ >>>>>>>>>
694+
695+ SSLServer is a subclass of Python's
696+ `SimpleXMLRPCServer.SimpleXMLRPCServer <http://docs.python.org/library/simplexmlrpcserver.html#module-SimpleXMLRPCServer >`_
697+ which accepts arguments related to SSL in addition to those inherited from
698+ the base class. You expose XML-RPC services by extending SSLServer in your
699+ own subclass which is required to override one method, *register_functions *.
700+ *register_functions * may in turn use *self.register_function * for exposing those
701+ methods that should be accessible via XML-RPC, see
702+ `Python's documentation <http://docs.python.org/library/simplexmlrpcserver.html#SimpleXMLRPCServer.SimpleXMLRPCServer.register_function >`_
703+ for details of using *self.register_function *.
704+
705+ SSLServer.__init__'s default arguments::
706+
707+ class SSLServer(object, SimpleXMLRPCServer):
708+ def __init__(self, host=None, port=None, keyfile=None, certfile=None,
709+ ca_certs=None, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_TLSv1,
710+ do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None, **kwargs):
711+
712+ * *host * - interface to listen on, e.g. "localhost",
713+ * *port * - port to listen on, e.g. 8000,
714+ * *keyfile * - path to a PEM-encoded private key of the server, e.g. "./server-key.pem",
715+ * *certfile * - path to a PEM-encoded certificate of the server, e.g. "./server-cert.pem",
716+ * *ca_certs * - path to a PEM-encoded list (possibly one element long) of certificates
717+ of Certificate Authorities signing the certificates of clients you deal with,
718+ e.g. "./ca-chain.pem",
719+ * *cert_reqs * - whether the client is required to authenticate itself with a certificate,
720+ see `Python's documentation <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_
721+ for supported values,
722+ * *ssl_version * - the SSL/TLS version to use, see
723+ `Python's documentation <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_
724+ for supported values, note that the same value **must ** be used by the client
725+ application,
726+ * *do_handshake_on_connect * - `same as in Python <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_,
727+ * *suppress_ragged_eofs * - `same as in Python <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_,
728+ * *ciphers * - `same as in Python <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_,
729+ the value will be silently ignored if not running Python 2.7 or newer,
730+ * *\* *kwargs * - an open-ended list of keyword arguments, currently the only
731+ argument being recognized is *verify_fields * which must be a dictionary
732+ containing fields and values of the client certificate that must exist when the client's
733+ connecting. Fields names should be in the format given in `Appendix A of RFC 3280 <http://tools.ietf.org/html/rfc3280 >`_,
734+ which means using long names instead of short ones (commonName not CN, organizationName not O, etc.),
735+ for instance, setting verify_fields to:
736+
737+ ::
738+
739+ {"commonName":"My Client", "localityName":"My Town"}
740+
741+ will make sure the client certificate's subject has both commonName and localityName
742+ set and will also validate their respective values. The connection will not
743+ be accepted unless the fields and values match.
744+
745+ .. _remoting-secure-xml-config-sslserver-sample :
746+
747+ Sample SSL XML-RPC server which expects the client to use a certificate whose
748+ fields must match the configuration. The server exposes one method, *listdir *::
749+
750+ # -*- coding: utf-8 -*-
751+
752+ # stdlib
753+ import logging
754+ import os
755+ import ssl
756+
757+ # Spring Python
758+ from springpython.remoting.xmlrpc import SSLServer
759+
760+ class MySSLServer(SSLServer):
761+ def __init__(self, *args, **kwargs):
762+ super(MySSLServer, self).__init__(*args, **kwargs)
763+
764+ def _listdir(self, path):
765+ return os.listdir(path)
766+
767+ def register_functions(self):
768+ self.register_function(self._listdir, "listdir")
769+
770+ host = "localhost"
771+ port = 8000
772+ keyfile = "./server-key.pem"
773+ certfile = "./server-cert.pem"
774+ ca_certs = "./ca-chain.pem"
775+ verify_fields = {"commonName": "My Client", "organizationName":"My Company",
776+ "stateOrProvinceName":"My State"}
777+
778+ logging.basicConfig(level=logging.ERROR)
779+
780+ server = MySSLServer(host, port, keyfile, certfile, ca_certs, cert_reqs=ssl.CERT_REQUIRED,
781+ verify_fields=verify_fields)
782+ server.serve_forever()
783+
784+ .. _remoting-secure-xml-config-sslclient :
785+
786+ SSLClient
787+ >>>>>>>>>
788+
789+ SSLClient extends Python's built-in
790+ `xmlrpclib.ServerProxy <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_
791+ class and, unlike :ref: `SSLServer <remoting-secure-xml-config-sslserver >`,
792+ can be used directly without the need for subclassing. You can simply create
793+ an instance and start invoking server's methods.
794+
795+ SSLClient.__init__’s default arguments::
796+
797+ class SSLClient(ServerProxy):
798+ def __init__(self, uri=None, ca_certs=None, keyfile=None, certfile=None,
799+ cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1,
800+ transport=None, encoding=None, verbose=0, allow_none=0, use_datetime=0,
801+ timeout=socket._GLOBAL_DEFAULT_TIMEOUT, strict=None):
802+
803+ * *uri * - address of the XML-RPC server, e.g. "https://localhost:8000/RPC2",
804+ * *ca_certs * - path to a PEM-encoded list (possibly one element long) containing certificates
805+ of Certificate Authorities the client is to trust; client will be establishing
806+ authenticity of the server's certificate against certificates from that file;
807+ e.g. "./ca-chain.pem",
808+ * *keyfile * - path to a PAM-encoded private key of the client, e.g. "./client-key.pem",
809+ * *certfile * - path to a PAM-encoded certificate of the client, e.g. "./client-key.pem",
810+ * *cert_reqs * - whether a server is required to have a certificate,
811+ see `Python's documentation <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_
812+ for supported values,
813+ * *ssl_version * - the SSL/TLS version to use, see
814+ `Python's documentation <http://docs.python.org/library/ssl.html#ssl.wrap_socket >`_
815+ for supported values, note that the same value **must ** be used by the server,
816+ * *transport * - `same as in Python <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
817+ * *encoding * - `same as in Python <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
818+ * *verbose * - `same as in Python <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
819+ * *allow_none * - `same as in Python <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
820+ * *use_datetime * - `same as in Python <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
821+
822+ Sample SSL XML-RPC client which uses a private key and a certificate, can be
823+ used for invoking the :ref: `server <remoting-secure-xml-config-sslserver-sample >`
824+ shown in previous chapter::
825+
826+ # -*- coding: utf-8 -*-
827+
828+ # Spring Python
829+ from springpython.remoting.xmlrpc import SSLClient
830+
831+ server_location = "https://localhost:8000/RPC2"
832+ keyfile = "./client-key.pem"
833+ certfile = "./client-cert.pem"
834+ ca_certs = "./ca-chain.pem"
835+
836+ client = SSLClient(server_location, ca_certs, keyfile, certfile)
837+
838+ print client.listdir("/home")
681839
682840.. _remoting-secure-xml-rpc-logging :
683841
684842Logging
685843+++++++
686844
687- **ZzzzzZzz ** Describe loggers used..
845+ .. _remoting-secure-xml-logging-sslserver :
846+
847+ SSLServer
848+ >>>>>>>>>
849+
850+ Your subclass of SSLServer can be configured to use Python's
851+ standard `logging <http://docs.python.org/library/logging.html >`_ module.
852+ Currently, logging events are emitted at *DEBUG * and *ERROR * levels.
853+
854+ At ERROR level all failed attempts at validating of client certificates will
855+ be logged giving the exact reason for the failure. Interal errors (should they ever happen)
856+ are also logged at the ERROR level.
857+
858+ When told to run at DEBUG level, in addition to information logged at the ERROR level,
859+ the server will also log details of each client's certificate received along with
860+ the IP address of a client application connecting.
861+
862+ A sample SSL XML-RPC running with full verbosity turned on::
863+
864+ # -*- coding: utf-8 -*-
865+
866+ # stdlib
867+ import logging
868+ import os
869+ import ssl
870+
871+ # Spring Python
872+ from springpython.remoting.xmlrpc import SSLServer
873+
874+ class MySSLServer(SSLServer):
875+ def __init__(self, *args, **kwargs):
876+ super(MySSLServer, self).__init__(*args, **kwargs)
877+
878+ def _listdir(self, path):
879+ return os.listdir(path)
880+
881+ def register_functions(self):
882+ self.register_function(self._listdir, "listdir")
883+
884+ host = "localhost"
885+ port = 8000
886+ keyfile = "./server-key.pem"
887+ certfile = "./server-cert.pem"
888+ ca_certs = "./ca-chain.pem"
889+ verify_fields = {"commonName": "My Client", "organizationName":"My Company",
890+ "stateOrProvinceName":"My State"}
891+
892+ log_format = "%(asctime)s - %(levelname)s - %(process)d - %(threadName)s - %(name)s - %(message)s"
893+ formatter = logging.Formatter(log_format)
894+
895+ handler = logging.StreamHandler()
896+ handler.setFormatter(formatter)
897+
898+ logger = logging.getLogger("MySSLServer")
899+
900+ logger.setLevel(level=logging.DEBUG)
901+ logger.addHandler(handler)
902+
903+
904+ server = MySSLServer(host, port, keyfile, certfile, ca_certs, cert_reqs=ssl.CERT_REQUIRED,
905+ verify_fields=verify_fields)
906+ server.serve_forever()
907+
908+ .. _remoting-secure-xml-logging-sslclient :
909+
910+ SSLClient
911+ >>>>>>>>>
912+
913+ Although SSLClient does define a self.logger object it isn't currently used
914+ internally in any situation (subject to change without notice so you shouldn't
915+ rely on the current status). On the other hand, as a subclass of
916+ `xmlrpclib.ServerProxy <http://docs.python.org/library/xmlrpclib.html#xmlrpclib.ServerProxy >`_,
917+ the client may be configured to run in a *verbose * mode which means all HTTP traffic
918+ will be printed onto *standard output *.
919+
920+ A sample SSL XML-RPC client configured to use the verbose mode::
921+
922+ # -*- coding: utf-8 -*-
923+
924+ # Spring Python
925+ from springpython.remoting.xmlrpc import SSLClient
926+
927+ server_location = "https://localhost:8000/RPC2"
928+ keyfile = "./client-key.pem"
929+ certfile = "./client-cert.pem"
930+ ca_certs = "./ca-chain.pem"
931+
932+ client = SSLClient(server_location, ca_certs, keyfile, certfile, verbose=1)
933+
934+ print client.listdir("/home")
0 commit comments