Support communication with vCenter Server via kerberos authenticated proxy sever
Is your feature request related to a problem? Please describe.
I need to communicate with vCenter Server and all the traffic goes through a kerberos authenticated proxy sever.
I am not able to authenticate the proxy server using 'Proxy-Authorization' header.
I am using below code:
import ssl
from pyVim.connect import SmartConnect, Disconnect
from requests_kerberos import HTTPKerberosAuth
def get_unverified_context():
"""
Get an unverified ssl context. Used to disable the server certificate
verification.
@return: unverified ssl context.
"""
context = None
if hasattr(ssl, '_create_unverified_context'):
context = ssl._create_unverified_context()
return context
context = get_unverified_context()
kerb_auth = HTTPKerberosAuth(force_preemptive=True)
auth_header = kerb_auth.generate_request_header(None, '10.24.129.100', True)
custom_headers = {}
custom_headers['Proxy-Authorization'] = auth_header
si = SmartConnect(protocol='https',
host='10.24.129.1',
user='[email protected]',
pwd='p@s$w0rD',
sslContext=context,
httpProxyHost='10.24.129.100',
httpProxyPort='3128',
customHeaders=custom_headers)
# Retrieve the service content
content = si.RetrieveContent()
vc_guid = content.about.instanceUuid
print(vc_guid)
I see below exception:
Traceback (most recent call last):
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVim/connect.py", line 491, in __Login
content = si.RetrieveContent()
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/VmomiSupport.py", line 598, in <lambda>
self.f(*(self.args + (obj,) + args), **kwargs)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/VmomiSupport.py", line 388, in _InvokeMethod
return self._stub.InvokeMethod(self, info, args)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/SoapAdapter.py", line 1533, in InvokeMethod
conn.request('POST', self.path, req, headers)
File "/usr/lib/python3.10/http/client.py", line 1282, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1328, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1277, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1037, in _send_output
self.send(msg)
File "/usr/lib/python3.10/http/client.py", line 975, in send
self.connect()
File "/usr/lib/python3.10/http/client.py", line 1447, in connect
super().connect()
File "/usr/lib/python3.10/http/client.py", line 951, in connect
self._tunnel()
File "/usr/lib/python3.10/http/client.py", line 924, in _tunnel
raise OSError(f"Tunnel connection failed: {code} {message.strip()}")
OSError: Tunnel connection failed: 407 Proxy Authentication Required
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/connect_to_vc_via_proxy.py", line 24, in <module>
si = SmartConnect(protocol='https',
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVim/connect.py", line 979, in SmartConnect
return Connect(host=host,
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVim/connect.py", line 318, in Connect
si, stub = __Login(host,
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVim/connect.py", line 503, in __Login
reraise(vim.fault.HostConnectFault, fault, traceback)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/six.py", line 718, in reraise
raise value.with_traceback(tb)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVim/connect.py", line 491, in __Login
content = si.RetrieveContent()
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/VmomiSupport.py", line 598, in <lambda>
self.f(*(self.args + (obj,) + args), **kwargs)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/VmomiSupport.py", line 388, in _InvokeMethod
return self._stub.InvokeMethod(self, info, args)
File "/etc/vcp/applianceRTVEnv/lib/python3.10/site-packages/pyVmomi/SoapAdapter.py", line 1533, in InvokeMethod
conn.request('POST', self.path, req, headers)
File "/usr/lib/python3.10/http/client.py", line 1282, in request
self._send_request(method, url, body, headers, encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1328, in _send_request
self.endheaders(body, encode_chunked=encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1277, in endheaders
self._send_output(message_body, encode_chunked=encode_chunked)
File "/usr/lib/python3.10/http/client.py", line 1037, in _send_output
self.send(msg)
File "/usr/lib/python3.10/http/client.py", line 975, in send
self.connect()
File "/usr/lib/python3.10/http/client.py", line 1447, in connect
super().connect()
File "/usr/lib/python3.10/http/client.py", line 951, in connect
self._tunnel()
File "/usr/lib/python3.10/http/client.py", line 924, in _tunnel
raise OSError(f"Tunnel connection failed: {code} {message.strip()}")
pyVmomi.VmomiSupport.vim.fault.HostConnectFault: (vim.fault.HostConnectFault) {
dynamicType = <unset>,
dynamicProperty = (vmodl.DynamicProperty) [],
msg = 'Tunnel connection failed: 407 Proxy Authentication Required',
faultCause = <unset>,
faultMessage = (vmodl.LocalizableMessage) []
}
I see below messages in the proxy logs:
1689748074.915 12 10.24.131.174 TCP_TUNNEL/200 3196 CONNECT 10.24.129.1:443 [email protected] HIER_DIRECT/10.24.129.1 - 1689748074.918 2 10.24.131.174 TCP_DENIED/407 5978 CONNECT 10.24.129.1:443 - HIER_NONE/- text/html 1689748274.797 16 10.24.131.174 TCP_TUNNEL/200 3167 CONNECT 10.24.129.1:443 [email protected] HIER_DIRECT/10.24.129.1 - 1689748274.801 2 10.24.131.174 TCP_DENIED/407 5982 CONNECT 10.24.129.1:443 - HIER_NONE/- text/html
pyVmomi connects to vCenter Server 2 times.
First time here: https://github.com/vmware/pyvmomi/blob/f0fe4e279cebdfdbca5bfce699063d15b1d3bd1d/pyVim/connect.py#L663
Second time here: https://github.com/vmware/pyvmomi/blob/f0fe4e279cebdfdbca5bfce699063d15b1d3bd1d/pyVmomi/SoapAdapter.py#L1533
It seems that first request is passing and second request is failing. I am not sure why that is happening.
Describe the solution you'd like
I would like to know what I am doing wrong and any WAR to solve it? Is is even possible to connect to vCenter server via kerberos authenticated proxy sever using pyVmomi? I am trying to do register/unregister a plugin on vCenter server.
Describe alternatives you've considered
No response
Additional context
No response
@DanielDraganov Can you please let me know if adding support for communication with vCenter Server via kerberos authenticated proxy sever is possible? We need to plan release for our product which needs this functionality.
I could debug this issue and found the root cause. pyVmomi connects to vCenter Server twice:
- In
__FindSupportedVersionmethod, that returns the most preferred API version supported by the specified server - In
Connectmethod, that login and return the service instance object.
For each of this connection to pass through the kerberos authenticated proxy server, a new Proxy-Authorization header is needed.
We will need to modify SmartConnect method to allow multiple Proxy-Authorization headers. I have created a new method [since we'll need 2 custom headers]
def SmartConnectKerberosProxy(protocol='https',
host='localhost',
port=443,
user='root',
pwd='',
service="hostd",
path="/sdk",
preferredApiVersions=None,
keyFile=None,
certFile=None,
httpProxyHost=None,
httpProxyPort=80,
thumbprint=None,
sslContext=None,
httpConnectionTimeout=None,
connectionPoolTimeout=CONNECTION_POOL_IDLE_TIMEOUT_SEC,
token=None,
tokenType=None,
disableSslCertValidation=False,
customHeaders=[],
# Deprecated
b64token=None,
# Deprecated
mechanism='userpass'):
"""
Determine the most preferred API version supported by the specified server,
then connect to the specified server using that API version, login and return
the service instance object.
Throws any exception back to caller. The service instance object is
also saved in the library for easy access.
Clients should modify the service parameter only when connecting to
a VMOMI server other than hostd/vpxd. For both of the latter, the
default value is fine.
@param protocol: What protocol to use for the connection (e.g. https or http).
@type protocol: string
@param host: Which host to connect to.
@type host: string
@param port: Port
@type port: int
@param user: User
@type user: string
@param pwd: Password
@type pwd: string
@param service: Service
@type service: string
@param path: Path
@type path: string
@param preferredApiVersions: Acceptable API version(s) (e.g. vim.version.version9)
If a list of versions is specified the versions should
be ordered from most to least preferred. If None is
specified, the list of versions support by pyVmomi will
be used.
@type preferredApiVersions: string or string list
@param keyFile: ssl key file path
@type keyFile: string
@param certFile: ssl cert file path
@type certFile: string
@param httpProxyHost The host name of the proxy server.
@type httpProxyHost: string
@param httpProxyPort The proxy server port.
@type httpProxyPort: string
@param thumbprint: host cert thumbprint
@type thumbprint: string
@param sslContext: SSL Context describing the various SSL options. It is only
supported in Python 2.7.9 or higher.
@type sslContext: SSL.Context
@param httpConnectionTimeout: Timeout in secs for http requests.
@type httpConnectionTimeout: int
@param connectionPoolTimeout: Timeout in secs for idle connections to close, specify
negative numbers for never closing the connections
@type connectionPoolTimeout: int
@type token: string
@param token: Authentication and Authorization token to use for the connection.
The presence of this token overrides the user and pwd parameters.
@type disableSslCertValidation: bool
@param disableSslCertValidation: Creates an unverified SSL context when True.
@type customHeaders: array
@param customHeaders: Array of dictionaries with custom HTTP headers.
@param b64token: base64 encoded token
*** Deprecated: Use token instead ***
@type b64token: string
@param mechanism: authentication mechanism: userpass or sspi
*** Deprecated: Use tokenType instead ***
@type mechanism: string
"""
if len(customHeaders) < 2:
raise Exception("At least 2 Proxy-authorization headers are needed"
" to connect via Kerberos authenticated proxy server.")
if preferredApiVersions is None:
preferredApiVersions = GetServiceVersions('vim25')
sslContext = getSslContext(host, sslContext, disableSslCertValidation)
supportedVersion = __FindSupportedVersion(protocol, host, port, path,
preferredApiVersions, sslContext,
httpProxyHost, httpProxyPort,
customHeaders[0])
if supportedVersion is None:
raise Exception("{0}:{1} is down or is not a VIM server"
.format(host, port))
portNumber = protocol == "http" and -int(port) or int(port)
return Connect(host=host,
port=portNumber,
user=user,
pwd=pwd,
service=service,
adapter='SOAP',
version=supportedVersion,
path=path,
keyFile=keyFile,
certFile=certFile,
httpProxyHost=httpProxyHost,
httpProxyPort=httpProxyPort,
thumbprint=thumbprint,
sslContext=sslContext,
httpConnectionTimeout=httpConnectionTimeout,
connectionPoolTimeout=connectionPoolTimeout,
token=token,
tokenType=tokenType,
disableSslCertValidation=disableSslCertValidation,
customHeaders=customHeaders[1],
b64token=b64token,
mechanism=mechanism)
Hello, With the latest major release of pyVmomi the connection logic was simplified and streamlined. However, this does not affect the Kerberos usage with the provided code sample. It's not clear if this specific use case requirement comes from Kerberos or from requests-kerberos but nevertheless this is not a pyVmomi related issue. Another workaround is to use Connect() instead of the SmartConnect() wrapper and provide a static version. It's enough for most of the use cases.