Skip to content

Kerberos Considerations (Role Authoring)

Rules for writing role task/var code that behaves correctly when Ansible authenticates to Windows hosts with Kerberos.

Scope

This page covers role code, not environment setup. Configuring the Ansible controller for Kerberos — krb5.conf, kinit, ansible_psrp_auth: kerberos, the SPN-vs-IP negotiate_hostname_override, ticket lifetime — lives in KERBEROS.md at the repo root. DNS/SPNs, clock skew, and transport selection are environment concerns a role author doesn't touch and are out of scope here.

The underlying theme

Under Kerberos, role code cannot rely on connection-derived context. Password and NTLM auth hand the target a concrete username and reusable credentials; a Kerberos ticket does neither in the same way. The two rules below are the same idea applied to identity and to credentials: when gathering more rules, the question to ask is "what else does role code assume the connection hands it that Kerberos doesn't?"

Rule — resolve identity from the target, not ansible_user

A role that needs to know or act as the connecting user — adding the install account to a group, running a task as that account — must resolve that identity from the target host at runtime, by querying the session directly (e.g. whoami), rather than deriving its operating identity from ansible_user.

Why. ansible_user cannot be assumed present or usable as the operating identity under Kerberos:

  • It may be undefined. The connection user is supplied from the vault (server_admin_useransible_user); in an environment where that isn't set, there's nothing to read.
  • Even when set, it's the connection principal in UPN form (user@REALM), not the resolved local/domain account (DOMAIN\user) you need for operations like local group membership.

Querying the host with whoami is the only source of truth for "who am I actually running as" that holds across every auth type.

This is not a contradiction of KERBEROS.md

KERBEROS.md requires ansible_user to be set (in UPN format) so the connection authenticates. That's the connection layer. This rule is about role tasks deriving their operating identity — a different concern. Set ansible_user for the connection; still resolve the operating identity from the target.

Reference implementation (kuiper).

  • tasks/install/prerequisites.yml runs whoami on the target and set_facts the result into kuiper_install_user, but only when it wasn't explicitly provided.
  • vars/main.yml defines kuiper_effective_install_user, templated lazily so it picks up that set_fact (a set_fact outranks role vars).
  • tasks/install/dependencies.yml consumes the effective var, guarded by when: item is string — an unresolved value is the boolean false, which the guard safely skips.

Rule — supply credentials explicitly for the second hop (double-hop)

Kerberos breaks operations where the target must authenticate onward to a third machine. A ticket obtained for the first hop can't be reused for a second hop, so downstream auth fails (login errors) even though the initial connection succeeded. The most common victim here is SQL Server Windows-integrated authentication.

Roles must:

  • Not assume a credential can be delegated across a hop. Any task reaching a second host on behalf of the user needs an explicit credential for that hop, not the connection's Kerberos ticket.
  • Assert that credential is present before the hop is attempted — fail fast with a clear message instead of surfacing an opaque downstream auth error.

Reference implementation (kuiper). tasks/install/prerequisites.yml requires kuiper_install_password / server_admin_password to be non-empty, then runs its SQL connectivity check under become: runas with that password (interactive logon, with_profile). That produces a fully-credentialed local logon that can authenticate onward — instead of a delegated ticket that cannot. This is the sanctioned double-hop solution in this repo; we deliberately do not rely on Kerberos delegation (ansible_winrm_kerberos_delegation) or CredSSP, which carry additional security tradeoffs.

Out of scope (handled elsewhere or by hand)

  • Ticket-cache refresh after group-membership changes. Group membership is baked into a Kerberos ticket, so a just-added membership isn't seen until a new ticket is issued — hence the klist purge in kuiper's dependencies.yml. This is a Windows/Kerberos behavior you'd work around regardless of Ansible, not a role-code authoring rule.
  • Controller and environment setup — see KERBEROS.md.