-
Notifications
You must be signed in to change notification settings - Fork 24
Add NO_PROXY support for automatic proxy bypass based on target host #114
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,4 +1,5 @@ | ||||||||||||||||||||||||||||
| import re | ||||||||||||||||||||||||||||
| import os | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| def check_is_file(destination): | ||||||||||||||||||||||||||||
|
|
@@ -64,3 +65,56 @@ def conditionalReplace( aMatch ) : | |||||||||||||||||||||||||||
| result = '"' + result + '"' | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return result+' ' | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| def _redact_proxy_url(url): | ||||||||||||||||||||||||||||
| """Redact userinfo from proxy URL for safe logging.""" | ||||||||||||||||||||||||||||
| if not url: | ||||||||||||||||||||||||||||
| return url | ||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||
| from urllib.parse import urlparse, urlunparse | ||||||||||||||||||||||||||||
| except ImportError: | ||||||||||||||||||||||||||||
| from urlparse import urlparse, urlunparse | ||||||||||||||||||||||||||||
| parsed = urlparse(url) | ||||||||||||||||||||||||||||
| if parsed.username or parsed.password: | ||||||||||||||||||||||||||||
| netloc = parsed.hostname or '' | ||||||||||||||||||||||||||||
| if parsed.port: | ||||||||||||||||||||||||||||
| netloc += ':%s' % parsed.port | ||||||||||||||||||||||||||||
| return urlunparse(parsed._replace(netloc=netloc)) | ||||||||||||||||||||||||||||
| except Exception: | ||||||||||||||||||||||||||||
| return '[REDACTED]' | ||||||||||||||||||||||||||||
| return url | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| def configure_proxy(arguments, winrmproxy, winrmnoproxy, endpoint, log): | ||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||
| from requests.utils import should_bypass_proxies | ||||||||||||||||||||||||||||
| except (ImportError, AttributeError): | ||||||||||||||||||||||||||||
| log.warning("requests.utils.should_bypass_proxies not available; NO_PROXY requires requests >= 2.14.0") | ||||||||||||||||||||||||||||
| if winrmproxy: | ||||||||||||||||||||||||||||
| arguments["proxy"] = winrmproxy | ||||||||||||||||||||||||||||
| log.info("Connecting to %s via PROXY (%s)" % (endpoint, _redact_proxy_url(winrmproxy))) | ||||||||||||||||||||||||||||
| return arguments | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if winrmproxy: | ||||||||||||||||||||||||||||
| if winrmnoproxy: | ||||||||||||||||||||||||||||
| # Delegate to requests via env vars so NO_PROXY matching works | ||||||||||||||||||||||||||||
| os.environ['HTTP_PROXY'] = winrmproxy | ||||||||||||||||||||||||||||
| os.environ['HTTPS_PROXY'] = winrmproxy | ||||||||||||||||||||||||||||
| os.environ['NO_PROXY'] = winrmnoproxy | ||||||||||||||||||||||||||||
| log.debug("Proxy via env vars: HTTP(S)_PROXY set, NO_PROXY=%s" % winrmnoproxy) | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if should_bypass_proxies(endpoint, no_proxy=winrmnoproxy): | ||||||||||||||||||||||||||||
| log.info("Connecting to %s DIRECTLY (matched NO_PROXY: %s)" % (endpoint, winrmnoproxy)) | ||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||
|
Comment on lines
+108
to
+110
|
||||||||||||||||||||||||||||
| log.info("Connecting to %s via PROXY (%s)" % (endpoint, _redact_proxy_url(winrmproxy))) | ||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||
| # Legacy: explicit proxy for all connections | ||||||||||||||||||||||||||||
| arguments["proxy"] = winrmproxy | ||||||||||||||||||||||||||||
| log.info("Connecting to %s via PROXY (%s)" % (endpoint, _redact_proxy_url(winrmproxy))) | ||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||
| if winrmnoproxy: | ||||||||||||||||||||||||||||
| log.warning("noproxy is set but no proxy configured; noproxy ignored") | ||||||||||||||||||||||||||||
| log.info("Connecting to %s DIRECTLY (no proxy configured)" % endpoint) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
| log.info("Connecting to %s DIRECTLY (no proxy configured)" % endpoint) | |
| # No proxy configured via plugin; check for environment proxies that requests/pywinrm may still use | |
| http_proxy = os.environ.get('HTTP_PROXY') or os.environ.get('http_proxy') | |
| https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy') | |
| if http_proxy or https_proxy: | |
| log.info( | |
| "Connecting to %s via PROXY from environment (HTTP_PROXY=%s, HTTPS_PROXY=%s)" | |
| % (endpoint, http_proxy, https_proxy) | |
| ) | |
| else: | |
| log.info( | |
| "Connecting to %s DIRECTLY (no proxy configured in plugin or environment)" % endpoint | |
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -110,6 +110,15 @@ providers: | |
| renderingOptions: | ||
| groupName: Connection | ||
| instance-scope-node-attribute: "winrm-proxy" | ||
| - name: winrmnoproxy | ||
| title: No Proxy List | ||
| description: "Comma-separated list of hosts/IPs/CIDRs that bypass the proxy. Supports exact IPs, CIDR notation (192.168.1.0/24), domain suffixes (.internal.corp), and wildcard (*). Requires Proxy to also be set. It can be overwriting at node level using `winrm-noproxy`" | ||
| type: String | ||
|
Comment on lines
+113
to
+116
|
||
| required: false | ||
| scope: Instance | ||
| renderingOptions: | ||
| groupName: Connection | ||
| instance-scope-node-attribute: "winrm-noproxy" | ||
| - name: operationtimeout | ||
| title: operation timeout | ||
| description: "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. It can be overwriting at node level using `winrm-operationtimeout`" | ||
|
|
@@ -380,6 +389,15 @@ providers: | |
| renderingOptions: | ||
| groupName: Connection | ||
| instance-scope-node-attribute: "winrm-proxy" | ||
| - name: winrmnoproxy | ||
| title: No Proxy List | ||
| description: "Comma-separated list of hosts/IPs/CIDRs that bypass the proxy. Supports exact IPs, CIDR notation (192.168.1.0/24), domain suffixes (.internal.corp), and wildcard (*). Requires Proxy to also be set. It can be overwriting at node level using `winrm-noproxy`" | ||
| type: String | ||
|
Comment on lines
+392
to
+395
|
||
| required: false | ||
| scope: Instance | ||
| renderingOptions: | ||
| groupName: Connection | ||
| instance-scope-node-attribute: "winrm-noproxy" | ||
| - name: enabledhttpdebug | ||
| title: Enable HTTP logging in debug mode | ||
| description: "Print extra http logging in debug mode" | ||
|
|
@@ -417,7 +435,7 @@ providers: | |
| plugin-type: script | ||
| script-interpreter: ${config.interpreter} -u | ||
| script-file: winrm-check.py | ||
| script-args: --username ${config.username} --hostname ${config.hostname} --password ${config.password_storage_path} --authentication ${config.authtype} --transport ${config.winrmtransport} --port ${config.winrmport} --nossl ${config.nossl} --debug ${config.debug} --certpath ${config.certpath} | ||
| script-args: --username ${config.username} --hostname ${config.hostname} --password ${config.password_storage_path} --authentication ${config.authtype} --transport ${config.winrmtransport} --port ${config.winrmport} --nossl ${config.nossl} --debug ${config.debug} --certpath ${config.certpath} --proxy ${config.winrmproxy} --noproxy ${config.winrmnoproxy} | ||
| config: | ||
| - name: interpreter | ||
| title: Python Interpreter | ||
|
|
@@ -512,4 +530,14 @@ providers: | |
| default: "false" | ||
| required: false | ||
| renderingOptions: | ||
| groupName: Kerberos | ||
| groupName: Kerberos | ||
| - name: winrmproxy | ||
| title: Proxy | ||
| description: "Specify a proxy address for communicating with Windows nodes. Example HTTP proxy strings are http://server:port and http://user:pass@server:port. An example SOCKS5 proxy string is socks5://user:pass@server:port." | ||
| type: String | ||
| required: false | ||
| - name: winrmnoproxy | ||
| title: No Proxy List | ||
| description: "Comma-separated list of hosts/IPs/CIDRs that bypass the proxy. Supports exact IPs, CIDR notation (192.168.1.0/24), domain suffixes (.internal.corp), and wildcard (*)." | ||
| type: String | ||
| required: false | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to add a credential redaction helper here to ensure we don't leak proxy passwords into the Rundeck logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very valid and important secuiry concern. Thank you.