Skip to content

Fixed some exotic passive checks, and security hardening

Most of 0.18.0 is about things that reported success while doing nothing. check_and_forward built its submission in a form no channel could read, check_nscp had counted zero crash reports since 0.4.2, and a module enabled by a fleet bundle was never loaded until the service restarted โ€” all three are fixed. Security reviews of the SMTP, NRPE, NRDP/NSCA and WEB modules landed alongside them, and the bundled OpenSSL moves to 3.5.8.

It also adds run_schedules for submitting a passive result without waiting out the interval, lets the MSI install your own TLS certificates, and gives real-time filters a way to prime their destination at startup.

โœจ Highlights

  • ๐Ÿ“ค check_and_forward submits again. The command ran the check, answered Message submitted and delivered nothing: the submission was built as a query message, which no channel can read. NSCA, NRDP, Graphite and every other client module were equally affected. It also gained channel, alias, destination and source. (#1452)
  • ๐Ÿ“… New: run_schedules. Run the configured schedules now instead of waiting out their interval, and submit the results on their normal channel โ€” nscp client --boot --query run_schedules, optionally --argument schedule=<alias>. Works over NRPE, REST and nscp test too. (#1450, #1452)
  • ๐Ÿ’ฅ check_nscp is a filter check, and its crash count works. It has read 0 crashes since 0.4.2 (it matched the extension txt against .txt), and since 0.6.10 there were no .txt reports to find. It now recognises .crash, reads the configured archive folder again, and exposes crashes, errors, uptime, crash_age, last_crash, last_error, version and date as filter keywords. (#1451)
  • ๐Ÿ›ก๏ธ Four security reviews โ€” SMTP, NRPE, NRDP/NSCA and WEB โ€” closed a set of defense-in-depth gaps: STARTTLS response injection, an unvalidated EHLO name, certificate verification that could not work on Windows, a metachar guard that ran before decoding, secrets in the trace log, and session tokens from a non-cryptographic generator.
  • ๐Ÿ” The bundled OpenSSL moves from 3.5.4 to 3.5.8 in the Windows builds, picking up four upstream security releases โ€” most relevantly CVE-2025-11187, a stack overflow parsing a hostile PKCS#12 file, reachable through check_certificate. (#1445)
  • ๐ŸชŸ The MSI can install your own TLS certificates. CERTIFICATE, CERTIFICATE_KEY and CERTIFICATE_CA place your files where every server module reads them, so the self-signed fallback is never generated. (#568)
  • โฑ๏ธ Real-time filters can prime their destination at startup. A new run on startup key submits the filter’s empty message once when the agent starts, so check_cache stops answering “Entry not found” after a restart. (#584)
  • ๐Ÿ“ก New ${address_ipv4} / ${address_ipv6*} hostname placeholders for every passive client, so a host can report itself by address instead of name. (#349)

๐Ÿ” Detailed changes

๐Ÿ“ค CheckHelpers โ€” check_and_forward delivers, and takes arguments properly

The command handed the raw QueryResponseMessage to the submission path, but channels parse a SubmitRequestMessage and the two are not wire compatible โ€” the payload sits in a different field, and field 2 of a submit message is the channel string. The channel received a message with zero payloads and cheerfully reported success. It now converts the query result into a submission first and checks the reply.

Option Meaning
channel Where to submit (default NSCA); target remains a synonym
alias Service description; defaults to the wrapped command’s name
destination Destination host for the submission
source Source host for the submission

target previously defaulted to the empty string, for which no handler exists, so even a correctly built message had nowhere to go.

๐Ÿ“… Scheduler โ€” run_schedules

run_schedules executes the schedules under [/settings/scheduler/schedules] immediately and submits each result on its own channel, target, source and alias with the same report filter, so the monitoring server cannot tell it from a timed run. The timers are untouched.

nscp client --boot --query run_schedules
nscp client --boot --query run_schedules --argument schedule=cpu

schedule= is repeatable and defaults to every schedule; an unknown alias is an error that names the ones you have. A schedule whose command is run_schedules is refused, and a reentrancy guard catches the indirect case (via check_timeout, for instance) โ€” previously that recursed until the agent died.

The caller’s identity is forwarded to the checks it runs, so REST and NRPE permissions apply to them; the scheduler’s own timed runs stay attributed to Scheduler.

๐Ÿ’ฅ CheckNSCP โ€” check_nscp rewritten as a filter check

Three independent bugs kept the crash count at zero: the extension comparison (txt vs the .txt the helper returns) has been false since 0.4.2; 0.6.10 replaced breakpad’s <guid>.dmp + .dmp.txt pair with a single <timestamp>.crash file, so even a fixed match found nothing; and 0.4.3 stopped reading [/settings/crash] archive folder, hardcoding the compile-time default. last_crash was never populated either โ€” the newest-file watermark started at the current time.

New filter keywords: crashes, errors, uptime, crash_age, last_crash, last_error, version, date. Thresholds accept duration units (crit=uptime < 5m, crit=crash_age < 7d), and a new max-unit option (default w) caps the largest unit rendered. Crash reports are a Windows concept; on Linux crashes is always 0.

โš™๏ธ Core โ€” reloads, channel verdicts and denied checks

  • A reload re-reads the included files before deciding which modules should run, so a module enabled in one since the last load is picked up. Only the includes are refreshed: clearing the whole store would discard configuration held in memory, which is exactly how nscp unit and nscp client set themselves up. One unreadable include no longer aborts the whole reload. (#1455)
  • Every channel’s verdict is reported for a channel list. All handlers were handed the same response buffer, so with channel=NSCA,GRAPHITE a failing NSCA was masked by a succeeding GRAPHITE.
  • A denied check is no longer submitted. The permission layer answers a denied query as a successful query carrying an UNKNOWN “Permission denied” payload; run_schedules and check_and_forward forwarded it, overwriting the last real result on the server while reporting success to the caller.
  • nscp client --query <cmd> no longer appends No module was specifiedโ€ฆ to every result.

๐Ÿ›ก๏ธ Security reviews

๐Ÿ”’ SMTPClient. Data pipelined across the STARTTLS handshake is refused (RFC 3207 ยง4) โ€” a prepared run of 2xx replies could otherwise walk the client through MAIL/RCPT/DATA and have it report an alert delivered while nothing was sent. The EHLO name is validated before connect, closing command injection on a relayed submission. Certificates are verified against a CA bundle through a new ca target setting and --ca argument (default ${ca-path}): the client previously used OpenSSL’s default verify paths only, which on Windows excludes the certificate store, so security=starttls failed against Gmail and M365 and operators simply turned verification off. EHLO capabilities are matched per reply line rather than by substring โ€” a greeting naming host starttls.example.com used to satisfy the STARTTLS lookup.

๐Ÿ”’ NRPE. The allow nasty characters = false guard now also runs on the decoded command and arguments; with a non-UTF-8 encoding, a multi-byte sequence could decode into a metacharacter that was never literally on the wire. A new expose version server setting (default true) lets the unauthenticated _NRPE_CHECK reply stop naming the exact build. nscp nrpe install reads the stored verify mode again instead of silently resetting it on every re-run.

๐Ÿ”’ NRDP / NSCA. A malformed <status></status> response no longer null-derefs the agent. The NRDP token and any proxy-URL credentials are redacted from the trace log โ€” including from the Target configuration: dump, which printed the raw settings map and defeated the redaction elsewhere (NSCA’s password leaked the same way). An https:// submission made through nscp client or REST with no verify mode now defaults to peer rather than trusting any certificate.

๐Ÿ”’ WEBServer. Session tokens and generated admin passwords come from OpenSSL’s CSPRNG with unbiased rejection sampling, and the server now fails closed if that RNG fails (HTTP 500 and a SECURITY: log line) rather than falling back to a weaker generator. Cookie lookups require a name boundary, so eviltoken no longer satisfies a lookup for token. Session validity and identity are read in one locked observation, closing an expiry race that could drop a request onto the anonymous grant. The web installer refuses an HTTPSโ†’HTTP redirect on the bundle download path.

๐Ÿ”’ OpenSSL 3.5.8. Windows builds only; Linux packages link the distribution’s OpenSSL. See Security notices.

๐Ÿ“ก Clients โ€” --source-host names the sender

--source-host / --sender-host were registered against the destination container, where the well-known host key is routed into the typed address field โ€” so naming a source host silently redirected the connection to it, and the sender the handler reads was never set. SMTPClient and NRDPClient had each worked around this with their own copies, which made the option ambiguous and therefore unusable on exactly the two modules most likely to need it.

๐Ÿ”ง Settings

  • settings --update --add-defaults --use-samples writes the sample objects; the flag was parsed and never read. --remove-defaults now enumerates samples too, making the two exact inverses. (#233)
  • A [/includes] entry naming a directory no longer breaks saving. The directory path reached the INI writer, and the resulting “Is a directory” error aborted the save โ€” so nscp settings --set and the web UI failed and the main file was never written. (#636)
  • New ${address_ipv4} and ${address_ipv6*} placeholders resolve in the hostname setting of NSCA, NSCANg, NRDP, Graphite, Syslog, Op5, Icinga, Elastic and Collectd clients. The address is the source address of the default route, falling back to the first non-loopback address the host name resolves to. (#349)

โฑ๏ธ Real-time filters โ€” run on startup

A new boolean filter key on the shared filter object, so it applies to CheckLogFile, CheckEventLog and the CheckSystem/CheckSystemUnix real-time filters. When true the filter submits its empty message with OK status once at startup, priming the destination. It is registered without a default on purpose: an absent key keeps the inherited value, while an explicit false overrides an inherited true. Delivery is retried across shortened waits while later modules (such as SimpleCache) are still loading. (#584)

๐ŸชŸ Windows installer โ€” install your own TLS certificates

Three new silent-install properties place your own files under the default names every server module reads, so the self-signed fallback is never generated:

Property Installed as
CERTIFICATE certificate.pem
CERTIFICATE_KEY certificate_key.pem
CERTIFICATE_CA ca.pem

The install fails if a named file is missing or is not PEM, or if a certificate is given with no key anywhere. CERTIFICATE_KEY also writes certificate key = โ€ฆ for the NRPE and WEB servers, so it is incompatible with ALLOW_CONFIGURATION=0; other servers need the setting added by hand. (#568)

๐Ÿ› Bug fixes

  • SMTP: the timeout error now says what happened in the client’s own words (timed out after 30s (the budget for the whole submission)) rather than reporting a platform error that blamed the connected party; a failed connect raises the exception callers actually catch; insecure-skip-verify is accepted over REST and no longer resets a configured target’s value on every submission; the reference no longer shows its default as N/A.
  • NRPE: the arguments-case rejection says “arguments” instead of “command”.
  • check_nscp: a crash report whose timestamp cannot be read still counts but takes no part in newest-wins, so crash_age no longer reports ~56 years.
  • Configuring with -DNSCP_BUILD_TESTS=OFF no longer aborts on the mongoose_wrapper_test target.

โš ๏ธ Upgrade notes

  • check_and_forward starts delivering, and no longer takes positional arguments. Anything that treated Message submitted as success will now see real channel failures. check_and_forward command=check_cpu warn=load>80 now fails to parse โ€” pass one arguments= per wrapped argument instead: check_and_forward command=check_cpu "arguments=warn=load>80". The positional form had to go because its parser also swallowed the CLI’s own --argument key=value tokens, which is what fed the wrapped command garbage.
  • check_nscp may start reporting CRITICAL. Its crash count works now, so an agent with an old report still in the archive folder will report a crash where it read 0. Threshold on crash_age ("crit=crash_age < 7d") if you only care about recent crashes, or clean the folder out. The message also loses its last crash: / last error: fragments โ€” put them back with an explicit detail-syntax if you match on the text.
  • A denied check now fails instead of submitting. If you restrict what a REST or NRPE identity may run, expect an error where a stale UNKNOWN previously appeared on the monitoring server.
  • NRDP over HTTPS verifies by default on the nscp client / REST path. Pass --verify none (or point --ca at the certificate) to keep submitting to a self-signed endpoint that way. Configured targets already defaulted to peer. ๐Ÿ”’
  • SMTP verifies the server certificate, against ${ca-path} by default. A target that relied on verification being effectively off needs ca pointed at the right bundle, ca = none for OpenSSL’s defaults, or insecure-skip-verify = true. ๐Ÿ”’
  • The SMTP timeout is now a budget for the whole submission, not a fresh deadline per operation โ€” a target that only completed by consuming several multiples of it will now give up.
  • --source-host no longer redirects the connection. If you used it to choose where to connect, use --host / --address.
  • settings --add-defaults --use-samples now writes samples. Drop the flag if you were relying on today’s sample-free output; --remove-defaults strips them again.
  • nscp client --query output loses its trailing No module was specifiedโ€ฆ line. Scripts that stripped it can stop.
  • Settings writes work again when [/includes] names a directory. If you removed a directory include to get saving working, you can put it back.
  • WEBServer: a script or module name beginning with - is rejected (rename it; interior dashes are fine), and POST /auth/logout now enforces allowed hosts. ๐Ÿ”’
  • NRPE: allow nasty characters = false now also inspects decoded input, so a request the guard was always meant to block may now be rejected. Set expose version = false to stop the _NRPE_CHECK ping naming your build. ๐Ÿ”’

Download

You can download the new version from GitHub

// Michael Medin