Repository navigation
Disambiguate node file paths from container mount aliases #2130
Description
Problem
A node file policy watching host /etc must match changes to the host-backed directory, even when a container accesses it through a different mount destination. A container-visible /etc path alone does not establish that the node's /etc changed.
| Host source → container destination | Observed path | Intended result for a node /etc policy |
|---|---|---|
/tmp/scratch → /etc |
/etc/example |
No match: the backing file is outside host /etc |
/etc → /etc |
/etc/example |
Match |
/etc → /host-config |
/host-config/example |
Match |
/etc → /etc, with /tmp/scratch → /etc/nested |
/etc/nested/example |
No match: the nested mount overrides the parent |
Use temporary directories to model these cases in tests; do not modify the real host /etc.
This is a path-identity/correctness problem. A container mount lookup should help resolve existing paths and metadata accurately, rather than only append mount records to diagnostic output.
Evidence and boundaries
- Fact already distinguishes the observed
pathfromhost_path. HostScanner uses device/inode tracking to supply the latter, and existing tests cover some container aliases. Fact collection also matches observed container paths; collecting such an event is not itself proof of a false node-policy alert. - The archived experiments associated with feat: add opt-in container runtime diagnostics #1651 observed both container-rootfs and EmptyDir writes with empty
host_path/ACSactualPath. The positive mount match distinguished the EmptyDir backing directory. A chrootedoc debug nodeprocess produced no prefix-based mount match despite a populated host path. Thusno_mount_matchmust remain unresolved, not be classified as rootfs. - fix: follow absolute symlink directories from host mount #1655 and test: cover configured relative symlink root #1641 cover related absolute/relative symlink discovery problems. Missing collection and incorrect path attribution need separate regression assertions.
- In StackRox, Sensor copies Fact
pathtoEffectivePathandhost_pathtoActualPath.ConstructFileAccesscurrently includes both paths (and both rename endpoints) as alternatives for the file path criterion. Accurate mount-backed paths alone may therefore not correct node-policy matching. Node/deployment routing and path matching must be verified independently. - A false node-policy alert for the exact matrix above has not yet been reproduced end to end in this work. The issue tracks the reported ambiguity, the Fact path contract, and the downstream verification/fix; it does not assume that every case requires a Fact code change.
Relevant code:
Regression/characterization PR: #2131 (test-only, based on main).
Initial live characterization (2026-10-14)
The four layouts above, each exercised with existing-file OPEN and new-file CREATE through both gRPC and OTEL, passed all 16 cases against the current main image quay.io/stackrox-io/fact:0.4.x-81-g661acec004-otel on a privileged Podman Fedora CoreOS VM (arm64, kernel 6.11.3-200.fc40.aarch64).
The unrelated-source and nested-shadow cases emitted the observed container path with an empty host_path. Both genuine host aliases emitted the correct watched host path. This preserves the existing Fact output contract; it does not reproduce a false node-policy alert or prove the end-to-end policy behavior is correct. No expected-failure marker is appropriate for these passing cases.
Building blocks
1. Regression and characterization tests
Introduce a small main-based test PR with the matrix above, covering existing-file writable OPEN and new-file CREATE in both gRPC and OTEL. Assert observed path, host-path attribution, and container identity. Keep positive alias cases to prevent fixing a false positive by dropping genuine host writes. Preserve runtime mount inspection and Fact logs for failures.
Separately exercise the StackRox node matcher with conflicting ActualPath/EffectivePath, then verify actual Sensor routing and alert results on OpenShift. Add rename source/destination, multiple aliases of one inode, and chroot/namespace cases as focused extensions.
2. Shared container lookup following Mauro's architecture
Build on #1900's container gatherer: startup discovery, runtime lifecycle updates, cached metadata, and bounded waiting for events whose container data is missing. Extract Docker-specific operations behind a narrow provider boundary instead of maintaining a second enrichment pipeline.
Providers own protocol/connection details; the gatherer owns container state and pending events. New providers should require a small registration change, without receiving unrelated application state. Bound cache/request/event queues and coalesce concurrent lookups.
3. CRI backend and normalized mounts
Adapt #2112 into the shared design for CRI-O/containerd. Keep feature gating and Konflux release exclusion. Define startup discovery and lifecycle/reconnect behavior based on verified runtime capabilities.
Normalize the common Docker/CRI mount fields (container destination, host source, read-only status, propagation where available), preserving unavailable fields explicitly. Do not infer Kubernetes volume type as a guaranteed CRI field. Full container ID output and labels remain separate, narrow data-collection follow-ups; internal lookup identity must be unambiguous.
4. Shared path resolution using mount and file identity
Match the longest destination by path components, respecting nested mounts. Construct a candidate backing path from source plus relative suffix. Validate with device/inode identity where possible and explicitly retain uncertainty for removed files, runtime lookup failure, dynamic mounts, and process-root/mount-namespace differences.
Keep observed path, configured monitoring alias, and backing path conceptually distinct. Do not overwrite inode-backed host attribution with an unverified prefix substitution. Determine the correct integration point in HostScanner/event handling; missing-discovery cases may require an earlier hook than output enrichment.
5. Node-policy semantics and end-to-end acceptance
Ensure node policies use established host-file identity, rather than accepting a container-only observed-path match as host /etc. Preserve deployment-policy behavior and verify routing separately. Update Sensor/protobuf contracts only if existing fields cannot express the necessary identity/confidence.
Acceptance: temporary backing mounted at /etc produces no host /etc attribution or node-policy violation; real host /etc accessed under either destination remains detectable; nested overrides do not inherit the parent mount's attribution. Include an explicit unknown/unresolved outcome rather than inventing a host path.
Scope
This issue is the umbrella for focused PRs, not a request to combine provider extraction, CRI support, mount resolution, policy changes, and all metadata into one patch. Image enrichment alone does not solve the path-identity problem.
Activity