TLDR: for Windows to authenticate with another server (double-hop), it will either need a Kerberos ticket with the delegation flag or the user’s actual credentials (CredSSP/become) there is no way around that. If there is that is a security issue with Windows and should be fixed by Microsoft.
CredSSP isn’t really the best way to go about this. And I think this post should go on Git as Ansible needs a better way to cover double-hops.
While I agree, it really isn’t the best way to go about this and people should use Kerberos with delegation instead, CredSSP is still viable in some situations and in total Ansible covers 3 ways of allowing this
- Kerberos with delegation
- CredSSP
- Become (Ansible specific implementation)
All 3 allow you to cover double-hops while the 3rd fix even more limitations of a network process, I’ll go into more detail below.
Background
The way that Windows works is that on a normal local logon, the username and password is supplied to LSA who then authenticates the user. This password is then available in that logon session and can be used by the client to authenticate with a remote server, i.e. connecting to a fileshare with the logged on credentials and so on. When authenticated with a server over the network (like WinRM), it will use an auth protocol like NTLM, Kerberos, CredSSP to authenticate the user and in the majority of cases the password of the user is not sent to the server. When LSA creates a logon session without a password (from an NTLM hash or Kerberos ticket like WinRM), it is unable to do things like calculate an NTLM hash or get a Kerberos ticket from the KDC in order to authenticate with another server.
This is usually fine for things like SMB but with WinRM users may want to interact with a third server to copy files like they would do locally but without that local password this is impossible for Windows to do, i.e. it can’t calculate the NTLM hash if it doesn’t have the password. In order to get over this problem, you can either use CredSSP to send the password to the remote server that is accessible by LSA or you can use a Kerberos ticket with the delegation flag set. With either of these methods, the remote server is able to authenticate with another server like you would be able to do locally but is unable to delegate to a 4th server from there, e.g. local → remote → double hop remote → another hop remote is not allowed.
CredSSP
The first option CredSSP is a protocol designed by Microsoft to allow an application (A) to delegate the user’s credentials to another server (B) by sending the user’s password as “plaintext” and not a hash. This delegation means that the LSA of the remote host (B) has a copy of the plaintext username and password of the user which it can then use to authenticate with a third remote server (C). In general the protocol works like this
- The initial server response returns a HTTP 401 error with
CredSSP
in the WWW-Authenticate
header
- The client sets up a TLS connection and starts the TLS Handshake which includes things like cipher suite negotiation
- Once the handshake is complete, the client will send either an NTLM or Kerberos token to authenticate the user
- After authenticating the user, the client will encrypt the server’s CredSSP public key with the authentication wrap function and send that to the server
- The server validates that the correct public key was used and there is no middle man in between the client and the server
- The server then sends it’s public key again with the first bit set to 1 also encrypted with the authentication wrap function
- The client will verify the public key and verify the first bit was set to 1
- Once both the client and server have verified each other, the client will then encrypt the username and password with the authentication wrap function and send that to the server
As well as the steps that use the authentication wrap function to encrypt the data, each step after the TLS handshake is also encrypted with the TLS protocol itself, e.g. when sending the username and password it is doubly encrypted (auth wrap and then TLS wrap). So the fact that the password is sent over the wire is troubling (and rightly unacceptable for some) but anyone snooping over the network would have a really hard time to get the credentials as it would have to break both the TLS protocol encryption and the auth protocol encryption (+1 more TLS encryption if running on the HTTPS WinRM listener).
Kerberos
Kerberos with delegation is a slight modification of the normal Kerberos process, normally the flow works like (I may be slightly wrong in some steps)
- User contacts the KDC (Domain Controller) to get a Ticket Granting Ticket (TGT) for their logon session (for Windows this happens on logon time)
- When authenticating with a server, the client uses the TGT and sends that to the KDC alongside the server’s SPN where the KDC then verifies everything is correct and sends back a service ticket and session key
- The client then sends this ticket to the service and uses the session key to encrypt the data
- The service then talks to the KDC to verify the client’s ticket and then sends back another ticket based on the KDC response
- The client then verifies this same ticket with the KDC in order to verify that the service is who they say it is
Once this process is complete, the service has authenticated the user and the user has also authenticated the server itself but this is still not enough for the server to authenticate to another remote server as it does not have the user’s password, just a short lived Kerberos ticket. The Kerberos protocol has an extension that defines a delegation flag which is attached to the request in step 2 that tells the KDC the service can delegate the user’s credentials to another service. So when the service in step 4 talks to the KDC it is able to get a TGT and use it to run the same process for a remote host.
Become
The third option (with Ansible) is to use become, as the username and password are sent to the server and used to create a new logon process. As this means LSA then has the credentials of that account it works like it would when running processes locally. This has it’s own problems in itself but solves more issues than what CredSSP or Kerberos with credential delegation fix. I won’t go into too much detail about this but become pretty much changes the Ansible process to run like you would run it locally.
Summary
Ultimately all 3 options allow you to authenticate with a further remote host on the remote session but where Kerberos with delegation is best is that you can define constrains on what servers it is able to authenticate with. With constrained delegation, the remote server (B) can then use that ticket to authenticate with another server (C) as long as it has been allowed and not constrained in the Domain Controller.
So in the end, if you need to authenticate with another server (double hop) you are better off using Kerberos with delegation as;
- You can restrict the server’s the credentials can be used to auth with to stop untrusted server’s from getting a hash/ticket of your credentials
- The password itself is still never sent to the server, a special Kerberos ticket is sent instead
- The number of requests is less than CredSSP so the setup time can be quicker
If you cannot use Kerberos (for whatever reason) then CredSSP can also be done to achieve the same result but it does have some security implications you should be aware off beforehand. When people say CredSSP is insecure, that is definitely not right, you just need to be aware of the risks involved and mitigate them if necessary.
Thanks
Jordan