当Ansible连接到Windows时使用代理的情况
首先
这篇帖子是Ansible 3 Advent Calendar 2019第12天的文章。虽然题目是Ansible,但内容主要是关于pywinrm…。
如果使用Ansible操作Windows主机时处于代理环境下,有时可能会由于经过代理而导致错误。为了解决这个问题并避免通过代理,我确认了pywinrm模块的行为并进行介绍。
前提条件 (Paraphrased in Chinese)
我已经准备好参考这篇文章《准备在Ansible中操作Windows》,并在Ansible和Windows上完成了准备工作。
$ ansible --version
ansible 2.9.2
$ python3 --version
Python 3.6.8
[windows]
windows-server ansible_host=192.168.1.1
[windows:vars]
ansible_user=hogeuser
ansible_password=hogepass
ansible_port=5986
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
通过代理服务器引发的错误
$ ansible -i inventory -m win_ping windows-server
windows-server | UNREACHABLE! => {
"changed": false,
"msg": "ntlm: HTTPSConnectionPool(host='192.168.1.1', port=5986): Max retries exceeded with url: /wsman (Caused by ProxyError('Cannot connect to proxy.', timeout('timed out',)))",
"unreachable": true
}
1. 参考Ansible控制节点的环境变量no_proxy。
如果要连接到Windows,则使用ansible_connection=winrm,并使用名为pywinrm的模块进行http/https连接。
在默认情况下,pywinrm模块会参考Ansible控制节点的环境变量http_proxy,https_proxy和no_proxy。如果您想在代理环境中执行Ansible时直接连接到Windows而不需要通过代理,请将目标主机名或IP地址(网络地址)设置为环境变量no_proxy的值。
顺便提一下,虽然也会加载NO_PROXY,但由于no_proxy优先级更高,因此如果分别进行设置,可能会导致意料之外的行为。(这是winrm参考requests模块的规范)
http_proxy=[proxyserver]
https_proxy=[proxyserver]
no_proxy=192.168.0.0/16
NO_PROXY=192.168.0.0/16,172.16.0.0/12
# no_proxyが優先されてNO_PROXYの'172.16.0.0/12'は反映されないため、172.17.0.1などはプロキシ経由となってしまう
2. 修改pywinrm模块(不推荐使用)
如果pywinrm参考环境变量http_proxy和https_proxy,会通过代理进行访问。如果不希望这样,只需不加载环境变量即可。
在pywinrm的transport.py文件中,引用了requests模块,并且当session.trust_env = True时,会读取环境变量。根据requests模块的说明,trust_env表示”相信执行环境的环境变量吗?如果相信,就让我来使用它们!”。
因此,如果将transport.py的session.trust_env = False修改为不读取环境变量,就可以直接连接到Windows主机而不经过代理。
在我的环境中,/usr/local/lib/python3.6/site-packages/winrm/下存在transport.py文件。由于pywinrm版本的不同,transport.py的实现也有所差异,因此在Ansible中必须使用0.3.0版本和当前最新版本0.4.1。
版本0.3.0
def build_session(self):
session = requests.Session()
session.verify = self.server_cert_validation == 'validate'
if session.verify and self.ca_trust_path:
session.verify = self.ca_trust_path
# configure proxies from HTTP/HTTPS_PROXY envvars
#session.trust_env = True # 変更前
session.trust_env = False # 変更後
只需将0.3.0版本中的session.trust_env = True更改为False。
版本0.4.1。
def build_session(self):
session = requests.Session()
proxies = dict()
if self.proxy is None:
proxies['no_proxy'] = '*'
elif self.proxy != 'legacy_requests':
# If there was a proxy specified then use it
proxies['http'] = self.proxy
proxies['https'] = self.proxy
session.trust_env = False #この行を追加
# Merge proxy environment variables
settings = session.merge_environment_settings(url=self.endpoint,
proxies=proxies, stream=None, verify=None, cert=None)
从版本0.4.0开始,已经删除了session.trust_env = True的说明。由于session.trust_env的默认值为True,即使没有说明,Ansible控制节点的环境变量也会被引用。
如果你添加”session.trust_env = False”,结果将与0.3.0版本相同。
$ ansible -i inventory -m win_ping windows-server
windows-server | SUCCESS => {
"changed": false,
"ping": "pong"
}
将pywinrm变更为非推荐的原因。
只是个人观点,我认为不推荐使用pywinrm。
每次升级pywinrm版本都需要相应的适配。
如上所述,版本0.3.0和0.4.1中的transport.py实现已经有所改变。未来随着版本的升级,将需要特别对此进行处理。顺便提一句,
使用AWX公式容器映像变得更加不便。
如果您在AWX的部署中使用官方的awx_task容器,那么您需要修改容器内的transport.py文件。然而,在容器重新启动时,配置会被重置。
综上所述
我已经确认了Ansible连接Windows时pywinrm的行为。总而言之,只需要在环境变量no_proxy中设置目标主机即可。但是,如果因为某些原因无法设置no_proxy,则也可以通过修改pywinrm来实现。