Repository navigation
fix(deploy): check_mounted_config reads what the container sees, not what docker cp re-reads - #772
Merged
Merged
Conversation
…what docker cp re-reads The check read each container's copy of a single-file mount with `docker cp`. For a bind mount, docker cp re-resolves the mount's source on the host, so it returned the file git had just written rather than the inode the container is pinned to, and every comparison was the new file against itself. Measured on a scratch container after a rename: `docker exec cat` said old, `docker cp` said new. On 2026-09-30 it passed blackbox.yaml while the exporter ran without the module #182 had just added. It now reads /proc/1/root/<path> from a helper sharing the container's PID namespace (SYS_PTRACE for the uid check, DAC_READ_SEARCH for 0640 keys), the pinned Alloy image via image-for.sh, so distroless loki is covered too. --self-test reproduces the rename on a throwaway non-root container. It passes with the new reader, and fails three of its five cases with the old one. Run against production it found Alertmanager on a pre-#761 config since 2026-09-28, missing the TlsAcmeRenewalStalled-over-Late inhibit. Recreated and verified. trinity's 12 single-file mounts all match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The implementation directly addresses the faulty read path and includes focused regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Fixes mounted-config verification by reading files through the container’s mount namespace instead of docker cp.
Changes:
- Adds a capability-limited Alloy helper for accurate reads.
- Adds a Docker-based regression self-test.
- Documents the failure and operational findings.
| File | Description |
|---|---|
scripts/check_mounted_config.py |
Implements namespace-aware reads and self-testing. |
docs/changelog.md |
Records the incident, fix, and deployment findings. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
What changed
scripts/check_mounted_config.pychecks that each running container has the same single-file config the repo has.make upruns it with--fix. It could never fail. This PR makes it read what the container actually sees.Why it could never fail. It read the container's copy with
docker cp. For a bind mount,docker cpre-resolves the mount's source path on the host, so it returns the file git just wrote. It does not return the inode the container is pinned to. Every comparison was the new file against itself.Measured on a scratch container after a git-style rename:
docker exec catolddocker cpnewThe new reader opens
/proc/1/root/<path>from a throwaway helper that shares the container's PID namespace. That resolves the path in the container's own mount namespace. It covers distrolessloki, whereexec catis impossible.SYS_PTRACEfor the cross-uid/proccheck, andDAC_READ_SEARCHfor 0640 keys. No network.image-for.sh.--self-test, discovered byself-tests.sh, reproduces the rename on a non-root, capability-free container. It passes with the new reader. With the old one it fails the three cases that involve a rename and passes the other two.What it found
TlsAcmeRenewalLateunderTlsAcmeRenewalStalled, so a stalled ACME renewal would have paged twice. It is recreated and verified with the new reader.How it first showed up
On 2026-09-30, during the #182 rollout, the check reported
blackbox.yamlas matching. The exporter was actually running without the module #182 had just added.Verification
--self-testpasses. A mutation run with the olddocker cpreader fails it.validate.shexit 0.🤖 Generated with Claude Code