Skip to content

Harden config map value handling in custom prometheus config generation - #1766

Open
suyadav1 wants to merge 2 commits into
ci_prodfrom
fix/prom-customconfig-toml-injection
Open

Harden config map value handling in custom prometheus config generation#1766
suyadav1 wants to merge 2 commits into
ci_prodfrom
fix/prom-customconfig-toml-injection

Conversation

@suyadav1

@suyadav1 suyadav1 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Values from the prometheus_data_collection_settings section of the container-azm-ms-agentconfig config map were interpolated into the generated telegraf configuration without escaping. A value containing a quote and a newline could terminate its own assignment and declare an additional telegraf plugin, which telegraf then executes inside a privileged agent. The interval value additionally reached the telemetry file that main.sh sources, so it could inject shell as well.

Changes

All in build/common/installer/scripts/tomlparser-prom-customconfig.rb, the single parser shared by the Linux and Windows agents.

  1. Strict interval validation, anchored with \A/\z. Line anchors (^/$) would let a payload hide on a second line; the existing helper in tomlparser-agent-config.rb has exactly that flaw, so it was deliberately not reused. Accepted units are the intersection of the shipped Linux (1.39.x) and Windows (1.24.x) telegraf versions: ns, us/µs, ms, s, m, h, including fractional (1.5s) and compound (1h30m) forms. d is excluded because the Windows version rejects it. Zero and negative durations are rejected.
  2. TOML serialization of every dynamic value. Validating interval alone was not sufficient: fieldpass, fielddrop, urls, kubernetes_services, the label/field selectors and the per-namespace generated plugins all used the same unescaped quote interpolation and offered equivalent breakouts.
  3. Namespace validation: monitor_kubernetes_pods_namespaces entries are validated as RFC 1123 labels. Invalid entries are skipped while valid ones still apply.
  4. Block-form gsub. With a replacement string, Ruby reinterprets \\ and \0 in the escaped value as backreferences, which would have silently undone the escaping.
  5. Rejected values are no longer echoed back into the logs.

Behaviour on invalid input

Invalid values fall back to the documented 1m default and log a warning rather than failing the agent — a typo in a config map should not stop monitoring on every node. kubernetes/container-azm-ms-agentconfig.yaml is updated to document the fallback.

Compatibility

Valid existing configurations are unaffected. Array rendering is byte-identical (["a","b"]) and valid intervals pass through unchanged. Tests assert both.

Testing

build/common/installer/scripts/tomlparser-prom-customconfig_test.rb: 32 tests, 261 assertions, all passing. The tests run the real parser end to end against the real templates for all four sinks (replicaset, prometheus sidecar, daemonset, Windows) plus the namespace-generated plugins, parse the resulting TOML, and assert the input-plugin set is unchanged versus a benign baseline.

The tests were verified to actually catch the bug. Against the pre-fix parser they fail with:

Expected ["disk", "diskio", "exec", "file", "net", "prometheus"] to not include "exec"
telemetry line for replicaset is not an inert assignment: "[[inputs.exec]]\n"

reproducing both the telegraf sink and the shell sink. With the fix applied all 32 pass.

test/unit-tests/test_driver.rb now also globs build/common/installer/scripts/*_test.rb, which previously had no coverage.

Follow-ups, intentionally not in this PR

  • Windows startup ignores the telegraf --test exit code (kubernetes/windows/main.ps1:976-978) and starts the service regardless. Note that --test is not a security boundary: on Windows it runs unfiltered and can itself execute an injected input plugin.
  • Sibling parsers still write config map values into shell that main.sh sources: tomlparser.rb, tomlparser-common-agent-config.rb, tomlparser-geneva-config.rb, tomlparser-metric-collection-config.rb. tomlparser-osm-config.rb interpolates namespaces into TOML. Config map to node RCE should not be considered fully closed until these are addressed or separately tracked.
  • Pre-existing and unrelated: @defaultRsMonitorPods is commented out (line 25), so an unset monitor_kubernetes_pods on the replicaset emits monitor_kubernetes_pods = , which is invalid TOML. Left alone deliberately, since changing it would newly enable a currently-broken path.
  • No ReleaseNotes.md entry: entries there are tied to an image version bump and belong with the release PR.

CI

These are the first ruby tests to require tomlrb, so .github/workflows/run_unit_tests.yml now installs it, pinned to 2.0.1 to match the version the agent ships. The full ruby suite passes: 34 runs, 278 assertions, 0 failures.

Values in the prometheus_data_collection_settings section of the
container-azm-ms-agentconfig config map were interpolated into the
generated telegraf configuration without escaping. A value containing a
quote and a newline could terminate its assignment and declare an
additional telegraf plugin, which telegraf then runs inside a privileged
agent. The interval value was additionally written into the telemetry
file that main.sh sources, so it could also inject shell.

Changes:
- Validate interval as a positive telegraf duration anchored with \A and
  \z, falling back to the default when invalid. Line anchors would let a
  payload hide on a second line. The accepted unit set is the
  intersection of the linux and windows telegraf versions, so fractional
  and compound durations keep working and "d" stays unsupported.
- Serialize every config map derived scalar and array element as a
  quoted TOML basic string with structural characters escaped. This
  covers interval, fieldpass, fielddrop, urls, kubernetes_services, the
  label and field selectors, and the generated per namespace plugins.
- Validate monitor_kubernetes_pods_namespaces entries as RFC 1123 labels
  and skip entries that are not.
- Use the block form of gsub so that escape sequences in a substituted
  value are not reinterpreted as regex backreferences.
- Never echo a rejected value back into the logs.

Rejected values fall back to the documented default rather than failing
the agent, since a typo should not stop monitoring.

Adds regression tests covering the replicaset, prometheus sidecar,
daemonset and windows templates plus the namespace generated plugins,
and registers build/common/installer/scripts with the ruby test driver.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@suyadav1
suyadav1 requested a review from a team as a code owner August 18, 2026 22:52
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.


def test_toml_string_array_escapes_structural_characters
parsed = Tomlrb.parse("urls = #{toTomlStringArray(["http://ok", BREAKOUT])}")
assert_equal ["http://ok", BREAKOUT], parsed["urls"]
The new prometheus custom config tests are the first ruby tests that
require tomlrb, so the unit test workflow failed to load them. Install
the same version the agent ships (2.0.1).

That version cannot parse inline tables with quoted keys, which the
telegraf templates use for `tags = {"interface" = ["lo"]}`, so the test
helper now collapses inline table values before parsing. Injected
plugins declare their own table headers and are still detected, which
was confirmed by re-running the suite against the unpatched parser.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants