Skip to main content
RunBook Academy

AnsibleXXVII · Collections, Galaxy and Dependency TrustCollections and dependency trust

Checksums, GPG signatures and what they prove

Advanced⏱ ~20 minansible-galaxygpg

What you'll learn

  • Verify an installed collection against its manifest and against the published artefact
  • Distinguish what --offline verification proves from what an online verification proves
  • Configure GPG signature requirements for installs, and state their limits
  • Explain precisely why integrity verification is not a safety judgement

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.

Lesson 5 read a collection’s own claims about itself. This lesson checks a much narrower and much harder thing: that the bytes on your controller are the bytes somebody published.

That is a real and useful property. It is also, routinely, mistaken for a different and far larger one. A verified collection is not a safe collection. It is an unaltered one — which means the publisher’s code, exactly as the publisher wrote it, including anything they wrote badly or on purpose.

The two checksum files

Every collection installed from a Galaxy artefact carries two extra files that were not in the source tree:

Read-only / Safewhat an installed collection has that a git clone does not
$ ls -a collections/ansible_collections/example_corp/platform
.
..
FILES.json
MANIFEST.json
README.md
docs
meta
plugins
roles

FILES.json holds a SHA-256 checksum for every file in the collection. MANIFEST.json holds the collection metadata plus a checksum of FILES.json. That is a two-level chain: verify MANIFEST.json, and it vouches for FILES.json, which vouches for every file.

This is also the answer to the * version from lesson 3. A collection checked out from git has neither file, so there is nothing to verify against — which is the practical reason a production controller should install artefacts rather than clone repositories.

Verifying, and watching it catch something

Verification against the local manifest, with no network:

Read-only / Safeinternally consistent
$ ansible-galaxy collection verify example_corp.platform -p ./collections --offline
Verifying 'example_corp.platform:1.0.0'.
Installed collection found at '/home/opsuser/project/collections/ansible_collections/example_corp/platform'
MANIFEST.json hash: b7a3fc1e750306f07c857feb1f8fb374656e158fdf7101656503c9b03ba146df
Successfully verified that checksums for 'example_corp.platform:1.0.0' are internally consistent with its manifest.

Read the wording carefully: internally consistent with its manifest. That phrasing is doing real work, and the next command shows why it is not a stronger claim.

Now append one line to a file in the installed collection — the sort of thing a “quick local fix” looks like — and verify again:

Read-only / Safetampering is detected, by name
$ ansible-galaxy collection verify example_corp.platform -p ./collections --offline; echo "rc=$?"
Verifying 'example_corp.platform:1.0.0'.
Installed collection found at '/home/opsuser/project/collections/ansible_collections/example_corp/platform'
MANIFEST.json hash: b7a3fc1e750306f07c857feb1f8fb374656e158fdf7101656503c9b03ba146df
Collection example_corp.platform contains modified content in the following files:
  README.md
rc=1

Named file, non-zero exit. That is a check you can put in CI and in a pre-change gate.

--offline versus online

Drop --offline and the command downloads the published artefact and compares against that:

Read-only / Safeverified against what the server actually serves
$ ansible-galaxy collection verify community.docker -p ./collections
Verifying 'community.docker:4.7.0'.
Installed collection found at '/home/opsuser/project/collections/ansible_collections/community/docker'
Downloading https://galaxy.ansible.com/api/v3/plugin/ansible/content/published/collections/artifacts/community-docker-4.7.0.tar.gz to /home/opsuser/.ansible/tmp/ansible-local-162019a7u02r88/tmph1rz50he/community-docker-4.7.0-e9vko67e
MANIFEST.json hash: bedb151342e01b6945da014d533ef46128d1e82542722ddecc1f751a7b8fd2f6
Successfully verified that checksums for 'community.docker:4.7.0' match the remote collection.

The closing line differs: match the remote collection, not internally consistent with its manifest. Two different claims:

ModeComparesCatchesMisses
--offlineFiles on disk against the local MANIFEST.json/FILES.jsonLocal modification after installA manifest that was itself replaced, and anything wrong upstream
onlineFiles on disk against the freshly downloaded artefactLocal modification, and a manifest replaced along with the filesAnything wrong upstream

The --offline gap is real but narrow. An attacker who can rewrite files in your collections directory can also rewrite MANIFEST.json and FILES.json to match. Offline verification catches the careless edit; it does not catch the deliberate one.

Both miss the same larger thing, which is the point of the whole lesson.

GPG signatures

Galaxy servers can serve detached GPG signatures for a collection’s MANIFEST.json. ansible-galaxy will check them at install time if you give it a keyring.

Configuration changeinstall with signature requirements
ansible-galaxy collection install -r requirements.yml \
-p ./collections \
--keyring ~/.ansible/galaxy-keyring.kbx \
--required-valid-signature-count +all

The options that matter:

  • --keyring <path> — the GPG keyring used for verification. Without it, signature verification does not happen; there is no default keyring and no implicit trust store.
  • --signature <source> — an additional signature source, for a signature you obtained out of band rather than from the server. Repeatable. Only usable with a positional collection name, not with -r.
  • --required-valid-signature-count <n> — how many signatures must verify. A positive integer, or all. Prefix with + to fail when no valid signatures are found at all+all rather than all.
  • --ignore-signature-status-code <code> — tolerate specific GPG status codes such as NO_PUBKEY or EXPKEYSIG. Each one you add is a documented hole in the check.
  • --disable-gpg-verify — turn it off entirely.

Knowledge check

Knowledge check · 4 questions

  1. Q1. ansible-galaxy collection verify reports success. What has been established?

  2. Q2. A pipeline installs collections with --required-valid-signature-count all and reports that signature verification is enforced. What is wrong?

  3. Q3. Which are true of ansible-galaxy collection verify --offline? Select all that apply.

  4. Q4. Requiring GPG signatures on installs from public Galaxy is a straightforward hardening step for most estates.

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