Thursday, December 12, 2013

Python suds (SOAP Library) does not support SSLv3

The Oracle Virtual Machine (OVM) manager exposes a SOAP (wsdl) web-service. At the time of writing this can be found here: (for version 3.2.x)

https://<hostname>:7002/ovm/core/wsapi/soap?wsdl

You can access this url in your web browser and will be prompted to accept the self signed cert etc.  (There is a way to add a real certificate but that's not what this article is about.)

In order to consume this webservice in python, I thought to try out the (now quite old) "suds" library.  Opensuse still ship this library and you can install it with:
zypper in python-suds
EDIT: I have added the forked library to github here: https://github.com/linuxplayground/suds-sslv3-fork
The problem I found was that when trying to connect to the service, I was receiving an exception error around SSL.

Here is how it looked...
The example code:
#!/usr/bin/python
from suds.client import Client
client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
print client
Output:
david.latham@davidpc:~/bin/ovm-webservices> python broken.py 
Traceback (most recent call last):
  File "broken.py", line 3, in 
    client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
  File "/usr/lib/python2.7/site-packages/suds/client.py", line 112, in __init__
    self.wsdl = reader.open(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 152, in open
    d = self.fn(url, self.options)
  File "/usr/lib/python2.7/site-packages/suds/wsdl.py", line 136, in __init__
    d = reader.open(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 79, in open
    d = self.download(url)
  File "/usr/lib/python2.7/site-packages/suds/reader.py", line 95, in download
    fp = self.options.transport.open(Request(url))
  File "/usr/lib/python2.7/site-packages/suds/transport/https.py", line 60, in open
    return  HttpTransport.open(self, request)
  File "/usr/lib/python2.7/site-packages/suds/transport/http.py", line 62, in open
    return self.u2open(u2request)
  File "/usr/lib/python2.7/site-packages/suds/transport/http.py", line 118, in u2open
    return url.open(u2request, timeout=tm)
  File "/usr/lib64/python2.7/urllib2.py", line 404, in open
    response = self._open(req, data)
  File "/usr/lib64/python2.7/urllib2.py", line 422, in _open
    '_open', req)
  File "/usr/lib64/python2.7/urllib2.py", line 382, in _call_chain
    result = func(*args)
  File "/usr/lib64/python2.7/urllib2.py", line 1222, in https_open
    return self.do_open(httplib.HTTPSConnection, req)
  File "/usr/lib64/python2.7/urllib2.py", line 1184, in do_open
    raise URLError(err)
urllib2.URLError: 
That last line, "SSL23_GET_SERVER_HELLO:unknown protocol" had me stuck for ages. Eventually I found a blog post that suggested this can be fixed by adding a new HTTPHandler to the URLLIB2 Opener.
Rather than mess with the urllib2 library, I decided to download the source for the suds library, and fix the library.
Download suds from here:
svn co http://svn.fedorahosted.org/svn/suds/trunk
In the source tree add the following httpsconnectionv3.py to /path/to/downloaded/source/suds/transport/httpsconnectionv3.py
#!/usr/bin/python
import httplib, ssl, urllib2, socket

class HTTPSConnectionV3(httplib.HTTPSConnection):
    def __init__(self, *args, **kwargs):
        httplib.HTTPSConnection.__init__(self, *args, **kwargs)
        
    def connect(self):
        sock = socket.create_connection((self.host, self.port), self.timeout)
        if self._tunnel_host:
            self.sock = sock
            self._tunnel()
        try:
            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv3)
        except ssl.SSLError, e:
            print("Trying SSLv3.")
            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file, ssl_version=ssl.PROTOCOL_SSLv23)
            
class HTTPSHandlerV3(urllib2.HTTPSHandler):
    def https_open(self, req):
        return self.do_open(HTTPSConnectionV3, req)
Then update /path/to/downloaded/source/suds/transport/http.py with the following additions:
  • include the new library in the imports
    • from httpsconnectionv3 import *
  • Add the following line to the function "u2handlers" after the line "handlers.append(u2.ProxyHandler(self.proxy))"
    • handlers.append(HTTPSHandlerV3())
Now you are ready to test using this library instead of the default suds library that ships with your OS.
The example code but this time you place your example code in the downloaded source tree so that python uses the local library. OR You can make the above modifications to your installed library which should be somewhere in /usr/lib/python-2.7/site-packages/suds.
Up to you.
Example Code:
david.latham@davidpc:~/bin/ovm-webservices/ovm_wsapi> cat getVMList.py
#!/usr/bin/python
from suds.client import Client
client = Client(url='https://ovm:7002/ovm/core/wsapi/soap?wsdl')
username='admin'
password='xxxxxxxx'
loginResponse = client.service.login(username,password)
print "Logging in .... <%s>" % loginResponse
if loginResponse == True:
        vmlist = client.service.vmGetAll()
        for vm in vmlist:
                if vm.vmRunState == 'RUNNING' or vm.vmRunState == 'STOPPED':
                        print "%s | %s" % (vm.name, vm.vmRunState)
        logoutResponse = client.service.logout()
        print "Logging out ... <%s>" % logoutResponse
else:
        print "login failure"
Output:
david.latham@davidpc:~/bin/ovm-webservices/ovm_wsapi> ./getVMList.py 
Logging in .... 
puppetmaster | RUNNING
ksplice-test | RUNNING
zimbra-test-el6 | STOPPED
ksplice-client | RUNNING
DB2_Server_RHEL58 | STOPPED
ora133 | STOPPED
secret_host_1 | STOPPED
zimbra-test | RUNNING
sqlsandpit | RUNNING
secret_host_2 | STOPPED
repo_server | RUNNING
Logging out ... 
So that fixes suds for sslv3
Post a Comment