Skip to main content
RunBook Academy

AnsibleXXXIX · Automation Platforms, RBAC and Event-DrivenAutomation platforms, RBAC and event-driven automation

Credentials the operator never sees

Advanced⏱ ~26 minansible-vaultansible-playbook

What you'll learn

  • Explain why use-without-read is impossible on a shared controller and possible in a platform
  • Trace the lifetime of an injected credential through a job run
  • Name the ways an injected credential still escapes into output
  • Assess the concentration risk a credential store creates and what mitigates it

Prerequisites

Verified against ansible-core 2.21.x · ansible (community package) 14.x · Python (controller) 3.12+ · ansible-lint 26.x · Molecule 26.x · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL / Rocky / AlmaLinux 9.x · 2026-08-11

Not yet marked complete on this device.

There is one thing a platform does that a controller genuinely cannot, and this lesson is about that one thing.

A person can launch a job that authenticates with a credential they are not able to read.

Every other capability in this part has a decent command-line equivalent. This one has an approximation with a hole in it, and the hole is where most estates keep their production root key.

Why a controller cannot do this

The argument is short. On a shared controller, ansible-playbook runs as you. Anything it can read, you can read.

Read-only / Safethe vault password file that lets a play decrypt also lets you decrypt
$ ansible-vault view --vault-password-file .vault-pass secrets.yml
---
db_admin_password: REPLACE_ME_DB_PASSWORD

The same is true of every other credential the run depends on. If your SSH agent holds the production key while the playbook runs, ssh can use it too. If the run reads a cloud token from an environment variable, so can env.

This is not a hardening failure. It is what “runs as you” means. On a controller, use implies read, and the only way around it is to run the job as an account you cannot become — which is the sudo-plus-wrapper construction from lesson 3, and which is worth building, and which fails completely against anyone with root on that box.

What the platform does instead

The mechanism has three parts, and it is worth knowing all three because the security property depends on each.

Encrypted at rest, and never returned. AWX encrypts passwords and key material in its database and never makes secret information visible via the API. A secret field, read back through the API, comes out as a placeholder rather than a value. There is no supported call that returns it.

Attached, not referenced. A credential is attached to a job template as an object. The template stores a relationship, not a path or a value. Nothing in the template needs to contain the secret in order to use it.

Injected at launch, into a process the user does not control. When a job starts, the platform decrypts the credential and injects it into the run — an SSH key written to a temporary file for the duration, a vault password supplied to the process, a cloud token placed in the environment — inside the execution environment, then removes it when the job ends.

Attaching a credential to a template requires the Use role on that credential. Use lets you employ the credential in a job template without viewing its sensitive content. That single role is the whole property:

The end user gets the benefit of the secret without the secret.

What still leaks

The property is real and it is narrower than people assume. An injected credential is protected from being read at rest. It is not protected from a playbook that prints it.

Everything from Part XXI about how secrets leak applies unchanged, and a platform makes two of the leaks worse because the output is now stored, searchable, and readable by anyone with Read on the job.

A command or shell task with the secret in its arguments. The task line appears in the output. A platform records that output permanently.

--diff on a rendered template. Diff mode prints file contents, including the credential your template just wrote into a config file. Set diff: false on tasks that render secrets, as Part XXV covers.

A registered result printed with debug. no_log: true on the task that handles the secret, and remember it applies to the task, not to the variable — a later task that touches the same value needs its own.

Verbosity. A job template has a verbosity field. At higher levels the module arguments are printed, which is exactly where the credential is.

The risk you accept

Put all the credentials in one system and that system becomes the most valuable host in the estate. This is not a reason to avoid doing it — scattering them across fourteen controllers is worse — but it is a trade you should make deliberately.

What the attacker gets. Not one environment: all of them. Not one key: the key, the vault passwords, the cloud tokens, the registry credentials. Plus the ability to run arbitrary automation against everything, with a job history telling them exactly which templates reach which hosts.

How they get it, in practice. Rarely by breaking the encryption. Usually by one of:

  • An account with Admin on a project, pointed at a repository they can push to. The platform runs what the repository contains, with whatever credentials the template holds.
  • An account with Admin on a job template, editing it to run a different playbook with the same credentials.
  • A shell on the platform host, which holds the encryption key.
  • Backups of the platform database, stored somewhere with weaker access control than the platform itself.

What mitigates it. Nothing eliminates concentration; these reduce the blast radius of a compromise:

ControlWhat it buys
Distinct credentials per environment, never a fleet-wide oneA compromised staging credential does not reach production
Credential Admin held by a small named groupFewer accounts can create a template that uses the production key
Projects pinned to a protected branch or tagPushing code is not enough to make the platform run it
An external secret manager as the source of truthThe platform holds a short-lived lease rather than the permanent secret
Short-lived certificates instead of long-lived keysA stolen credential expires on its own
Monitoring on credential use, not just on job successThe unusual run at 04:00 is visible as an event

The last two are where mature estates end up. Part XXI covered external secret managers; the relevant point here is that a platform integrated with one holds a reference plus the authority to fetch, rather than the secret itself — which converts “an offline copy of everything” into “an online capability we can revoke”.

Knowledge check

Knowledge check · 4 questions

  1. Q1. What makes use-without-read achievable in a platform but not on a shared controller?

  2. Q2. A no_log mistake under a platform credential can disclose a secret to more people than could have read the credential in the first place.

  3. Q3. Which of these meaningfully reduce the blast radius of a compromised credential store? Select all that apply.

  4. Q4. During an incident you need to determine whether the SSH key stored in a platform credential is the same one present in authorized_keys on a failing host. What is the correct approach?

Passing score: 75%. Answers are checked in this browser.