0.18.1 #1474
mickem
announced in
Announcements
0.18.1
#1474
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Security hardening and bugfixes of monitoring clients
0.18.1 is a security and correctness release for the passive/outbound side of
the agent. A review pass over the client modules — Icinga, NRDP, NSCA, NSCA-NG,
check_mk, Elastic, Graphite, syslog, and a second round on SMTP — turned up the
same three shapes over and over: configuration that parsed fine and was then
thrown away, network operations that could never time out, and
attacker-influenced text reaching another system's log unscrubbed. The
external-script launcher and the filter framework got the same treatment.
The most consequential single item is NSCA-NG cert mode, which applied its TLS
configuration too late and therefore accepted any server certificate. Alongside
the hardening, the Elastic module can finally talk to a current Elasticsearch,
the web UI moves to MUI 9 / react-router 8 with its test suites wired into CI,
and a coverage sweep adds unit tests to every source file that was under 50%.
✨ Highlights
certificate. The OpenSSL context was configured after the TLS stream was
created from it, and
SSL_new()copies the verify mode, certificate, cipherlist and version bounds at creation time. So
verify mode = peer-certranwith verification off, any certificate was accepted, and the configured client
certificate was never sent. The default PSK mode was never affected. (fix: NSCA-NG cert mode never applied its TLS config (peer verification silently off) #1461)
targets discarded every TLS key (
use ssl,certificate,verify mode, …)and connected in plaintext regardless; syslog targets never read
severity,facility,tag_syntaxormessage_syntax;NSCAServer'sperformance data = falsewas ignored; andext-scr install --arguments=…wrote its lockdown to a key the module does not read. All four are fixed.
Icinga, NRDP, Graphite and Elastic submissions all ran with no deadline; each
is now bounded by the target's
timeout(default 30 s) as a single budgetover resolve, connect, handshake and exchange. External scripts enforce their
timeoutby wall clock on both platforms. (NRDP client security review: enforce timeouts/retries, cap responses, fix the shared TLS "1.2+" floor #1464, GraphiteClient: scrub status path against metric-line injection #1465, IcingaClient: security-review hardening (trace redaction, timeouts, verify-mode warning) #1466, GraphiteClient: fix metric injection and enforce submission timeout #1467, CheckExternalScripts security-review hardening #1468,ElasticClient: add authentication, TLS verification, and modernize #1453)
Verification was hardcoded off; it now defaults to
peerwithtls version,verify modeandcasettings, plus newuser/passwordandapi keyauthentication. The legacy
_typeparameter is no longer sent by default, andevery document in a bulk request gets its own
_id— previously they sharedone and overwrote each other. (ElasticClient: add authentication, TLS verification, and modernize #1453)
tls version = 1.2+means "1.2 or later" again. The+was strippedand the value mapped onto a version-pinned method, which pins the maximum
too — so the common default negotiated TLS 1.2 only and silently excluded
TLS 1.3. Fixed in the shared stack: all HTTP-based clients, the NRPE/NSCA
socket clients and servers, and
check_tcp. (NRDP client security review: enforce timeouts/retries, cap responses, fix the shared TLS "1.2+" floor #1464)receiver stops promoting the tag to origin host — which meant check output
could choose which host a record was filed under. An unknown
severityorfacilitynow degrades to<13>(user.notice) instead of<0>,kernel.emergency. (Syslog client security-review hardening #1470)
The parser and the AST evaluator both recurse with the shape of the input, so
a long or deeply nested expression could exhaust the stack and crash the
agent. Real filters sit an order of magnitude below the limits. (Add expression length and nesting-depth limits to filter parser #1469)
vitest unit tests and 18 Playwright integration tests now running in CI on
every build. (Update dependencies and migrate MUI v7 to v9 with API changes #1459)
🔍 Detailed changes
🔐 NSCA-NG — cert mode applies TLS configuration before the stream exists
use psk = falsetargets built thessl::context, created the connection fromit, and then set the verify mode, client certificate, cipher list and TLS
version bounds. OpenSSL copies all of that out of the context when the stream is
created, so none of it took effect:
verify mode = peer-certaccepted aman-in-the-middle's certificate, and a server asking for a client certificate
never got one. Configuration is applied first now.
Two visible consequences: a cert-mode target whose server certificate does not
chain to the configured
ca(or does not match the host name) will now fail toconnect — that is the verification working — and servers requiring a client
certificate will start receiving it. The default PSK mode authenticates both
ends through the pre-shared key and is unaffected.
🧾 Settings that were read but never applied
CheckMKClientuse ssl,certificate,certificate key,ca,allowed ciphers,verify mode,dhregister_all()/notify(), so the keys were parsed and thrown away — the client connected in plaintext whatever the configuration said, and the keys were missing from the reference docs.SyslogClientseverity,facility,tag_syntax,message_syntax,ok-severity/warning-severity/critical-severity/unknown-severityNSCAServerperformance data = falseCheckExternalScriptsext-scr install --arguments=…GraphiteClienttimeouttimeoutkey is not stored — the default 30 always won.Values you configured — possibly years ago, without effect — now apply. Review
those target sections for stale keys before upgrading.
⏱️ Operations that could never time out
Each of these ran with no deadline, so an endpoint that accepted the connection
and then went silent held the submitting thread indefinitely, quietly stopping
passive results until a service restart.
metrics flush) and Elastic are now bounded by the configured
timeout(default 30 s; 10 s for one-shot
nscp clientsubmissions) as one budgetcovering name resolution, connect, TLS handshake and the exchange. NRDP also
retries transport failures up to
retry, each attempt on a fresh connection.retryis gone. It never had any effect — the module alwaysmade exactly one attempt — and a retry loop would multiply the worst-case time
a stalled endpoint can hold a thread. It is still registered centrally for all
client modules, so it remains in the reference, but
GraphiteClientdoes notact on it. Mirrors the SMTP
retrychange in 0.18.0.popen(), which hides the child PID, sotimeout=was unenforced and a hungscript wedged a worker thread per invocation. On Windows the read loop counted
iterations rather than elapsed time, so a continuously chatty script escaped
the timeout entirely and leaked an unkillable process each run. Both launchers
now bound the wait by wall-clock deadline, with captured output capped at
8 MiB.
left the cancelled operation's completion handler queued — to be run by the
retry against the next resolved address, with references into a stack frame
that no longer existed. Handler state is heap-owned now, and a spent budget
ends the endpoint walk instead of retrying into it. (SMTP client hardening: reply limits, scrubbing, and validation #1471)
🧹 Injection, scrubbing and resource limits
${check_alias}substituted into them can come from a remote submitter, so analias carrying a newline injected an extra, attacker-chosen metric line into
Graphite (and a
;injected carbon tags) — a way to hide a real problem orfabricate one. (GraphiteClient: scrub status path against metric-line injection #1465, GraphiteClient: fix metric injection and enforce submission timeout #1467)
inbox channel: control characters are stripped from host and service names,
and a return code outside 0–3 is clamped to UNKNOWN instead of flowing on as
an arbitrary 16-bit integer. (security(nsca): fail hard on unknown encryption, warn on empty password, honour perf-data setting, validate wire fields #1460)
printable US-ASCII is replaced — the C0 controls and the C1 range
(0x80–0x9F), which carries single-byte terminal escapes such as CSI — so a
multi-line reply can no longer forge extra log lines. The reply to
STARTTLSmust be exactly
220per RFC 3207 rather than any2xx, and AUTH credentialscontaining a NUL are refused before connecting. (SMTP client hardening: reply limits, scrubbing, and validation #1471)
unbounded — a hostile server, or a man in the middle on a plain
http://target, could stream the agent out of memory), and an SMTP reply at 64 KB per
line and 100 lines. Nothing previously capped how much a peer could make the
client buffer inside its timeout window, so bytes without a line ending, or
endless
250-continuations, turned a 30-second budget into gigabytes.printed raw
password/tokenvalues; the fix is in the shared clientmachinery, so every outbound client module is covered. (IcingaClient: security-review hardening (trace redaction, timeouts, verify-mode warning) #1466)
httpssubmission whoseverify moderesolves to no peer verification logs a message naming theendpoint. An empty NSCA
passwordwith encryption enabled logs an error onboth ends — the key is the password zero-padded with no derivation step, so an
empty one is a well-known all-zero key.
🔎 ElasticClient — verified, authenticated, and Elasticsearch 8 compatible
tls version1.2+https://addressesverify modepeerca${ca-path}user/passwordapi keytimeout30event type,metrics type,nsclient log type_type; set explicitly on ES 6.x or olderBeyond the TLS and auth work: every document in a bulk request now gets its own
_id, so multi-line events show up completely instead of overwriting each otherdown to a single entry; responses are parsed defensively and non-2xx statuses
are reported instead of ignored; a timestamp from one event line no longer leaks
into later lines; and events are refused after
unloadModule. (#1453)📨 SyslogClient — a well-formed datagram, and options that reach the wire
Datagrams now read
<PRI>TIMESTAMP HOSTNAME TAG MESSAGE. Thehostnamesettingunder
[/settings/syslog/client]— until now read but never used — fills theHOSTNAME field (default
auto, the machine name). Receivers that promoted thetag (default
NSCA) to origin host will now file records under the real hostname, so adjust any log-parsing rule keyed on the old, hostname-less format. An
IPv6 hostname is kept intact.
tag_syntax,message_syntaxand the per-state severity options take effectfor the first time; an unknown
severity/facilityfalls back to<13>instead of
<0>; and all C0 control bytes and DEL in the outgoing line arereplaced with spaces (previously only CR, LF and NUL), so check output cannot
smuggle ANSI escape sequences into the receiver's log. (#1470)
🧱 Filter framework — bounded expression length and depth
A
filter/warning/criticalexpression — and a%(...)expressionplaceholder inside a syntax template — longer than 1024 characters or nested
more than 64 parentheses deep is rejected at parse time with a clear
"exceeds the maximum length/depth" error. Both the recursive-descent parser and
the AST evaluator recurse with the shape of the input, so an unbounded or deeply
nested expression could exhaust the thread stack and crash the whole agent —
reachable by anyone able to influence a filter string over the authenticated
REST API, or over NRPE with
allow arguments = true. String literals are exemptfrom the depth count, and real filters are a small fraction of both limits.
(#1469)
🐚 CheckExternalScripts — sandbox, arguments and the shell fallback
Beyond the timeout work above: the
show/deletesandbox resolves symlinksbefore its containment test (previously a symlink inside the script root
pointing outside it let an authenticated admin read or remove files anywhere the
service account could reach);
%and^are refused on the Windows shellfallback (cmd.exe
%VAR%expansion and its escape character, opt-out viaallow nasty characters);add argumentsis honoured andlist --include-libworks; and a null-provider dereference and the
help-pbargument numbering arefixed. The docs now warn that write access to any
script pathdirectory isequivalent to code execution as the service account, and clarify that
allow argumentsdoes not gate aliases. (#1468)🖥️ Web UI — dependency modernization
Every dependency in
web/package.jsonmoves to its latest release: MUI(material, icons, x-charts) 7/8 → 9, react-router 7 → 8, eslint 9 → 10,
TypeScript 5.9 → 6.0, plus minor bumps for react, redux, zod, vite and vitest.
The UI is adapted to the MUI 9 breaking changes — removed system props moved
into
sx, renamed outlined icons, thecontainedPrimaryshadow expressed as atheme variant, and
Autocomplete'srenderInputparams now exposingslotProps.input.Both web suites are now part of the CI build (
build-web.yml, Node raised to 22for react-router 8): 65 vitest unit tests and 18 Playwright integration tests
driving the built bundle in a real Chromium. The e2e preview server binds to
127.0.0.1. (#1459)
🧪 Tests and coverage
A sweep of the gcovr reports added unit tests to every source file under 50%
combined line coverage that can be exercised deterministically — roughly 4,000
lines of new test code across the check_mk wire protocol,
pid_file, the compathelpers,
execute_process_unix, the NRDP/NSCA-ng/NSCP client handlers, Icingatarget objects, the CheckDisk file filter,
perf_filter, the where-engineevaluation context, the external-scripts provider, CheckSystemUnix network /
service / cpu-frequency, the simple file logger, the zip plugin, onboarding's
chown_subtreeand the settings proxy. New integration suites coverCheckExternalScripts commands, Elastic submission and NSCA-NG cert mode.
check_cpu_frequencywas refactored to take a sysfs base path so a fixture treecan drive it; production behaviour is unchanged.
🐛 Bug fixes
NSClientServer:check_ntinstance listing could crash the serving I/Othread.
list_instance()advanced a tokenizer iterator under a guard thatwas always true, making the invalid-line branch dead and dereferencing
tok.end()on any line with fewer than three comma-separated fields — which afailed PDH enumeration produces (
ERROR: …). It now advances to the thirdfield explicitly and logs genuinely malformed lines. (Fix iterator safety in NSClientServer::list_instance #1463)
nsca-ng.cfgusedcommandinstead of
command_file(so it did not parse) and carried anauthorizeblock with only a password, which nsca-ng treats as authorizing nothing — the
PSK handshake succeeds and every submission is rejected with FAIL. The example
now has anchored
hosts/servicespatterns, a Common Gotchas entry for thatsymptom, a danger admonition against wildcard
commandspatterns, andguidance on PSK entropy and not passing
--passwordon the command line.(docs: fix the NSCA-NG server example and add authorization/PSK security guidance #1462)
(
elastic_handler.hpp, the unused channel/command machinery and theclient-parser dependency).
execute_process_w32usesGetTickCountfor XP-toolset compatibility.use psk = false. A target whose server certificate does not chain to theconfigured
ca, or does not match the host name, will start failing toconnect — fix the certificate, or accept the exposure explicitly with
insecure = true. Servers requiring a client certificate will now receiveone. PSK mode is unaffected.
use ssl = truenow really negotiate TLS. Aplaintext-only server end will start failing — loudly, which is the point.
encryptionvalue is a hard error. A typo(
aes-256) or an algorithm not compiled into the build used to fall back tono encryption on the end carrying it. The
NSCAServermodule now refuses toload and an
NSCAClientsubmission fails, each naming the availablealgorithms. Breaking only for setups relying on that fallback — including
builds compiled without crypto++, where every cipher name degraded to
plaintext. Fix the name, or set
encryption = noneif plaintext was intended.Default installs (
aes256) are unaffected.passwordwith encryption enabled now logs an error onboth ends. The password is the key, so an empty one is a well-known key —
set the same real password on both ends.
NSCAServer'sperformance data = falseis honoured again. If you reliedon it while it was broken, perfdata really is dropped now.
httpsverifies certificates. Pointcaat yourself-signed certificate, or set
verify mode = noneto keep the oldbehaviour. On Elasticsearch 6.x or older, set
event type,metrics typeandnsclient log typeexplicitly — the legacy_typeparameter is no longersent by default.
set on a
[/settings/syslog/client/targets/…]section were never read, so thebuilt-in defaults always won. Review those sections for stale keys. Receivers
whose parsing rules keyed on the old hostname-less datagram need adjusting —
records now arrive attributed to the agent's host name instead of the tag.
Syslog remains cleartext and unauthenticated: keep the path to the server on a
trusted segment.
unresponsive Icinga, NRDP, Graphite or Elastic endpoint gives up after
timeout(default 30 s) — raise it on that target if the endpoint islegitimately slower, or set
timeout = 0on an Icinga target if you depend onthe old unbounded wait. Graphite's
timeoutis now a budget for the wholesubmission, so a target that only completed by quietly taking longer will fail
at the configured value.
retryis no longer read. The module always made oneattempt; the setting still appears in the reference (it is registered for all
client modules centrally) but has no effect.
tls versionwith a trailing+means "that version or later".1.2+previously negotiated TLS 1.2 only; it now also permits TLS 1.3, and
anyisaccepted as documented. This applies to the HTTP-based clients, the NRPE/NSCA
clients and servers, and
check_tcp. Pin an exact version(
tls version = 1.2) if a peer misbehaves when TLS 1.3 is offered.ext-scr installafter upgrading so anargument lockdown lands on the setting the module actually reads. The default
install is unaffected (arguments are off by default). Treat write access to
any
script pathdirectory, and the ability to configure external-scriptcommands, as equivalent to code execution as the service account.
rejected. Every normal configuration is far below both limits; only a
pathologically large or deeply nested expression is refused.
passwordandtokenvalues are masked in the target dump at log level
trace, across everyoutbound client module.
Full detail on the security items lives in Security notices; the operator
actions are mirrored on Upgrading.
Full Changelog: 0.18.0...0.18.1
This discussion was created from the release 0.18.1.
All reactions