Skip to main content
RunBook Academy

AnsibleVI · Configuration and PrecedenceConfiguration and precedence

The config file Ansible refuses to read

Intermediate⏱ ~15 minansible-core

What you'll learn

  • Explain why a config file in a world-writable directory is a code-execution vector
  • Recognise the symptom: settings silently absent and config file = None
  • Apply both remedies and choose between them by situation
  • State the two cases the check does not cover

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.

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.

SettingWhat it points at
libraryDirectories searched for modules
action_pluginsDirectories searched for action plugins, which run on the controller
callback_plugins + callbacks_enabledPlugins loaded into the Ansible process itself
inventoryAn inventory source, which for a script plugin means an executable that is run
vault_password_fileA file that, if executable, is run and its stdout taken as the vault password
roles_pathWhere 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 --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
$ 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
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
export ANSIBLE_CONFIG=/srv/estate/ansible.cfg
ansible --version

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

  1. Q1. Why does Ansible refuse to read ./ansible.cfg from a world-writable directory?

  2. Q2. Which of these were confirmed on ansible-core 2.21.3? Select all that apply.

  3. Q3. Setting ANSIBLE_CONFIG to point at the file is a workaround that bypasses the security check and should be avoided.

  4. 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.