catalog / exploratory/docker.io/netdata/netdata
docker.io/netdata/netdata
exploratory pin unchecked tags:
netdata cap_add_validation — EXPLORATORY (below the validated bar; see acceptance_contract_violations). Validates netdata's officially-recommended grant (cap_add: [SYS_PTRACE, SYS_ADMIN]) against observed cap_capable syscalls, under the same representative run as the validated capabilities dimension (pid:host, host network, host /proc,/sys,docker.sock ro-mounts). Why exploratory, probably permanently: the target itself generates the event flood. apps.plugin trips a SYS_PTRACE cap_capable check for every /proc/<pid> read of every host process, every second (~44k held events per 30s), so the perf-buffer drop rate stays >=1% on any host — measured 1.9% over 310s on a 16-core desktop with in-gadget container filtering active, ~4% on the quiet csd-derive VM. In-kernel filtering cannot help when every dropped event is attributable to the target (csd#407 discussion). READ WITH the validated capabilities dimension in catalog/docker.io/netdata/netdata.yaml, which is AUTHORITATIVE for the minimum: its drop-test proves SYS_ADMIN is droppable (the image ships no ebpf.plugin, so nothing gates on it) while SYS_PTRACE is required. This observation adds the complementary fact: netdata EXERCISES SYS_ADMIN opportunistically when granted (collectors probe SYS_ADMIN-gated operations and degrade gracefully without) — "exercised" here does NOT mean "required". The two observers answer different questions; where they diverge, drop-test wins for the minimum.
Use it
services:
netdata:
image: docker.io/netdata/netdata:<tag>
Dimension: cap_add_validation
cap_add_validation
| tool | container-sec-derive 0.7.0 |
|---|---|
| observer | capadd |
| validated image | docker.io/netdata/netdata@sha256:bcc822ec685d… |
| validated date | 2026-07-16 |
| confidence | low |
| validated via | bpf-observation |
| workload | profiles/workloads/netdata.sh |
| workload sha256 | ba6ae07c95ee… |
| ig version | v0.51.0 |
Workload script (profiles/workloads/netdata.sh)
#!/usr/bin/env bash
# Correctness check for netdata cap_add bisection (docker.io/netdata/netdata).
#
# netdata is run as a host monitor — pid:host, host /proc,/sys,/var/run/docker.sock
# read-only mounts, host network, and the granted cap set present — then each
# candidate capability is dropped, the container restarted, and this correctness check
# decides "still works CORRECTLY". Correctness is not just liveness: netdata must
# still drop root -> the unprivileged `netdata` user AND still collect per-process
# metrics for non-root processes, or the derivation is wrong.
#
# Required env: NDCONTAINER (target container name or id).
# Exit 0 = correct; non-zero = the dropped capability was required.
set -euo pipefail
: "${NDCONTAINER:?NDCONTAINER must be set}"
API=http://localhost:19999
# chart_nonzero <chart-id> -> 0 if the latest sample has a NON-ZERO value.
# Non-zero, not merely non-null: apps.plugin denied access to a process's /proc
# reports the metric as 0 (not null), so a non-null check passes on broken
# collection. A live process always has open fds > 0.
chart_nonzero() {
curl -fsS --max-time 4 "${API}/api/v1/data?chart=$1&after=-20&points=1&format=json" 2>/dev/null \
| python3 -c 'import sys,json
try:
rows=json.load(sys.stdin).get("data",[])
sys.exit(0 if [v for r in rows for v in r[1:] if v is not None and v != 0] else 1)
except Exception:
sys.exit(1)'
}
# 1. Healthy within the container's own healthcheck window.
deadline=$((SECONDS + 75))
until [ "$(docker inspect -f '{{.State.Health.Status}}' "$NDCONTAINER" 2>/dev/null)" = healthy ]; do
[ "$(docker inspect -f '{{.State.Status}}' "$NDCONTAINER" 2>/dev/null)" != running ] && { echo "not running"; exit 1; }
(( SECONDS >= deadline )) && { echo "did not become healthy"; exit 1; }
sleep 3
done
# 2. HTTP API up and apps.plugin collecting.
curl -fsS --max-time 5 "${API}/api/v1/info" >/dev/null || { echo "API down"; exit 1; }
# capture then grep — piping the (large) charts dump into `grep -q` can SIGPIPE
# curl and, under pipefail, mark the pipeline failed despite a match.
charts="$(curl -fsS --max-time 5 "${API}/api/v1/charts")" || { echo "charts API down"; exit 1; }
grep -q '"apps\.' <<<"$charts" || { echo "apps.plugin not collecting"; exit 1; }
# 3. CORRECTNESS: the netdata daemon dropped privileges (must NOT be uid 0/root).
pid="$(docker inspect -f '{{.State.Pid}}' "$NDCONTAINER")"
uid="$(awk '/^Uid:/{print $2}' /proc/"$pid"/status)"
[ "$uid" != 0 ] || { echo "daemon is running as ROOT (privilege drop failed)"; exit 1; }
# 4. CORRECTNESS: per-process io/fd metrics are actually COLLECTED (non-zero).
# apps.plugin runs real-uid = the netdata user (setuid gives euid 0, not real uid
# 0), so it needs CAP_SYS_PTRACE to read ANY process's /proc/<pid>/io|fd; without
# it, it reports every per-process metric as 0 (not null). So require a per-process
# fds_open to be NON-ZERO — dropping SYS_PTRACE zeros it. (A prior non-null check
# passed on all-zeros and wrongly derived SYS_PTRACE removable, regressing prod.)
ppok=0
for _ in 1 2 3 4 5 6 7 8; do
for ch in app.dockerd_fds_open app.netdata_fds_open app.systemd-journald_fds_open app.containerd_fds_open; do
if chart_nonzero "$ch"; then ppok=1; PPCH="$ch"; break 2; fi
done
sleep 3
done
[ "$ppok" = 1 ] || { echo "per-process metrics all zero — apps.plugin can't read /proc/<pid>/fd (SYS_PTRACE gap)"; exit 1; }
echo "netdata correct: healthy, API up, daemon uid=$uid (non-root), per-process metrics collecting non-zero ($PPCH)"
Dimension: capabilities
Not yet derived — this dimension has not been drop-tested for this image. Absence means not tested, not that a minimum here is infeasible.
Dimension: filesystem
Not yet derived — this dimension has not been drop-tested for this image. Absence means not tested, not that a minimum here is infeasible.
Evidence & provenance
- Profile source in the repository
- Version history — every previously published pin, with the full profile as validated against it
Raw profile YAML
# netdata cap_add_validation — EXPLORATORY (below the validated bar; see
# acceptance_contract_violations). Validates netdata's officially-recommended
# grant (cap_add: [SYS_PTRACE, SYS_ADMIN]) against observed cap_capable
# syscalls, under the same representative run as the validated capabilities
# dimension (pid:host, host network, host /proc,/sys,docker.sock ro-mounts).
#
# Why exploratory, probably permanently: the target itself generates the
# event flood. apps.plugin trips a SYS_PTRACE cap_capable check for every
# /proc/<pid> read of every host process, every second (~44k held events per
# 30s), so the perf-buffer drop rate stays >=1% on any host — measured 1.9%
# over 310s on a 16-core desktop with in-gadget container filtering active,
# ~4% on the quiet csd-derive VM. In-kernel filtering cannot help when every
# dropped event is attributable to the target (csd#407 discussion).
#
# READ WITH the validated capabilities dimension in
# catalog/docker.io/netdata/netdata.yaml, which is AUTHORITATIVE for the
# minimum: its drop-test proves SYS_ADMIN is droppable (the image ships no
# ebpf.plugin, so nothing gates on it) while SYS_PTRACE is required. This
# observation adds the complementary fact: netdata EXERCISES SYS_ADMIN
# opportunistically when granted (collectors probe SYS_ADMIN-gated
# operations and degrade gracefully without) — "exercised" here does NOT
# mean "required". The two observers answer different questions; where they
# diverge, drop-test wins for the minimum.
schema_version: "1.5"
image: docker.io/netdata/netdata
reference_url: https://tmatens.github.io/container-security-profiles/profiles/exploratory/docker.io/netdata/netdata.html
status: exploratory
acceptance_contract_violations:
- confidence.level "low" below moderate (see confidence.factors)
- trace_health.drop_rate 1.8663 >= 0.01
dimensions:
cap_add_validation:
recommended_cap_add: [SYS_ADMIN, SYS_PTRACE]
validated_caps: [SYS_ADMIN, SYS_PTRACE]
derivation:
tool: container-sec-derive
tool_version: "0.7.0"
sidecar_schema_version: "1.6"
observer: capadd
validated_image: docker.io/netdata/netdata@sha256:bcc822ec685dd7ee488e9f99b957f479b66301be04f13f346df692ad7de94151
validated_date: "2026-07-16"
duration_seconds: 310
confidence: low
workload: profiles/workloads/netdata.sh
workload_sha256: "ba6ae07c95eed30155baff45933b0ba9c171694a294539b08119360fc31180f6"
validated_via: [bpf-observation]
observation_backend:
ig_version: v0.51.0
gadgets: []