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_user→ansible_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.ymlrunswhoamion the target andset_facts the result intokuiper_install_user, but only when it wasn't explicitly provided.vars/main.ymldefineskuiper_effective_install_user, templated lazily so it picks up thatset_fact(aset_factoutranks role vars).tasks/install/dependencies.ymlconsumes the effective var, guarded bywhen: item is string— an unresolved value is the booleanfalse, 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 purgeinkuiper'sdependencies.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.