Repository navigation
feat(collection-fleet): optional ansible-vault encrypted inventory secrets - #207
Merged
Merged
Conversation
…crets
A private fleet repo built from examples/collection-fleet can now commit its
inventory secrets ansible-vault encrypted as vault.yml (host_vars/<host>/ or
group_vars/<group>/, at any depth). The git-ignored plaintext secret.yml flow
is unchanged, and a fleet without a vault.yml works exactly as before.
- scripts/check-vault-encrypted.sh + a vault-encrypted pre-commit hook: the
exact vault header, then ciphertext only; check-json and yamllint skip it.
- CI decrypts every committed vault file with the ANSIBLE_VAULT_PASSWORD
secret when set, else replaces each with {} and warns.
- This public repo git-ignores vault.* in both inventory trees; a private
copy (only the example's .gitignore) can commit them. scripts-test.sh
checks both sides, the script and the hook's patterns.
- Docs: the example README's "Encrypted secrets (optional)", ansible/README.md
private overlay, secret.yml.example headers, and the statements that called
secret.yml the only inventory home for a credential.
Closes #25
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
The setup command can overwrite an existing vault password without rekeying files, risking lost access, while the documented guard paths miss the overlay’s vault files.
2 open issues
What changed in this PR
Adds optional ansible-vault support to the private fleet template, addressing the scoped-down goal of #25 without changing role behavior or the existing plaintext-secret workflow.
Changes:
- Adds encrypted-file checks, pre-commit integration, and CI decryption or password-free stubbing.
- Documents vault setup, password handling, precedence, and rotation.
- Keeps vault files out of the public repository and adds regression checks.
| File | Description |
|---|---|
tests/scripts-test.sh |
Tests vault validation, ignore rules, and hook patterns. |
examples/collection-fleet/scripts/check-vault-encrypted.sh |
Checks vault headers and ciphertext-only bodies. |
examples/collection-fleet/README.md |
Documents optional encrypted secrets and CI behavior. |
examples/collection-fleet/inventory/host_vars/origin-1/secret.yml.example |
Documents the encrypted alternative for origins. |
examples/collection-fleet/inventory/host_vars/decdn-node-1/secret.yml.example |
Documents the encrypted alternative for nodes. |
examples/collection-fleet/AGENTS.md |
Updates private-fleet secret-handling guidance. |
examples/collection-fleet/.pre-commit-config.yaml |
Adds vault checks and ciphertext lint exclusions. |
examples/collection-fleet/.gitignore |
Explains why private vault files remain committable. |
examples/collection-fleet/.github/workflows/ci.yml |
Decrypts vault files or substitutes empty mappings. |
docs/requirements.md |
Lists private encrypted inventory as a secrets option. |
docs/publishers.md |
Documents encrypted inventory for origin credentials. |
CONTRIBUTING.md |
Describes expanded script-test coverage. |
ansible/roles/grafana_alloy/files/grafana-alloy.env.example |
Updates credential-storage comments only. |
ansible/roles/grafana_alloy/defaults/main.yml |
Updates token-storage comments only. |
ansible/README.md |
Documents vault use in private overlays. |
ansible/inventory/host_vars/origin-1/secret.yml.example |
Mirrors origin secret guidance. |
ansible/inventory/host_vars/decdn-node-1/secret.yml.example |
Mirrors node secret guidance. |
ansible/.gitignore |
Ignores vault files in public Ansible inventories. |
AGENTS.md |
Clarifies that vault support is private-repository only. |
.gitignore |
Ignores vault files in the public fleet example. |
Review effort: Balanced
…an overlay Review follow-ups on #207: the README's setup line refuses to replace an existing password file (set -C), and ansible/README.md says the copied hook and CI step need the inventory/ prefix dropped for an overlay whose vars sit beside hosts.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Closes #25, scoped down: the roles read plain variables, so the collections need nothing. The work is in the consumer template,
examples/collection-fleet, plus docs. Opt-in: the git-ignoredsecret.ymlflow is unchanged, and a fleet without avault.ymlworks exactly as before. A secret file written on the host stays the first choice.What changes
examples/collection-fleet(the private-repo template)vault.ymlbeside wheresecret.ymlwould go (host_vars/<host>/,group_vars/<group>/, at any depth, since Ansible loads those directories recursively).secret.*stays ignored, so a plaintext file still can't be committed by accident.scripts/check-vault-encrypted.shand avault-encryptedpre-commit hook. A vault file must be the exact ansible-vault header (1.1, or1.2with a--vault-idlabel) followed by hex lines only, so appended plaintext, plaintext on the header line and an empty body are all refused.check-jsonand yamllint skip the ciphertext.ANSIBLE_VAULT_PASSWORDActions secret, every committed vault file must decrypt (ansible-vault view, including files of hosts the inventory doesn't name). Without it (and on fork and Dependabot PRs), each vault file is replaced by{}in that checkout, with a::warning::. The secret reaches the step throughenv:and is written to aumask 077file.ansible.cfg), precedence (vault>secret>main), readable diffs, rekey-plus-rotate when someone leaves, CI.AGENTS.mdupdated.decdn/devops (public)
ansible/.gitignoreignorevault.*in both inventory trees. Even encrypted, ciphertext in a public repo can be attacked offline.ansible/README.md§ Private fleet inventory, the foursecret.yml.exampleheaders (still byte-identical across both trees), and the statements that calledsecret.ymlthe only inventory home for a credential (grafana_alloyenv example and defaults comment,docs/publishers.md,docs/requirements.md,CONTRIBUTING.md). The twografana_alloyedits are comments only.roles/grafana_alloy/tasks/preflight.yml'sfail_msgstill names only the two existing homes for the token. Left for a role change.Tests
tests/scripts-test.sh(make test-scripts) adds checks for:vault.*is ignored in this repo at every depth and in both trees (includingansible/inventory/<dir>/), while a private copy, which carries only the example's.gitignore, can commit it. Each rule has a negative case. Global git excludes are switched off, so a broken rule can't pass locally.1.1and1.2-labelled files, and refuses each plaintext shape, by inventory scan and by file argument.files:regex selects exactly what the script scans, and thecheck-jsonand yamllint excludes skip exactly the vault files.Verification
make test-scripts,make lintand actionlint pass.ansible-vaultfiles:vault.ymland a nestedvault.json; fails on plaintext appended to one.ansible-inventory --listfails ("no vault secrets found"); with it,vault.ymlwins oversecret.yml.::error;🤖 Generated with Claude Code