Skip to content

Disambiguate node file paths from container mount aliases #2130

Open

Description

@robbycochran

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 path from host_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/ACS actualPath. The positive mount match distinguished the EmptyDir backing directory. A chrooted oc debug node process produced no prefix-based mount match despite a populated host path. Thus no_mount_match must 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 path to EffectivePath and host_path to ActualPath. ConstructFileAccess currently 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions