Group,
I think we need to start discussing what to do with the various WinRM connection options again.
Current state:
We allow Basic and Kerberos-authenticated connections to the ssl port only (5986 by default)
The hostvars controlling beavhior is:
ansible_connection
ansible_ssh_port
ansible_connection: winrm will use winrm instead of ssh against the node
ansible_ssh_port: 5986 will tell ansible to use https against port 5986 on the node
Weaknesses
- Hard to use with non-standard ports: There is currently a “hard link” between port 5986 and https, which doesn’t take into account that a user might want to use a different port.
- No support for non-https connections: Specifying 5985 should (in theory) have ansible switch to http (without the “s”) but this doesn’t seem to be implemented - results in error
- Hard to use domain accounts with basic auth: Since Ansible assumes kerberos if the user is specified as user@domain.com it is currently not possible to use domain accounts with basic authentication
- No support for CredSSP authentication (not sure if pywinrm supports credssp)
End goal: To have an option set flexible enough that the user is able to select a combination of port, transport and auth mechanism of their choosing
AFAIK: We have two options for accomplishing this: provide several additional valid options for ansible_connection, or add another couple of hostvars; for instance ansible_winrmauth and ansible_winrmtransport (or a combination of the two)
Option 1:
Instead of simply a “winrm” value for ansible_connection, we would need multiple values
winrm_http_kerberos
winrm_http_basic
winrm_https_kerberos
winrm_https_basic
In addition, some shorthand versions would probably also be necessary:
winrm: kerb/basic based on user name, https (provides backwards compatibility)
winrm_https: same as above
winrm_http: same as above using http
Option 2:
case1: User wants to connect to winrm using kerberos and http on non-default port 1799:
ansible_connection: winrm
ansible_winrmauth: kerberos
ansible_winrmtransport: http
ansible_ssh_port: 1799
case2: User wants to connecto using a domain account using basic auth on ssl port 1999
ansible_connection: winrm
ansible_winrmauth: basic
ansible_winrmtransport: https (or ommit, as this would be the default to ensure backwards comatibility)
ansible_ssh_port: 1999
These settings would remove the need for the “prep script” in scenarios where the node is domain-joined when deployed and winrm is enable by default (this setting accepts kerberos over http by default)
Any comments and suggestions appreciated!
I had briefly started down this path before to allow specifying the
connection scheme and path:
https://github.com/cchurch/ansible/commit/8490ee9ee27992cee14ca9578e7a690e1359608d#diff-632363d26324e7faf20ce2e8a932f8ea
v2 provides an explicit method for extracting any additional host variables
needed for the connection (
https://github.com/ansible/ansible/blob/devel/lib/ansible/plugins/connections/__init__.py#L95),
so it would be easy enough to support these connection options for winrm.
I'd recommend option 2 with at least the following host variables:
- ansible_winrm_scheme - http or https - default behavior chooses http
for port 5985, otherwise https. I've confirmed http with 5985 is working
against my winrm_v2_fixes branch. You do have to set
AllowUnencrypted="true" on the Windows host for http to work.
- ansible_winrm_path - defaults to /wsman, but can be anything to allow
for certain proxying configurations (e.g.
https://groups.google.com/d/topic/ansible-devel/BHxcyxamiuI/discussion).
- ansible_winrm_transport - plaintext, kerberos or ssl (or any future
option supported by the transport parameter to pywinrm), possibly a list of
transports to try in order, defaults to current behavior of trying kerberos
then plaintext if kerberos is installed, otherwise just plaintext.
Any additional arguments supported by pywinrm could be supported here as
well.
For those who really don't like using the ansible_ssh_* vars to specify
winrm options (
https://groups.google.com/forum/#!msg/ansible-devel/M-o91TtrXsM/_tiUC4Vh7Z4J),
there's the possibility of using alternate ansible_winrm_* variable names
for user, password, port, etc. But maybe that's going too far...
I was already planning to revisit supporting additional host vars once I
get an ok on my v2 fixes and finish work on improving the performance of
put_file (
https://github.com/cchurch/ansible/commit/e5021965f90c68ce3ed04a9bba8b5cb1898a3c9b
).
Sweet! I agree option two would be better.
I need to test 5985 in depth, allowunencrypted shouldn’t be needed as long as authentication is Kerberos. But I’ll do some retesting.
As for developing this my Python isn’t good enough to assist you but let me know if you need testing and/or sanity-checking!
Chris, I just tested a bit more between windows computer: The “AllowUnencrypter” flag does not seem to be necessary when using Kerberos authentication. Here’s a session from another computer to the target node:
and here’s the winrm config on that node. As you can see, allowunencrypted is not flagged true.
When trying to connect to the same node from ansible with port set to 5985 (using your PR) I’m getting this (using a local account - this shouldn’t work since local accounts default to basic auth, but the error indicates that it actually tries to setup a ssl session)
And here’s the same attempt using a domain account (this should work without the allowunencrypted flag imho)
Oops, wrong screenshot on the last one. Here’s the error from Ansible when trying port 5985 using a domain account without the allowunencrypted flag:
I did some more digging here, looking at the differences between how a “regular” powershell client (on Windows) authenticates over http (not https) vs how the pywinrm library does it in relation to the “allowunencrypted” setting (this is nerdy, I know):
A domain-joined Powershell client invoking a session against another domain-joined server will default to Kerberos-based authentication. Upon setting up the session the client wil not wait for the “www-authenticate” header from the server, it will simply pass inn the following auth header by default:
Authorization: Kerberos
The client can/will then start chatting xml-serialized powershell commands back and forth with the server.
The pywinrm library, on the other hand will pass an unauthenticated post request to the winrm service, and get back a regular www-authenticate response from the server. The client will then repost the original request with the following auth header:
Authorization: Negotiate
If “AllowUnencrypted” is set to false on the winrm listener (which it is by default on windows), the powershell commands sent to the winrm service will fail, although the initial authentication succeeds. If “AllowUnencrypted” is set to true, the client can then send powershell commands to the server.
So, I guess this has to do with how pywinrm and the server’s winrm service talk more than anything. Although I tell pywinrm to communicate over kerberos with the server it actually uses the “negotiate” header, which (i’m suspecting) winrm interprets as “unencrypted” although they actually use kerberos.
I also tried to disable the negotiate auth protocol on the server. In this case, a Windows winrm client will still be able to talk to the server. Pywinrm will get a www-authenticate header back from the server with instructions to authenticate using Kerberos, which causes pywinrm to fail.
I don’t know if anyone here is active on the pywinrm project but I guess there’s not much we can do from the Ansible side to attempt to make the pywinrm library more “windows-like” in the way it operates (apart from building our own winrm client which I guess nobody wants)