@@ -450,4 +450,197 @@ index to increment and roll over. If a service doesn't appear to be reachable,
450450it is deleted from the list and attempted again. A little more sophisticated
451451error handling should be added in case there are no services available. And
452452there needs to be a way to grow the services. But this gets us off to a good
453- start.
453+ start.
454+
455+ Secure XML-RPC
456+ --------------
457+
458+ .. highlight :: python
459+
460+ Spring Python extends Python’s built-in XML-RPC mechanims by adding the
461+ support for securing the communications path. You can choose whether to:
462+
463+ * simply encrypt the link,
464+ * have server require a client certificate signed off by a given CA or a chain of CAs,
465+ * validate the client certificate’s fields, for instance you can configure the server
466+ to only allow requests if a commonName is equal to an upon agreed value
467+
468+ Encrypted connection only
469+ +++++++++++++++++++++++++
470+
471+ .. image :: gfx/sslxmlrpc-01.png
472+ :align: center
473+
474+ The most basic setup which requires the server to have a private key and
475+ a certificate and the client to have a list (possibly consisting of one
476+ element only) of Certificate Authorities it is allowed to trust. Client will
477+ connect to server only if the server’s certificate has been signed off by given
478+ CAs. This is the most common way of performing SSL akin to what browsers do when
479+ connecting to secure online sites that don’t require a client certificate such
480+ as the majority of online banking sites.
481+
482+ In the code below the server exposes a Python’s built-in pow function over
483+ encrypted XML-RPC link and the client invokes it to get the result. Server
484+ uses its private key and a certificate which must have been signed off by
485+ one of CAs the client is aware of::
486+
487+ # -*- coding: utf-8 -*-
488+
489+ # Spring Python
490+ from springpython.remoting.xmlrpc import SSLXMLRPCServer
491+
492+ class MySSLServer(SSLXMLRPCServer):
493+ def __init__(self, *args, **kwargs):
494+ super(MySSLServer, self).__init__(*args, **kwargs)
495+
496+ def register_functions(self):
497+ self.register_function(pow)
498+
499+ host = "localhost"
500+ port = 8000
501+ key = "./server-key.pem"
502+ cert = "./server-cert.pem"
503+
504+ server = MySSLServer(host, port, key, cert)
505+ server.serve_forever()
506+
507+ ::
508+
509+ # -*- coding: utf-8 -*-
510+
511+ # Spring Python
512+ from springpython.remoting.xmlrpc import SSLXMLRPCClient
513+
514+ server_location = "https://localhost:8000/RPC2"
515+ ca_certs = "./cacert.pem"
516+
517+ client = SSLXMLRPCClient(server_location, ca_certs=ca_certs)
518+
519+ print client.pow(41, 3)
520+
521+ Server requires the client to have a certificate
522+ ++++++++++++++++++++++++++++++++++++++++++++++++
523+
524+ .. image :: gfx/sslxmlrpc-02.png
525+ :align: center
526+
527+ Same as above but this time the client must authenticate itself using its
528+ own certificate which must have been signed off by one of CAs known to the server.
529+ Server is still required to have a certificate whose signing CAs need to be
530+ known to the client::
531+
532+ # -*- coding: utf-8 -*-
533+
534+ # Spring Python
535+ from springpython.remoting.xmlrpc import SSLXMLRPCServer
536+
537+ # PyOpenSSL
538+ from OpenSSL import SSL
539+
540+ class MySSLServer(SSLXMLRPCServer):
541+ def __init__(self, *args, **kwargs):
542+ super(MySSLServer, self).__init__(*args, **kwargs)
543+
544+ def register_functions(self):
545+ self.register_function(pow)
546+
547+ host = "localhost"
548+ port = 8000
549+ key = "./server-key.pem"
550+ cert = "./server-cert.pem"
551+ ca_certs = "./cacert.pem"
552+
553+ server = MySSLServer(host, port, key, cert, ca_certs, verify_options=SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT)
554+ server.serve_forever()
555+
556+ ::
557+
558+ # -*- coding: utf-8 -*-
559+
560+ # Spring Python
561+ from springpython.remoting.xmlrpc import SSLXMLRPCClient
562+
563+ server_location = "https://localhost:8000/RPC2"
564+ key = "./client-key.pem"
565+ cert = "./client-cert.pem"
566+ ca_certs = "./cacert.pem"
567+
568+ client = SSLXMLRPCClient(server_location, key_file=key, cert_file=cert, ca_certs=ca_certs)
569+
570+ print client.pow(41, 3)
571+
572+ Server requires the client to have a certificate and checks its fields
573+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
574+
575+ .. image :: gfx/sslxmlrpc-03.png
576+ :align: center
577+
578+ Same as above (both sides need to have certificates signed off by trusted CAs)
579+ but this time the server inspects the client certificate’s fields and lets it
580+ in only they match the configuration it was fed with. In the example below
581+ *commonName * must be *Client *, *Organization * must be *The Sample Company * and the
582+ *State * must be *New York *. Server checks for both their existance and value and
583+ if there’s any mismatch the connection won’t be established in which case the
584+ error reason will be logged on the server side but no details of the error
585+ will be leaked to the client::
586+
587+ # -*- coding: utf-8 -*-
588+
589+ # Spring Python
590+ from springpython.remoting.xmlrpc import SSLXMLRPCServer
591+
592+ # PyOpenSSL
593+ from OpenSSL import SSL
594+
595+ class MySSLServer(SSLXMLRPCServer):
596+ def __init__(self, *args, **kwargs):
597+ super(MySSLServer, self).__init__(*args, **kwargs)
598+
599+ def register_functions(self):
600+ self.register_function(pow)
601+
602+ host = "localhost"
603+ port = 8000
604+ key = "./server-key.pem"
605+ cert = "./server-cert.pem"
606+ ca = "./chain.pem"
607+
608+ verify_fields = {"CN": "Client", "O":"The Sample Company", "ST":"New York"}
609+
610+ server = MySSLServer(host, port, key, cert, ca, verify_options=SSL.VERIFY_PEER|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
611+ verify_fields=verify_fields)
612+ server.serve_forever()
613+
614+ ::
615+
616+ # -*- coding: utf-8 -*-
617+
618+ # Spring Python
619+ from springpython.remoting.xmlrpc import SSLXMLRPCClient
620+
621+ server_location = "https://localhost:8000/RPC2"
622+ key = "./client-key.pem"
623+
624+ # Make sure the commonName is set to what the server requires.
625+ cert = "./client-cert.pem"
626+
627+ ca_certs = "./cacert.pem"
628+
629+ client = SSLXMLRPCClient(server_location, key_file=key, cert_file=cert, ca_certs=ca_certs)
630+
631+ print client.pow(41, 3)
632+
633+ More options
634+ ++++++++++++
635+
636+ **ZzzzzZzz ** All the config options go here..
637+
638+ Note that you can use both the client and the server with other XML-RPC
639+ implementations, there’s nothing preventing you from exposing secure XML-RPC to
640+ Java or .NET clients or from connecting with the secure client to XML-RPC servers
641+ implemented in other languages and technologies.
642+
643+ Logging
644+ +++++++
645+
646+ **ZzzzzZzz ** Describe loggers used..
0 commit comments