Repository navigation
[CXP-3978] Migrate Operator process passwd mount + mount /etc/os-release by default - #3645
gengnamstyle wants to merge 27 commits into
Conversation
Use a 7.86.0-0 minimum for /host/etc/passwd, matching other Agent feature minimum versions and Helm, so 7.86.0+ RC/devel builds can be tested. Older versions, unknown tags, digest pins, and older Autopilot allowlist pins still retain /etc/passwd. Update tests and generated docs. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
GKE Warden matches volumeMounts by name and rejected a passwd mount at /host/etc/passwd against the v1.0.8 allowlist. Live process and process discovery now mount a dedicated host-passwd volume (hostPath /etc/passwd) at /host/etc/passwd; security-agent and system-probe keep the passwd volume at /etc/passwd. Talos strips both volumes. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Formatting only: split each process passwd test case so every struct field is on its own line. No case, value, or expectation changes. Environment: Datadog workspace Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Sync the previous agent WorkloadAllowlist (v1.0.7) alongside the v1.0.8 default while it propagates across GKE node versions, matching the CSI synchronizer. Explicit pins still sync only the pinned version. - When process passwd moves to /host/etc/passwd, also mount host /etc/os-release read-only at /host/etc/os-release (volume os-release-file, as in Helm and the GKE allowlist). The Agent sets HOST_ETC=/host/etc once that directory exists, and the default agent container had no other /host/etc mount, so host metadata would otherwise lose os-release. Talos strips it with the passwd volumes. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cbb2ae7 to
732c22a
Compare
…rride Reword the struct comment in liveprocess and processdiscovery: the passwd mount path depends on the final Agent image, but ManageNodeAgent runs before user overrides are applied, so the features keep the DatadogAgent and node Agent override from Configure to predict that image. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… resolution - Replace ProcessPasswdVolumes with ProcessPasswdVolume, which only returns the passwd volume and keeps its name paired with the mount path. Add the os-release volume inline in liveprocess and processdiscovery, next to the other host volumes, when passwd is relocated under /host/etc. - Export the synchronizer's ResolveWorkloadAllowlistVersion and use it instead of a copied version regex, so the mount decision always uses the version the synchronizer installs. - Build expected mounts and volumes in order in the feature tests. No behavior change. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the custom image prediction (resolveProcessAgentImage) with the pattern other version-gated features use: IsAboveMinVersion of GetComponentVersion(dda, nodeAgent) against 7.86.0-0, keeping /etc/passwd when the version cannot be parsed. Known limitation, shared with those features and documented in code, tests, and docs: experimental per-container image annotations and container renames are not considered. The impact is limited to process usernames. Tags without a full X.Y.Z version (7, latest, 7.86) keep /etc/passwd; Datadog does not publish X.Y tags. Also expand the previousWorkloadAllowlistVersion comment. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ublished Gate the /host/etc/passwd relocation off whenever Autopilot is enabled: Autopilot only admits it once a WorkloadAllowlist that permits it is published and rolled out. Non-Autopilot behavior is unchanged. Move the Autopilot-only pieces to a follow-up: the v1.0.8 default allowlist, syncing the previous version, the allowlist-pin check, and the exported version resolver. Update tests and docs. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a k8s suite subtest that deploys a DatadogAgent with live process collection on Agent 7.86.0-rc.6 and checks the agent container: host-passwd at /host/etc/passwd with no /etc/passwd mount, os-release at /host/etc/os-release, dd-agent still resolvable, and a host platform in agent status. Switch to 7.86.0 once it is released. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kubernetes allows one volume at two paths; GKE Autopilot's Warden matches allowlisted volumeMounts by name, so host-passwd is its own volume. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The e2e check now compares the Agent platform with the node's os-release ID. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mount the existing passwd volume at /host/etc/passwd for Agent 7.86+ instead of a separate host-passwd volume, matching Helm. GKE Autopilot needs a separately named volume (Warden matches allowlisted mounts by name); that comes with the Autopilot follow-up. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| // Mounted with the relocated passwd: the Agent sets HOST_ETC=/host/etc whenever that directory exists. | ||
| // The volume name matches the Helm chart and the GKE Autopilot WorkloadAllowlist. |
There was a problem hiding this comment.
Now that /host/etc exists with our passwd mount, we also need to mount the os-release location because the agent will use /host/etc for mounted paths when the directory exists as the agent sets HOST_ETC=/host/etc https://github.com/DataDog/datadog-agent/blob/8113ee2796ecc8b0c89301aebb339e8fdbb29c1a/pkg/config/setup/config.go#L372-L385
This is then also used to read host OS files from there.
Before this PR the Operator's agent container had nothing under /host/etc, so mounting passwd at /host/etc/passwd is what turns HOST_ETC on. Therefore, we must mount host /etc/os-release there too keeps host metadata's platform populated (gopsutil).
Helm's agent container already mounts it https://github.com/DataDog/helm-charts/blob/9eccc9bc6d0f325c7807f4dafcc15b1019cb11b1/charts/datadog/templates/_container-host-release-volumemounts.yaml#L1-L13
Explain why the process features keep the DatadogAgent, and rename the test helpers' want* locals to expected*. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 345e2244a9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
An image override that sets no name or tag (for example only pullSecrets) keeps the default image tag, so passwd mount selection now uses the default Agent version. Also add a GKE Autopilot e2e step checking that a 7.86 Agent keeps /etc/passwd until the WorkloadAllowlist permits /host/etc/passwd. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Combine the partial image override fallback with the Autopilot allowlist pin check, and keep the follow-up's host-passwd Autopilot e2e step. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| // Mounting passwd under /host/etc makes the Agent read host files from there, | ||
| // so host os-release must be mounted there too. | ||
| if mountPath == common.HostPasswdMountPath { | ||
| osReleaseVol, osReleaseVolMount := volume.GetVolumes(common.OSReleaseFileVolumeName, common.OSReleaseFileHostPath, common.OSReleaseFileMountPath, true) | ||
| managers.VolumeMount().AddVolumeMountToContainer(&osReleaseVolMount, agentContainerName) | ||
| managers.Volume().AddVolume(&osReleaseVol) | ||
| } | ||
|
|
There was a problem hiding this comment.
I think having this here seems odd since this mount actually has nothing to do with the liveprocess feature. Shouldn't the os-release file just always be mounted at HOST_ETC if this is what helm does? It won't break the processes features.
There was a problem hiding this comment.
Yeah I agree having it here is kind of odd since we would be mounting it only if these features are enabled.
The os-release file would be searched for at HOST_ETC but it won't be mounted there.
https://github.com/shirou/gopsutil/blob/v4.26.9/host/host_linux.go#L270-L275
So if we don't actually explicitly mount it then we could see issues with the libraries reading there.
Something that is interesting is that os-release is not mounted by default in the operator at all it appears
datadog-operator/internal/controller/datadogagent/component/agent/default.go
Lines 899 to 912 in 5b4a186
What this mean is that when the agent reads the the os-release file it may read the agent's own container image os-release file instead of the host's node which I believe has been an existing bug on the operator for a while.
Ideally, it seems like the operator should by default mount this but this seems like something that should be a followup.
My agent pointed me to these 2 hosts internally (one installed with the operator and one with helm and they report different os's despite being on the same type of amazon node.
There was a problem hiding this comment.
To rephrase, I think the mount should be added by default by the Operator, in the spot you linked:
datadog-operator/internal/controller/datadogagent/component/agent/default.go
Lines 899 to 912 in 5b4a186
The Agent sets HOST_ETC=/host/etc whenever /host/etc exists and reads the host OS from /host/etc/os-release, so mount it in the core agent and process-agent by default, like the Helm chart, instead of only alongside the relocated passwd in the process features. Every WorkloadAllowlist version already allows it. Also reword docs and comments to say "the host's /etc/passwd". Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
os-release now comes from the default node Agent volumes, so drop it from the Talos strip lists; keep host-passwd and the Autopilot pin handling. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Revert the always-on os-release mount in the default node Agent volumes; it changes host OS reporting for every Operator user and will be a separate PR. os-release is again mounted only alongside the relocated passwd. Keep the "the host's /etc/passwd" wording. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Containerized Agents read the host OS from /host/etc/os-release once /host/etc exists, and from the Agent image's /etc/os-release otherwise. Mount it by default in the core agent and process-agent, like the Helm chart, instead of only alongside the relocated passwd in the process features. Every WorkloadAllowlist version already allows it. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the generated golden renders conflicted; regenerated them on the merged tree. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Covers GetVolumeForOSRelease and GetVolumeMountForOSRelease in the common package, which the patch coverage gate counted as uncovered. Environment: Datadog workspace Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
/etc/os-release by default
tbavelier
left a comment
There was a problem hiding this comment.
Thanks for the detailed PR and the validation notes. I'm requesting changes for the default-version tests that will break on the next Agent bump, and for the duplicated os-release volume. Details are in the inline comments.
-
Tests: the default-version tests fail as soon as
images.AgentLatestVersionreaches 7.86 (inline onprocess_passwd_test.go). -
Duplicated os-release volume with CWS and the private action runner: reuse the new
os-release-filevolume instead (inline oncommon/const.go). -
Nits: the e2e platform comparison and the Windows branch in
ProcessPasswdMountPath(inline). -
PR description: "Talos strips
passwdandos-release-file" is out of date. Since ceff214, Talos only stripspasswd, which is correct. I checked on a live Talos v1.15.0-alpha.0 QEMU cluster:/etc/os-releaseexists on the node as a regular file (ID=talos).- A pod with the same untyped hostPath mounted read-only at
/host/etc/os-releaseruns fine and reads the host file. - A hostPath for
/etc/passwdfails withfailed to mkdir "/etc/passwd": mkdir /etc/passwd: read-only file system, so the passwd strip is still required.
Could you update that line?
| expectedPath string | ||
| }{ | ||
| { | ||
| name: "no override uses the default Agent version", |
There was a problem hiding this comment.
These cases hard-code the legacy path for the default Agent version, as do the default cases in the liveprocess and processdiscovery feature tests (for example liveprocess/feature_test.go:51, which expects common.PasswdMountPath).
When the automated "Update default Agent to 7.86.x" PR bumps images.AgentLatestVersion, ProcessPasswdMountPath starts returning /host/etc/passwd and these tests fail. I checked locally by setting AgentLatestVersion = "7.86.0". TestProcessPasswdMountPath, Test_liveProcessFeature_Configure and Test_processDiscoveryFeature_Configure all fail. The bump PRs already regenerate the golden files, but they don't update hand-written tests, so the bump PR would be blocked.
Could the expected path for the default cases come from images.AgentLatestVersion? For example, a small test helper that returns HostPasswdMountPath when the default is >= 7.86.0-0. The other option is to pin the version these tests use, the way nodeAgentVersion already takes defaultVersion.
| // Always mounted in the core agent and process-agent (like Helm): the Agent sets HOST_ETC=/host/etc | ||
| // whenever that directory exists and then reads the host OS from /host/etc/os-release. | ||
| // The volume name matches the Helm chart and the GKE Autopilot WorkloadAllowlist. | ||
| OSReleaseFileVolumeName = "os-release-file" |
There was a problem hiding this comment.
This duplicates SystemProbeOSReleaseDir* (L139-141): same hostPath /etc/os-release, same mountPath /host/etc/os-release, but a different volume name (host-osrelease). With CWS or the private action runner enabled, the pod now declares two hostPath volumes for the same host file. That leaves two sets of constants to keep in sync.
Now that os-release-file is a default node Agent volume, I'd remove SystemProbeOSReleaseDir* and have CWS and the private action runner reuse the new helpers:
// cws/feature.go:352 and privateactionrunner/feature.go:379
osReleaseVol, osReleaseVolMount := common.GetVolumeForOSRelease(), common.GetVolumeMountForOSRelease()The volume manager merges volumes with the same name, so the pod ends up with a single os-release-file volume. The core agent, process-agent, system-probe and the private action runner all share it, and it matches the Helm volume name. Autopilot is unaffected because CWS and the private action runner are Rejected there (feature/provider_support.go).
I tried this locally:
- Only the two Talos golden files change: the
host-osreleasevolume disappears and system-probe's mount is renamed toos-release-file. go test ./internal/controller/... ./pkg/...passes after updating the CWS and private action runner tests to the new constants.- One assertion in
process_passwd_integration_test.goneeds to skip system-probe. The check that the process features add no os-release mount now sees theos-release-filemount that CWS adds to system-probe.
| if !assert.NoError(c, json.Unmarshal([]byte(stdout), &status)) { | ||
| return | ||
| } | ||
| assert.Equal(c, strings.TrimSpace(hostID), status.HostInfo.Platform, "platform from /host/etc/os-release") |
There was a problem hiding this comment.
Nit: this compares the raw ID from the node's os-release with the Agent's hostinfo.platform, but the two aren't always the same string.
Only os-release is mounted under /host/etc. There is no debian_version, redhat-release, system-release and so on, so gopsutil uses its os-release branch. That branch returns ID unchanged except for one rename: amzn → amazon (gopsutil v4.26.9, internal/common/common_linux.go:328). On an Amazon Linux node (AL2 or AL2023), this assertion compares amzn with amazon and fails even though the mount works.
The k8s_suite only runs on kind today, where nodes are Debian (ID=debian), so it passes. It would break if the suite were reused on EKS nodes. You could apply the same amzn → amazon mapping before comparing. Or assert what the test is really after: that the platform comes from the node and not from the Agent image, i.e. it differs from the image's own os-release ID.
| // falls back to image-local users, and a 7.86+ Agent given /etc/passwd behaves as before this change. | ||
| func ProcessPasswdMountPath(dda metav1.Object) string { | ||
| // Windows does not use the Unix host passwd resolver. | ||
| if dda != nil && dda.GetAnnotations()[kubernetes.ProviderAnnotationKey] == kubernetes.WindowsProvider { |
There was a problem hiding this comment.
Nit: this branch never has an effect. ApplyWindowsPodTransformation (component/agent/windows.go:633) already drops every mount whose path starts with /, so on Windows the passwd mount is removed whichever path is returned here. It's fine to keep as documentation, but you could also drop it and its test cases.
Changes
/etc/passwdread-only at/host/etc/passwdfor process collection on Agent >= 7.86.0 (including 7.86.0+ prereleases, matching other-0minimum versions), preserving image-local accounts for IPC. Like other version-gated features, the version comes from the node Agent image override (spec.override.nodeAgent.image) or the Operator's default Agent version (also used when the override sets no image name or tag, for example onlypullSecrets); older versions, tags without a full X.Y.Z version (7,latest), custom tags, and digest-only images retain/etc/passwd. Original agent implementation pr [CXP-3881] Resolve process users from host passwd datadog-agent#55789passwdvolume (hostPath: /etc/passwd), and security-agent/system-probe still mount it at/etc/passwd. No new passwd volume is added. GKE Autopilot will need a separately namedhost-passwdvolume because Warden matches allowlisted volumeMounts by name; that comes with the Autopilot follow-up. Talos stripspasswdandos-release-file./etc/passwd, and the WorkloadAllowlist synchronizer is unchanged (defaultv1.0.7). Enabling Autopilot (v1.0.8 default, syncing v1.0.7 during rollout, allowlist-pin handling) moves to a follow-up PR here [CXP-3978][operator] Mount host passwd at /host/etc/passwd on GKE Autopilot #3766 once Google publishes v1.0.8 here: https://gke-ap-allowlist-review.googlesource.com/c/Datadog/+/20280/etc/os-releaseread-only at/host/etc/os-release(volumeos-release-file) in the core agent and process-agent, from the node Agent's default volumes, like the Helm chart. The Agent setsHOST_ETC=/host/etcwhenever that directory exists (which the relocated passwd now creates) and then reads the host OS from there. Every GKE WorkloadAllowlist version (v1.0.0 to v1.0.8) already allows it, and the Windows transform strips it with the other Linux hostPaths. User-visible effects on Operator upgrade: the node Agent DaemonSet rolls once, and host metadata reports the node's OS instead of the Agent image's OS on every Agent version. More details on this in this comment here: [CXP-3978] Migrate Operator process passwd mount + mount/etc/os-releaseby default #3645 (comment)k8s_suitee2e subtest for the new mount, and a GKE Autopilot e2e step checking that a 7.86 Agent keeps/etc/passwd.Validation
go test ./internal/controller/datadogagent/...,go vet, gofmt, andgo vet -tags=e2eon the e2e suite pass; docs regenerated.E2e tests were updated + added as well.
Runtime check on local kind (v0.29.0, Kubernetes 1.33.1) with a placeholder API key. Before installing the Agent, a user that exists only in the node's passwd (
cxp-host-user, uid 31001) starts asleepon the node. On 7.86 the user is missing from the container's/etc/passwd, so the process check resolving it proves usernames come from/host/etc/passwd. Outputs are trimmed; theagentcontainer never turns Ready with the placeholder key, so checks ran once the pod was Running. The Operator runs from this branch.dda.yamldd-agentin containercxp-host-userin containerpasswd: agent/etc/passwdcxp-host-userpasswd: agent/host/etc/passwd;os-release-file: agent/host/etc/os-releasecxp-host-userNo
host-passwdvolume is rendered.With the default os-release mount (re-run on kind at ceff214):
/etc/passwd/host/etc/passwdagent.datadoghq.com/cnm-direct-send-enabled: "false"(separate process-agent)/host/etc/passwd/etc/passwdThe host-only user's process resolved to
cxp-host-userin whichever container runs live process, and no case renderedhost-passwdor an unmounted volume.Output: a) 7.85.0
Output: b) 7.86.0-rc.6
Output:
agent statusprocess section (b)Why one volume works
Plain Kubernetes accepts one hostPath volume mounted at two paths, so 7.86+ process containers reuse
passwdat/host/etc/passwdwhile system-probe/security-agent keep it at/etc/passwd. Only GKE Autopilot's Warden needs a separately named volume, which comes with the Autopilot follow-up.passwd-reuse.yamlOutput: volume reuse
For the Autopilot follow-up: GKE Autopilot (sandbox, GKE 1.37) against allowlist v1.0.8 admitted Agent 7.86.0-rc.2 at
/host/etc/passwdfrom ahost-passwdvolume; Agent 7.85.0 and av1.0.7allowlist pin kept/etc/passwd. Commands are in ddoghq/datadog-gke-workload-allowlist#29.Merge
/etc/passwdin this PR. The Autopilot follow-up depends on Google publishing allowlist v1.0.8 (PR #29).Known limitation
/host/etc/passwdfalls back to image-local users, and a 7.86+ Agent with/etc/passwdbehaves as before this change).Note: existing os-release reporting issue
The Operator has never mounted the host's
/etc/os-releasein the agent container, so without a/host/etcmount the Agent reads the Agent image's/etc/os-releaseand reports that OS (for exampleubuntu 24.04) in host metadata instead of the node's. We have seen Operator-deployed Agents on Amazon Linux 2023 nodes reportubuntu 24.04while Helm-deployed Agents on the same OS reportamazon 2023. Mounting it by default (above) fixes this for every Agent version, matching Helm. More details in this comment here: #3645 (comment)🤖 Generated with Claude Code