diff --git a/pyVim/connect.py b/pyVim/connect.py index f142ee521..37b67209d 100644 --- a/pyVim/connect.py +++ b/pyVim/connect.py @@ -34,6 +34,7 @@ import requests from requests.auth import HTTPBasicAuth +import ssl from pyVmomi import vim, vmodl, SoapStubAdapter, SessionOrientedStub from pyVmomi.VmomiSupport import nsMap, versionIdMap, versionMap, IsChildVersion @@ -179,7 +180,8 @@ def _doLogin(soapStub): def Connect(host='localhost', port=443, user='root', pwd='', service="hostd", adapter="SOAP", namespace=None, path="/sdk", - version=None, keyFile=None, certFile=None): + version=None, keyFile=None, certFile=None, + unverified=False): """ Connect to the specified server, login and return the service instance object. @@ -213,6 +215,8 @@ def Connect(host='localhost', port=443, user='root', pwd='', @type keyFile: string @param certFile: ssl cert file path @type certFile: string + @param unverified: allow unsigned certificates + @type unverified: bool """ try: info = re.match(_rx, host) @@ -231,7 +235,7 @@ def Connect(host='localhost', port=443, user='root', pwd='', elif not version: version="vim.version.version6" si, stub = __Login(host, port, user, pwd, service, adapter, version, path, - keyFile, certFile) + keyFile, certFile, unverified) SetSi(si) return si @@ -266,7 +270,7 @@ def GetLocalTicket(si, user): ## connected service instance object. def __Login(host, port, user, pwd, service, adapter, version, path, - keyFile, certFile): + keyFile, certFile, unverified=False): """ Private method that performs the actual Connect and returns a connected service instance object. @@ -299,7 +303,8 @@ def __Login(host, port, user, pwd, service, adapter, version, path, # Create the SOAP stub adapter stub = SoapStubAdapter(host, port, version=version, path=path, - certKeyFile=keyFile, certFile=certFile) + certKeyFile=keyFile, certFile=certFile, + unverified=unverified) # Get Service instance si = vim.ServiceInstance("ServiceInstance", stub) @@ -532,7 +537,7 @@ def __FindSupportedVersion(protocol, server, port, path, preferredApiVersions): def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd='', service="hostd", path="/sdk", - preferredApiVersions=None): + preferredApiVersions=None, unverified=False): """ Determine the most preferred API version supported by the specified server, then connect to the specified server using that API version, login and return @@ -565,6 +570,8 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd= specified, the list of versions support by pyVmomi will be used. @type preferredApiVersions: string or string list + @param unverified: set to true to allow for unsigned certs + @type unverified: bool """ if preferredApiVersions is None: @@ -587,7 +594,8 @@ def SmartConnect(protocol='https', host='localhost', port=443, user='root', pwd= service=service, adapter='SOAP', version=supportedVersion, - path=path) + path=path, + unverified=unverified) def OpenUrlWithBasicAuth(url, user='root', pwd=''): """ diff --git a/pyVmomi/SoapAdapter.py b/pyVmomi/SoapAdapter.py index 71fd2e5a5..94f1b5ef4 100644 --- a/pyVmomi/SoapAdapter.py +++ b/pyVmomi/SoapAdapter.py @@ -38,6 +38,9 @@ if PY3: from io import StringIO + +import warnings + from pyVmomi.VmomiSupport import * from pyVmomi.StubAdapterAccessorImpl import StubAdapterAccessorMixin import pyVmomi.Iso8601 @@ -1029,8 +1032,12 @@ def __init__(self, proxyPath): # SSLTunnelConnection def __call__(self, path, key_file=None, cert_file=None, **kwargs): # Don't pass any keyword args that HTTPConnection won't understand. + # see: https://docs.python.org/3/library/http.client.html for arg in kwargs.keys(): - if arg not in ("port", "strict", "timeout", "source_address"): + if arg not in ("port", "strict", "timeout", "source_address", + "context", "check_hostname"): + warnings.warn("http_client.HTTPConnection does not understand" + "the argument {0}".format(arg)) del kwargs[arg] tunnel = http_client.HTTPConnection(path, **kwargs) tunnel.request('CONNECT', self.proxyPath) @@ -1148,7 +1155,7 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk', thumbprint=None, cacertsFile=None, version=None, acceptCompressedResponses=True, connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC, - samlToken=None): + samlToken=None, unverified=False): if ns: assert(version is None) version = versionMap[ns] @@ -1184,6 +1191,9 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk', else: self.thumbprint = None + self.unverified = unverified + + # SSL connection actually occurs if sslProxyPath: self.scheme = SSLTunnelConnection(sslProxyPath) elif httpProxyHost: @@ -1213,6 +1223,21 @@ def __init__(self, host='localhost', port=443, ns=None, path='/sdk', self._acceptCompressedResponses = acceptCompressedResponses + def _set_unverified_https_context(self): + self._create_default_https_context = None + if hasattr(ssl, '_create_unverified_context'): + # hold the normal HTTPS context creation method aside + warnings.warn("unverified=True was set on vSphere connection") + self._create_default_https_context = ssl._create_default_https_context + ssl._create_default_https_context = ssl._create_unverified_context + + + def _restore_default_https_context(self): + if self._create_default_https_context is not None: + ssl._create_default_https_context = _create_default_https_context + self._create_default_https_context = None + + # Context modifier used to modify the SOAP request. # @param func The func that takes in the serialized message and modifies the # the request. The func is appended to the requestModifierList and then @@ -1237,6 +1262,9 @@ def requestModifier(self, func): # deserialized object so that it's easier to distinguish an API error from # a connection error. def InvokeMethod(self, mo, info, args, outerStub=None): + if self.unverified: + self._set_unverified_https_context() + if outerStub is None: outerStub = self @@ -1302,6 +1330,9 @@ def InvokeMethod(self, mo, info, args, outerStub=None): conn.close() raise http_client.HTTPException("{0} {1}".format(resp.status, resp.reason)) + if self.unverified: + self._restore_default_https_context() + ## Clean up connection pool to throw away idle timed-out connections # SoapStubAdapter lock must be acquired before this method is called. def _CloseIdleConnections(self):