You have a correct ansible.cfg in your working directory. Your settings
are not applying. The file is there, the syntax is right, the values are
right, and ansible --version reports config file = None.
The directory is world-writable, and Ansible refused to read the file on
purpose.
Why the refusal exists
An ansible.cfg is not a passive settings file. Several of its options
tell Ansible where to load executable code from, and that code runs on
the controller, as the user invoking Ansible.
Setting
What it points at
library
Directories searched for modules
action_plugins
Directories searched for action plugins, which run on the controller
callback_plugins + callbacks_enabled
Plugins loaded into the Ansible process itself
inventory
An inventory source, which for a script plugin means an executable that is run
vault_password_file
A file that, if executable, is run and its stdout taken as the vault password
roles_path
Where roles — and therefore their tasks — are found
Now put that file in a directory any local user can write to. Anybody with
a shell on the controller drops their own ansible.cfg there, pointing
callback_plugins at code they control. The next person who runs
ansible-playbook from that directory executes it, under their own
account — and if their next step was become: true against three hundred
production hosts, the attacker now has whatever that run had.
That is the attack the check prevents, and it is why the correct response
is never to work around it.
The symptom
Run from a directory with mode 0777:
Read-only / Safethe file is there and is not read— ansible.cfg exists in the current directory. Ansible reports no config file at all.
$ ansible --version
[WARNING]: Ansible is being run in a world writable directory (/srv/shared/estate), ignoring it as an ansible.cfg source. For more information see https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
ansible [core 2.21.3]
config file = None
Illustrative output
Read-only / Safeand the settings are simply gone— --only-changed reports nothing changed from defaults, because no file was loaded.
$ ansible-config dump --only-changed
[WARNING]: Ansible is being run in a world writable directory (/srv/shared/estate), ignoring it as an ansible.cfg source. For more information see https://docs.ansible.com/ansible/devel/reference_appendices/config.html#cfg-in-world-writable-dir
CONFIG_FILE() = None
Illustrative output
Both captured on ansible-core 2.21.3, with the directory path shortened
for the page.
Both fixes
Fix the directory mode
The right fix when the directory is yours:
Configuration changeremove world write— Clears the other-write bit and leaves the rest of the mode alone. Run it on the project directory, not recursively.
chmod o-w /srv/shared/estate
stat -c '%a %n' /srv/shared/estate
chmod o-w rather than chmod 0755 because it changes only the bit that
matters and does not quietly reset group access somebody set deliberately.
The file mode semantics behind this are the Linux course’s territory; what
matters here is that only the directory’s other-write bit is examined.
Set ANSIBLE_CONFIG explicitly
The right fix when the directory is not yours to change — a shared scratch
area, an NFS export with fixed permissions, a container image you did not
build:
Read-only / Safename the file instead of finding it— ANSIBLE_CONFIG is location one in the search order, so it applies regardless of the current directory's mode.
This is not a workaround for the security check; it is a different and
better arrangement. The check exists because discovery by working
directory is unsafe in shared locations. Naming the file explicitly
removes the discovery step, and with it the attack: an attacker cannot
substitute a file at a path you stated.
The compensating requirement is that the file you name must itself live
somewhere only you can write. Pointing ANSIBLE_CONFIG at a file in a
world-writable directory reintroduces the whole problem and bypasses the
check that would have caught it.
Where this actually bites
The check almost never fires because somebody typed chmod 777 on a
production repository. It fires in these situations:
A project unpacked into /tmp for a quick test. /tmp is 1777, so
the config is ignored and the “quick test” runs with default forks,
default timeout, and the default inventory path.
A shared build or scratch directory on a multi-user controller,
created world-writable so that several people can use it — which is
precisely the scenario the check was written for.
An archive extracted with its original modes.tar restores the
stored permissions; a directory archived as 0777 arrives as 0777.
A container bind mount where the host-side directory is
world-writable and nobody looked, so the same repository behaves one way
on a laptop and another way in the pipeline.
Knowledge check
Knowledge check · 4 questions
Q1. Why does Ansible refuse to read ./ansible.cfg from a world-writable directory?
Q2. Which of these were confirmed on ansible-core 2.21.3? Select all that apply.
Q3. Setting ANSIBLE_CONFIG to point at the file is a workaround that bypasses the security check and should be avoided.
Q4. A nightly CI job stopped applying its project ansible.cfg after the build directory was recreated world-writable. The job log shows nothing unusual. Why?
Passing score: 75%. Answers are checked in this browser.