Skip to content

[procmgr] Secret backend resolution for config gates - #54734

Open
jose-manuel-almaza wants to merge 4 commits into
jose/procmgr-config-gatesfrom
jose/procmgr-secret-backend-gates
Open

[procmgr] Secret backend resolution for config gates#54734
jose-manuel-almaza wants to merge 4 commits into
jose/procmgr-config-gatesfrom
jose/procmgr-secret-backend-gates

Conversation

@jose-manuel-almaza

Copy link
Copy Markdown
Contributor

What does this PR do?

Resolves ENC[...] values during config gate evaluation so dd-procmgr matches Agent secret handling.

Adds:

  • config_gate/secrets.rs: resolve handles via secret_backend_command, native secret_backend_type, and multi_secret_backends (same precedence as the core Agent)
  • Platform secret backend runners (Windows CreateProcessAsUserW under the Agent account; Unix setuid when procmgr runs as root for Privileged children)
  • secret_backend_exec.rs: shared spawn, timeout, stdout drain, and response parsing
  • Windows ACL validation on secret backend executables before spawn
  • Agent config precedence for backend settings: DD_SECRET_BACKEND_* env (including core Agent SCM Environment on Windows) over datadog.yaml
  • Manager reload: invalidate secret caches when config changes

Fleet policy ENC[...] values stay unresolved here, matching Agent MergeFleetPolicy running after secret resolution.

Stack context: PR 3/4 split from #53249. Builds on #54732 (jose/procmgr-config-gates), which in turn builds on #54731. Process-agent dual-mode integration lands in PR 4 (jose/procmgr-windows-process-agent).

Motivation

PR 2 config gates read YAML, env, and fleet policy, but many customers gate features with secret-backed settings (ENC[api_key], secret-backed booleans, etc.). Without secret resolution, gates would mis-evaluate and auto-start behavior would diverge from the Agent.

Describe how you validated your changes

  • Rust unit tests in pkg/procmgr/rust/src/config_gate/secrets.rs and config gate integration tests (serialized env to avoid cross-test leakage)
  • Windows procmgr Rust build/tests in CI
  • Linux CI: secret-backend tests run under the agent service user where required

Additional Notes

  • Secret backends always run as the core Agent service account, not as the procmgr supervisor (LocalSystem) or a Privileged managed child.
  • No agent startup, fleet installer, or legacy SCM suppression changes in this PR.
  • Invokes secret-generic-connector when no custom secret_backend_command is configured.

@jose-manuel-almaza
jose-manuel-almaza requested a review from a team as a code owner August 11, 2026 15:56
@jose-manuel-almaza jose-manuel-almaza added the changelog/no-changelog No changelog entry needed label Aug 11, 2026
@jose-manuel-almaza jose-manuel-almaza added the qa/done QA done before merge and regressions are covered by tests label Aug 11, 2026
@dd-octo-sts dd-octo-sts Bot added internal Identify a non-fork PR team/agent-runtimes labels Aug 11, 2026
@github-actions github-actions Bot added the long review PR is complex, plan time to review it label Aug 11, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa9c11ac11

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +670 to +672
let resolved = secrets::resolve_config_string(text, agent_yaml);
if secrets::is_enc(&resolved) {
return None;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Treat unresolved ENC values as blocking

When a configured backend is unavailable, times out, or omits a handle, returning None here removes that source from consideration, so best_config_layer can select a lower-priority fleet, environment, YAML, or default value. For example, an unresolved DD_PROCESS_CONFIG_PROCESS_COLLECTION_ENABLED=ENC[...] with true in the YAML starts the process, whereas the core Agent returns an error from LoadDatadog when any pre-fleet secret cannot be decrypted. Preserve the unresolved source as a blocking result instead of falling through, or procmgr can auto-start services while the Agent rejects the same configuration.

Useful? React with 👍 / 👎.

Comment on lines +86 to +89
let wait_result = wait_for_child();
let output = reader
.join()
.map_err(|_| anyhow::anyhow!("secret backend stdout reader panicked"))??;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Terminate descendants before joining the stdout reader

If a backend forks a child that inherits stdout, killing or observing the direct backend process does not close the pipe, yet this code unconditionally joins the reader. A backend such as a shell script that launches a background helper can therefore hang config-gate evaluation indefinitely—even after the configured timeout—because both platform runners terminate only the direct process. Run the backend in a killable process group/job and terminate all descendants, or otherwise bound the reader join.

Useful? React with 👍 / 👎.

Comment on lines +73 to +75
unsafe fn drop_to_agent_user(uid: Uid, gid: Gid) -> std::io::Result<()> {
nix::unistd::setgid(gid).map_err(io_error)?;
nix::unistd::setuid(uid).map_err(io_error)?;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear supplementary groups before dropping privileges

When the Unix supervisor runs as root, setgid followed by setuid leaves the supervisor's supplementary groups intact; a typical root service can therefore execute the secret backend as UID dd-agent while retaining membership in privileged groups such as GID 0. Clear or initialize supplementary groups for dd-agent before changing GID/UID so the backend actually runs with the Agent account's privileges.

Useful? React with 👍 / 👎.

Comment on lines +28 to +30
timeout: std::time::Duration,
max_output_bytes: usize,
_skip_acl_check: bool,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate custom secret backend permissions on Unix

On Unix, the ACL-check parameter is ignored and every custom secret_backend_command is executed directly. The core Agent's filesystem.CheckRights rejects executables with group/other permissions unless secret_backend_command_allow_group_exec_perm permits the restricted group case; without the equivalent check, procmgr will execute a world-writable backend that the Agent refuses, allowing a local user to run code as dd-agent and potentially open config gates.

Useful? React with 👍 / 👎.

@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 52.43% (-0.01%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 01af0ac | Docs | Datadog PR Page | Give us feedback!

@datadog-datadog-prod-us1-2 datadog-datadog-prod-us1-2 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datadog Autotest: FAIL

Empty secret-backend values are accepted as resolved secrets, so they receive higher priority than Fleet policy and can suppress a valid Fleet-enabled gate. The core Agent rejects empty resolved values; this path should preserve the unresolved value instead of promoting an empty string.

View proposed fix
Open Bits AI session

🤖 Datadog Autotest · Commit fa9c11a · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

Comment on lines +415 to +418
entry
.get("value")
.and_then(Value::as_str)
.map(|value| normalize_secret_value(value, remove_trailing_line_break))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Empty secret values incorrectly override Fleet policy

Customers using an empty or newline-only secret can have Fleet-enabled process-manager features silently not start.

Assertion details
  • Input: A local config gate contains ENC[mode], the backend returns {"mode":{"value":""}}, and Fleet policy enables the same string-backed setting (for example infrastructure_mode: end_user_device).
  • Expected: An empty resolved secret is treated as unresolved, matching the core Agent, so the valid Fleet value remains effective.
  • Actual: parse_secret_response returns Ok("") and promote_secret_string assigns Secret priority to that empty value. The empty local secret therefore wins over the Fleet value and can disable the derived system-probe gate.
Suggested change
entry
.get("value")
.and_then(Value::as_str)
.map(|value| normalize_secret_value(value, remove_trailing_line_break))
entry
.get("value")
.and_then(Value::as_str)
.map(|value| normalize_secret_value(value, remove_trailing_line_break))
.filter(|value| !value.is_empty())
.with_context(|| format!("secret backend response missing value for {handle}"))

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest

@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from fa9c11a to 4f70c7b Compare August 12, 2026 11:26
@jose-manuel-almaza
jose-manuel-almaza requested review from a team and removed request for a team and dd-valdugay August 12, 2026 11:31
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from b11d137 to f8f5bdb Compare August 13, 2026 06:12
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from 919ce8d to 59cf619 Compare August 13, 2026 10:23
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch 2 times, most recently from ac7bfde to 7153cf9 Compare August 14, 2026 09:39
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from 7153cf9 to d57c842 Compare August 14, 2026 10:28
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from d57c842 to 8ec8ebc Compare August 14, 2026 10:46
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from 8ec8ebc to 805a702 Compare August 14, 2026 11:01
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from 805a702 to d8b67e1 Compare August 14, 2026 11:20
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch 2 times, most recently from 8b0a427 to 02e8ade Compare August 14, 2026 14:16
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from 02e8ade to 13aa0d1 Compare August 14, 2026 14:18
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch 2 times, most recently from df88247 to d554bba Compare August 17, 2026 08:53
Move writeProcessesDYamlContent and process-owner helpers here where the
agent-profile E2E tests use them, after dropping them from spawn-profiles.
Resolve ENC[...] gate values via secret_backend_command and native backends,
matching Agent precedence and spawning backends under the core Agent account.
Invalidate secret-backend and Windows SCM env caches before reloading
processes.d so config gates re-evaluate with fresh values.
@jose-manuel-almaza
jose-manuel-almaza force-pushed the jose/procmgr-secret-backend-gates branch from d554bba to 01af0ac Compare August 17, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant