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_forwardsubmits again. The command ran the check, answered
Message submittedand 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 gainedchannel,alias,
destinationandsource. (#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 andnscp testtoo.
(#1450, #1452) - 💥
check_nscpis a filter check, and its crash count works. It has read 0
crashes since 0.4.2 (it matched the extensiontxtagainst.txt), and since
0.6.10 there were no.txtreports to find. It now recognises.crash, reads
the configuredarchive folderagain, and exposescrashes,errors,
uptime,crash_age,last_crash,last_error,versionanddateas
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_KEYandCERTIFICATE_CAplace 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 startupkey submits the filter's empty message once when the agent
starts, socheck_cachestops 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 hownscp unitandnscp clientset
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 withchannel=NSCA,GRAPHITEa 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_schedulesandcheck_and_forwardforwarded it, overwriting the
last real result on the server while reporting success to the caller. nscp client --query <cmd>no longer appendsNo 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-sampleswrites the sample objects; the
flag was parsed and never read.--remove-defaultsnow 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 — sonscp settings --setand the web UI failed and the main file
was never written. (#636) - New
${address_ipv4}and${address_ipv6*}placeholders resolve in the
hostnamesetting 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-verifyis
accepted over REST and no longer resets a configured target's value on every
submission; the reference no longer shows its default asN/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, socrash_ageno longer reports ~56 years.- Configuring with
-DNSCP_BUILD_TESTS=OFFno longer aborts on the
mongoose_wrapper_testtarget.
⚠️ Upgrade notes
check_and_forwardstarts delivering, and no longer takes positional
arguments. Anything that treatedMessage submittedas success will now see
real channel failures.check_and_forward command=check_cpu warn=load>80now
fails to parse — pass onearguments=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=valuetokens, which is what fed the wrapped command garbage.check_nscpmay 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 oncrash_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-syntaxif 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--caat the certificate) to keep submitting to a
self-signed endpoint that way. Configured targets already defaulted topeer.
🔒 - SMTP verifies the server certificate, against
${ca-path}by default. A
target that relied on verification being effectively off needscapointed at
the right bundle,ca = nonefor OpenSSL's defaults, or
insecure-skip-verify = true. 🔒 - The SMTP
timeoutis 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-hostno longer redirects the connection. If you used it to
choose where to connect, use--host/--address.settings --add-defaults --use-samplesnow writes samples. Drop the flag
if you were relying on today's sample-free output;--remove-defaultsstrips
them again.nscp client --queryoutput loses its trailingNo 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), andPOST /auth/logoutnow enforces
allowed hosts. 🔒 - NRPE:
allow nasty characters = falsenow also inspects decoded input, so
a request the guard was always meant to block may now be rejected. Set
expose version = falseto stop the_NRPE_CHECKping naming your build. 🔒
Full Changelog: 0.17.0...0.18.0