Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide changing source IP address bind to network interface. #223

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
license='MIT license',
packages=('winrm', 'winrm.tests'),
package_data={'winrm.tests': ['*.ps1']},
install_requires=['xmltodict', 'requests>=2.9.1', 'requests_ntlm>=0.3.0', 'six'],
install_requires=['xmltodict', 'requests>=2.9.1', 'requests_ntlm>=0.3.0', 'requests-toolbelt>=0.8.0', 'six'],
extras_require = dict(kerberos=['requests-kerberos>=0.10.0'], credssp=['requests-credssp>=0.0.1']),
classifiers=[
'Development Status :: 4 - Beta',
Expand Down
5 changes: 3 additions & 2 deletions winrm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ def __repr__(self):

class Session(object):
# TODO implement context manager methods
def __init__(self, target, auth, **kwargs):
def __init__(self, target, auth, bind_to=None, **kwargs):
username, password = auth
self.url = self._build_url(target, kwargs.get('transport', 'plaintext'))
self.protocol = Protocol(self.url,
username=username, password=password, **kwargs)
username=username, password=password,
bind_to=bind_to, **kwargs)

def run_cmd(self, command, args=()):
# TODO optimize perf. Do not call open/close shell every time
Expand Down
4 changes: 4 additions & 0 deletions winrm/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(
password=None, realm=None, service="HTTP", keytab=None,
ca_trust_path=None, cert_pem=None, cert_key_pem=None,
server_cert_validation='validate',
bind_to=None,
kerberos_delegation=False,
read_timeout_sec=DEFAULT_READ_TIMEOUT_SEC,
operation_timeout_sec=DEFAULT_OPERATION_TIMEOUT_SEC,
Expand All @@ -52,6 +53,7 @@ def __init__(
@param string cert_pem: client authentication certificate file path in PEM format # NOQA
@param string cert_key_pem: client authentication certificate key file path in PEM format # NOQA
@param string server_cert_validation: whether server certificate should be validated on Python versions that suppport it; one of 'validate' (default), 'ignore' #NOQA
@param string bind_to: use it on the local machine as the source address of the connection. Note that IP address must be bind to network interface # NOQA
@param bool kerberos_delegation: if True, TGT is sent to target server to allow multiple hops # NOQA
@param int read_timeout_sec: maximum seconds to wait before an HTTP connect/read times out (default 30). This value should be slightly higher than operation_timeout_sec, as the server can block *at least* that long. # NOQA
@param int operation_timeout_sec: maximum allowed time in seconds for any single wsman HTTP operation (default 20). Note that operation timeouts while receiving output (the only wsman operation that should take any significant time, and where these timeouts are expected) will be silently retried indefinitely. # NOQA
Expand All @@ -76,13 +78,15 @@ def __init__(
self.operation_timeout_sec = operation_timeout_sec
self.max_env_sz = Protocol.DEFAULT_MAX_ENV_SIZE
self.locale = Protocol.DEFAULT_LOCALE
self.bind_to = bind_to

self.transport = Transport(
endpoint=endpoint, username=username, password=password,
realm=realm, service=service, keytab=keytab,
ca_trust_path=ca_trust_path, cert_pem=cert_pem,
cert_key_pem=cert_key_pem, read_timeout_sec=self.read_timeout_sec,
server_cert_validation=server_cert_validation,
bind_to=self.bind_to,
kerberos_delegation=kerberos_delegation,
kerberos_hostname_override=kerberos_hostname_override,
auth_method=transport,
Expand Down
9 changes: 9 additions & 0 deletions winrm/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import requests.auth
import warnings
from distutils.util import strtobool
from requests_toolbelt.adapters.source import SourceAddressAdapter

HAVE_KERBEROS = False
try:
Expand Down Expand Up @@ -57,6 +58,7 @@ def __init__(
self, endpoint, username=None, password=None, realm=None,
service=None, keytab=None, ca_trust_path=None, cert_pem=None,
cert_key_pem=None, read_timeout_sec=None, server_cert_validation='validate',
bind_to=None,
kerberos_delegation=False,
kerberos_hostname_override=None,
auth_method='auto',
Expand All @@ -74,6 +76,7 @@ def __init__(
self.cert_key_pem = cert_key_pem
self.read_timeout_sec = read_timeout_sec
self.server_cert_validation = server_cert_validation
self.bind_to = bind_to
self.kerberos_hostname_override = kerberos_hostname_override
self.message_encryption = message_encryption
self.credssp_disable_tlsv1_2 = credssp_disable_tlsv1_2
Expand Down Expand Up @@ -147,6 +150,12 @@ def __init__(
def build_session(self):
session = requests.Session()

if self.bind_to:
# SourceAddressAdapter() allows to send requests from selected IP
# address.
session.mount('http://', SourceAddressAdapter(self.bind_to))
session.mount('https://', SourceAddressAdapter(self.bind_to))

# allow some settings to be merged from env
session.trust_env = True
settings = session.merge_environment_settings(url=self.endpoint, proxies={}, stream=None,
Expand Down