In the previous posts I’ve started to show you some of the possible uses of Ansible, in particular some example for managing Windows machines. As I’ve explained in the first post about Ansible, the software can login into any Windows machine using both local or domain users. In the latter case, Kerberos has to be configured and used. This works really well, but there’s one issue that needs to be solved first: the refresh of the TGT.
A brief introduction to kerberos
Kerberos is a computer network authentication protocol that works on the basis of ‘tickets’ to allow nodes communicating over a non-secure network to prove their identity to one another in a secure manner. Its designers aimed it primarily at a client–server model and it provides mutual authentication—both the user and the server verify each other’s identity. Kerberos protocol messages are protected against eavesdropping and replay attacks. Kerberos tickets are requested by a client and delivered, upon successful authentication, by a kerberos server.
This is a quick explanation of how kerberos works: the client authenticates itself to the Authentication Server (AS) which forwards the username to a key distribution center (KDC). The KDC issues a ticket-granting ticket (TGT), which is time stamped, encrypts it using the user’s password and returns the encrypted result to the user’s workstation. This is done infrequently, typically at user logon; the TGT expires at some point, though may be transparently renewed by the user’s session manager while they are logged in. When the client needs to communicate with another node (“principal” in Kerberos parlance) the client sends the TGT to the ticket-granting service (TGS), which usually shares the same host as the KDC. After verifying the TGT is valid and the user is permitted to access the requested service, the TGS issues a ticket and session keys, which are returned to the client. The client then sends the ticket to the service server (SS) along with its service request.
How to renew a TGT in Linux
So, the issue that we are facing can be better understood after the brief Kerberos explanation. If we run a playbook or a command involving any Kerberos authentication, but without running a kinit command first, the task will fail:
The error is pretty clear in Ansible: No Kerberos credentials available. The kinit command we have launched at the beginning of the session requests the TGT to Active Directory, and with it Ansible is able to process windows servers using the TGT itself. But at some point, the TGT expires:
[root@ansible playbooks]# klist
klist: Credentials cache keyring 'persistent:0:0' not found
Klist returns zero results, meaning that there are no TGT available for the Ansible machine. We can run again klist to obtain a new TGT:
I’ve obtained a new TGT, that will expire after 10 hours. There are some switches in the kinit command to obtain a ticket with a longer duration, but at some point it will expire anyway. What I need is a solution to allow my linux machine where Ansible is running to renew the TGT without any manual intervention, but only relying on the local unattended services.
Automatic TGT renewal with keytab
The solution is a keytab file. It’s a file that contains a table of user accounts, with an encrypted hash of the user’s password. Why we want to use a keytab file? Well, when you want a server process to automatically logon to Active Directory on startup, you have two options: type the password (in clear text) into a config file somewhere, or store an encrypted hash of the password in a keytab file. Seems pretty easy to understand which method is more secure.
The file can be created using a number of utilities. On a Windows machine, you can use ktpass.exe. On Linux, you can use ktutil. Run this sequence of commands:
[root@ansible playbooks]# ktutil
ktutil: addent -password -p <a href="mailto:administrator@SKUNKWORKS.LOCAL">administrator@SKUNKWORKS.LOCAL</a> -k 1 -e RC4-HMAC
Password for <a href="mailto:administrator@SKUNKWORKS.LOCAL">administrator@SKUNKWORKS.LOCAL</a>:
ktutil: wkt username.keytab
Inside ktutil, we create a new hashed record for my domain user, we write the password in the console, and we store the hash in the username.keytab file. Once the keytab is created, we can test it by trying a new kinit operation, this time with the keytab instead of the password:
kinit <a href="mailto:administrator@SKUNKWORKS.LOCAL">administrator@SKUNKWORKS.LOCAL</a> -k -t username.keytab
The command returns nothing if it has completed successfully, and we can see that the new TGT has been granted:
[root@ansible playbooks]# klist
Ticket cache: KEYRING:persistent:0:0
Default principal: <a href="mailto:administrator@SKUNKWORKS.LOCAL">administrator@SKUNKWORKS.LOCAL</a>
Valid starting Expires Service principal
10/10/2016 10:26:42 10/10/2016 20:26:42 <a href="mailto:krbtgt/SKUNKWORKS.LOCAL@SKUNKWORKS.LOCAL">krbtgt/SKUNKWORKS.LOCAL@SKUNKWORKS.LOCAL</a>
renew until 10/17/2016 10:26:42
You can see that this new TGT has been issued at 10.26.42, while the previous was issued at 09.43.18.
Now that we have a valid TGT, Ansible can use this ticket to run operations against Windows machines joined to a domain. The missing piece is the automatic renewal. To do so, you can simply schedule a cron job with the kinit operation I’ve written before:
kinit <a href="mailto:administrator@SKUNKWORKS.LOCAL">administrator@SKUNKWORKS.LOCAL</a> -k -t /etc/ansible/keys/username.keytab