AnsibleXLVII · Controller SecurityController security
Vault identity custody and rotation
What you'll learn
- Judge a vault password arrangement by who can read it rather than by whether it is committed
- Choose between prompt, file and script vault ID sources for a given controller role
- Rekey a vault file to a new identity without losing access to encrypted history
- Plan a vault rotation that accounts for what the vault protected, not just the vault itself
- Separate custody from mechanism, and reference rather than re-derive the mechanism
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
Part XXI owns Vault: what it encrypts, vault IDs, no_log, and the
mechanics of where the password is read from. Part XXI lesson 5 in
particular establishes the sources available and the client-script
contract, and this lesson does not repeat any of it.
What belongs here is the security-review question that mechanism does not answer: who holds the password, what does holding it grant, and how do you take it away?
The blunt statement first, because everything follows from it.
A password file beside the repository protects nothing
If the vault password lives in a file readable by everyone who can read the repository, then the repository is not encrypted in any way that matters. Every person and process that can read the encrypted variables can also read the key that decrypts them. You have added a step, not a control.
This arrangement is extremely common and it is usually not a decision.
It happens because a password file is what makes unattended runs work,
~/.vault_pass is where the tutorial put it, and nobody revisited the
mode.
The test is not “is the password committed to Git”. That is a real failure and a serious one, but it is the easy version of the question, and a repository can pass it while being wholly unprotected.
The test is: enumerate the accounts that can read the password, and compare that set with the accounts that can read the encrypted files. If the two sets are the same, the encryption is decoration.
Custody arrangements, ranked by what they survive
Part XXI covers how each of these is configured. What follows is what each one survives, which is the custody question.
Interactive prompt. The password exists only in the head of the person running the play and in the process for its lifetime. It survives a stolen disk, a backup, a compromised service account, and a read of the filesystem. It does not support unattended runs at all, which is why it is correct for a human-operated production controller and useless for a scheduler.
A file on the controller. Survives nothing that can read the filesystem as the owning account. Its only protection is the file mode and the security of the controller — that is, everything in lessons 2 and 3. This is the honest description, and a team that has chosen this consciously, with a hardened dedicated controller and a small group, is in a defensible position. A team that arrived here by default is not.
A script fetching from a secret manager. Survives a filesystem read, because there is no password on disk — the script fetches it. Its strength depends entirely on how the secret manager authenticates the caller. If the script presents a token that also sits on the controller in a file, you have moved the problem one hop and not solved it. If the manager authenticates the machine or workload identity and logs each fetch, you have gained two things that no other option offers: the password is not at rest on the controller, and retrieval is recorded.
That second property is the one lesson 3 argued for. It converts “anyone with access could have taken this secret” into a list of retrievals with times.
Vault IDs as a separation boundary
DEFAULT_VAULT_IDENTITY_LIST defaults to empty, which is worth knowing
because it means no vault identity is configured until you configure
one:
$ ansible-config dump | grep -i vaultDEFAULT_ASK_VAULT_PASS(default) = False
DEFAULT_VAULT_ENCRYPT_IDENTITY(default) = None
DEFAULT_VAULT_IDENTITY(default) = default
DEFAULT_VAULT_ID_MATCH(default) = False
DEFAULT_VAULT_IDENTITY_LIST(default) = []
DEFAULT_VAULT_PASSWORD_FILE(default) = NoneFor custody, the identity list is how you make “the production vault password” a distinct thing from “the staging vault password” rather than one password with two uses. The mechanism is Part XXI’s; the reason to use it is lesson 3’s, and it is the same reason as separate SSH keys per environment.
Note DEFAULT_VAULT_ID_MATCH defaults to False. That means Ansible
will try every configured identity against an encrypted file until one
works, rather than requiring the file’s labelled ID. Convenient, and it
weakens the boundary: a controller holding both identities decrypts
production content whether or not you asked it to. Where separation is
the point, that default deserves a deliberate decision rather than
inheritance.
Rekeying
Changing a vault password is ansible-vault rekey, and with vault IDs
you name the new identity:
--new-vault-id and --new-vault-password-file are mutually exclusive,
which the CLI enforces — verified on 2.21.3, where they appear in the
same argument group.
Three operational points that the command itself will not tell you.
Rekey is in-place and per file. It rewrites each file you name. Missing one leaves a file encrypted under the old password, which will fail to decrypt once the old identity is retired — on the next run, in whatever play happens to reference it.
Commit before you rekey. The working tree is being rewritten. If the rekey is interrupted partway through a list of files, you want a clean point to return to.
Rekey does not rewrite history. This is the important one.
Rotating what the vault protected
That callout is the whole reason this lesson exists, so it is worth laying out as a procedure.
A vault password rotation has two distinct scopes and teams routinely complete only the first.
Scope one: the vault. Generate the new password, rekey the files, update the custody arrangement, distribute to whoever needs it, retire the old identity. Mechanical, a few hours, and entirely on the controller.
Scope two: the secrets. Every credential the vault contained is now known to whoever knew the old password, including from history. Each one must be changed in the system that owns it and then re-encrypted under the new identity.
Scope two is the expensive one, it involves other teams, and it is what turns a vault exposure into an incident with a project plan. It is also the only scope that changes an attacker’s position.
Whether scope two is required is a judgement about the exposure. A planned rotation on a schedule, where nothing suggests the old password left the controller, reasonably does scope one only. A password that was committed, shared in chat, or held by someone who left under unfriendly terms requires scope two, and deciding otherwise should be an explicit, recorded decision rather than an omission.
Knowledge check
Knowledge check · 4 questions
Q1. What is the right test of whether a vault arrangement provides real protection?
Q2. A vault password may have been exposed. Which statements are accurate? Select all that apply.
Q3. Which custody arrangement offers a record of when a secret was retrieved?
Q4. Running ansible-vault rekey across the repository removes the old password ability to decrypt that repository encrypted content.
Passing score: 75%. Answers are checked in this browser.