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

Added win_http_proxy and win_inet_proxy #54631

Merged
merged 6 commits into from
Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
267 changes: 267 additions & 0 deletions lib/ansible/modules/windows/win_http_proxy.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
#!powershell

# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType

$spec = @{
options = @{
bypass = @{ type = "list" }
proxy = @{ type = "raw" }
source = @{ type = "str"; choices = @("ie") }
}
mutually_exclusive = @(
@("proxy", "source"),
@("bypass", "source")
)
required_by = @{
bypass = @("proxy")
}
supports_check_mode = $true
}

$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)

$proxy = $module.Params.proxy
$bypass = $module.Params.bypass
$source = $module.Params.source

# Parse the raw value, it should be a Dictionary or String
if ($proxy -is [System.Collections.IDictionary]) {
$valid_keys = [System.Collections.Generic.List`1[String]]@("http", "https", "ftp", "socks")
# Check to make sure we don't have any invalid keys in the dict
$invalid_keys = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $proxy.Keys) {
if ($k -notin $valid_keys) {
$invalid_keys.Add($k)
}
}

if ($invalid_keys.Count -gt 0) {
$invalid_keys = $invalid_keys | Sort-Object # So our test assertion doesn't fail due to random ordering
$module.FailJson("Invalid keys found in proxy: $($invalid_keys -join ', '). Valid keys are $($valid_keys -join ', ').")
}

# Build the proxy string in the form 'protocol=host;', the order of valid_keys is also important
$proxy_list = [System.Collections.Generic.List`1[String]]@()
foreach ($k in $valid_keys) {
if ($proxy.ContainsKey($k)) {
$proxy_list.Add("$k=$($proxy.$k)")
}
}
$proxy = $proxy_list -join ";"
} elseif ($null -ne $proxy) {
$proxy = $proxy.ToString()
}

if ($bypass) {
if ([System.String]::IsNullOrEmpty($proxy)) {
$module.FailJson("missing parameter(s) required by ''bypass'': proxy")
}
$bypass = $bypass -join ';'
}
jborean93 marked this conversation as resolved.
Show resolved Hide resolved

$win_http_invoke = @'
using System;
using System.Runtime.InteropServices;

namespace Ansible.WinHttpProxy
{
internal class NativeHelpers
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_CURRENT_USER_IE_PROXY_CONFIG : IDisposable
{
public bool fAutoDetect;
public IntPtr lpszAutoConfigUrl;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;

public void Dispose()
{
if (lpszAutoConfigUrl != IntPtr.Zero)
Marshal.FreeHGlobal(lpszAutoConfigUrl);
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_CURRENT_USER_IE_PROXY_CONFIG() { this.Dispose(); }
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class WINHTTP_PROXY_INFO : IDisposable
{
public UInt32 dwAccessType;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;

public void Dispose()
{
if (lpszProxy != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxy);
if (lpszProxyBypass != IntPtr.Zero)
Marshal.FreeHGlobal(lpszProxyBypass);
GC.SuppressFinalize(this);
}
~WINHTTP_PROXY_INFO() { this.Dispose(); }
}
}

internal class NativeMethods
{
[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetDefaultProxyConfiguration(
[Out] NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);

[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpGetIEProxyConfigForCurrentUser(
[Out] NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG pProxyConfig);

[DllImport("Winhttp.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool WinHttpSetDefaultProxyConfiguration(
NativeHelpers.WINHTTP_PROXY_INFO pProxyInfo);
}

public class Win32Exception : System.ComponentModel.Win32Exception
{
private string _msg;

public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { }
public Win32Exception(int errorCode, string message) : base(errorCode)
{
_msg = String.Format("{0} ({1}, Win32ErrorCode {2})", message, base.Message, errorCode);
}

public override string Message { get { return _msg; } }
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); }
}

public class WinINetProxy
{
public bool AutoDetect;
public string AutoConfigUrl;
public string Proxy;
public string ProxyBypass;
}

public class WinHttpProxy
{
public string Proxy;
public string ProxyBypass;

public WinHttpProxy()
{
Refresh();
}

public void Set()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (String.IsNullOrEmpty(Proxy))
proxyInfo.dwAccessType = 1; // WINHTTP_ACCESS_TYPE_NO_PROXY
else
{
proxyInfo.dwAccessType = 3; // WINHTTP_ACCESS_TYPE_NAMED_PROXY
proxyInfo.lpszProxy = Marshal.StringToHGlobalUni(Proxy);

if (!String.IsNullOrEmpty(ProxyBypass))
proxyInfo.lpszProxyBypass = Marshal.StringToHGlobalUni(ProxyBypass);
}

if (!NativeMethods.WinHttpSetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpSetDefaultProxyConfiguration() failed");
}
}

public void Refresh()
{
using (NativeHelpers.WINHTTP_PROXY_INFO proxyInfo = new NativeHelpers.WINHTTP_PROXY_INFO())
{
if (!NativeMethods.WinHttpGetDefaultProxyConfiguration(proxyInfo))
throw new Win32Exception("WinHttpGetDefaultProxyConfiguration() failed");

Proxy = Marshal.PtrToStringUni(proxyInfo.lpszProxy);
ProxyBypass = Marshal.PtrToStringUni(proxyInfo.lpszProxyBypass);
}
}

public static WinINetProxy GetIEProxyConfig()
{
using (NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxy = new NativeHelpers.WINHTTP_CURRENT_USER_IE_PROXY_CONFIG())
{
if (!NativeMethods.WinHttpGetIEProxyConfigForCurrentUser(ieProxy))
throw new Win32Exception("WinHttpGetIEProxyConfigForCurrentUser() failed");

return new WinINetProxy
{
AutoDetect = ieProxy.fAutoDetect,
AutoConfigUrl = Marshal.PtrToStringUni(ieProxy.lpszAutoConfigUrl),
Proxy = Marshal.PtrToStringUni(ieProxy.lpszProxy),
ProxyBypass = Marshal.PtrToStringUni(ieProxy.lpszProxyBypass),
};
}
}
}
}
'@
Add-CSharpType -References $win_http_invoke -AnsibleModule $module

$actual_proxy = New-Object -TypeName Ansible.WinHttpProxy.WinHttpProxy

$module.Diff.before = @{
proxy = $actual_proxy.Proxy
bypass = $actual_proxy.ProxyBypass
}

if ($source -eq "ie") {
jborean93 marked this conversation as resolved.
Show resolved Hide resolved
# If source=ie we need to get the server and bypass values from the IE configuration
$ie_proxy = [Ansible.WinHttpProxy.WinHttpProxy]::GetIEProxyConfig()
$proxy = $ie_proxy.Proxy
$bypass = $ie_proxy.ProxyBypass
}

$previous_proxy = $actual_proxy.Proxy
$previous_bypass = $actual_proxy.ProxyBypass

# Make sure an empty string is converted to $null for easier comparisons
if ([String]::IsNullOrEmpty($proxy)) {
$proxy = $null
}
if ([String]::IsNullOrEmpty($bypass)) {
$bypass = $null
}

if ($previous_proxy -ne $proxy -or $previous_bypass -ne $bypass) {
$actual_proxy.Proxy = $proxy
$actual_proxy.ProxyBypass = $bypass

if (-not $module.CheckMode) {
$actual_proxy.Set()

# Validate that the change was made correctly and revert if it wasn't. The Set() method won't fail on invalid
# values so we need to check again to make sure all was good.
$actual_proxy.Refresh()
if ($actual_proxy.Proxy -ne $proxy -or $actual_proxy.ProxyBypass -ne $bypass) {
$actual_proxy.Proxy = $previous_proxy
$actual_proxy.ProxyBypass = $previous_bypass
$actual_proxy.Set()

$module.FailJson("Unknown error when trying to set proxy '$proxy' or bypass '$bypass'")
}
}

$module.Result.changed = $true
}

$module.Diff.after = @{
proxy = $proxy
bypass = $bypass
}

$module.ExitJson()

103 changes: 103 additions & 0 deletions lib/ansible/modules/windows/win_http_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = r'''
---
module: win_http_proxy
version_added: '2.8'
short_description: Manages proxy settings for WinHTTP
description:
- Used to set, remove, or import proxy settings for Windows HTTP Services
C(WinHTTP).
- WinHTTP is a framework used by applications or services, typically .NET
applications or non-interactive services, to make web requests.
options:
bypass:
description:
- A list of hosts that will bypass the set proxy when being accessed.
- Use C(<local>) to match hostnames that are not fully qualified domain
names. This is useful when needing to connect to intranet sites using
just the hostname.
- Omit, set to null or an empty string/list to remove the bypass list.
- If this is set then I(proxy) must also be set.
type: list
proxy:
description:
- A string or dict that specifies the proxy to be set.
- If setting a string, should be in the form C(hostname), C(hostname:port),
or C(protocol=hostname:port).
jborean93 marked this conversation as resolved.
Show resolved Hide resolved
- If the port is undefined, the default port for the protocol in use is
used.
- If setting a dict, the keys should be the protocol and the values should
be the hostname and/or port for that protocol.
- Valid protocols are C(http), C(https), C(ftp), and C(socks).
- Omit, set to null or an empty string to remove the proxy settings.
source:
description:
- Instead of manually specifying the I(proxy) and/or I(bypass), set this to
import the proxy from a set source like Internet Explorer.
- Using C(ie) will import the Internet Explorer proxy settings for the
current active network connection of the current user.
- Only IE's proxy URL and bypass list will be imported into WinHTTP.
- This is like running C(netsh winhttp import proxy source=ie).
- The value is imported when the module runs and will not automatically
be updated if the IE configuration changes in the future. The module will
have to be run again to sync the latest changes.
choices:
- ie
type: str
notes:
- This is not the same as the proxy settings set in Internet Explorer, also
known as C(WinINet); use the M(win_inet_proxy) module to manage that instead.
- These settings are set system wide and not per user, it will require
Administrative privileges to run.
seealso:
- module: win_inet_proxy
author:
- Jordan Borean (@jborean93)
'''

EXAMPLES = r'''
- name: Set a proxy to use for all protocols
win_http_proxy:
proxy: hostname

- name: Set a proxy with a specific port with a bypass list
win_http_proxy:
proxy: hostname:8080
bypass:
- server1
- server2
- <local>

- name: Set the proxy based on the IE proxy settings
win_http_proxy:
source: ie

- name: Set a proxy for specific protocols
win_http_proxy:
proxy:
http: hostname:8080
https: hostname:8443

- name: Set a proxy for specific protocols using a string
win_http_proxy:
proxy: http=hostname:8080;https=hostname:8443
bypass: server1,server2,<local>

- name: Remove any proxy settings
win_http_proxy:
proxy: ''
bypass: ''
'''

RETURN = r'''
#
'''