Upgrading¶
What to do when upgrading NSClient++, newest release first. Most upgrades are in place โ defaults are preserved and the default install is usually unaffected โ but the items below change observable behaviour or want a configuration touch. Read the entries between the version you are on and the version you are moving to.
Each entry carries an icon for the area it touches. ๐ is the one that matters: those entries are security-relevant, and the Security notices page tracks them in one place. An entry badged Action required needs a change on every installation that runs the module; one badged Check your setup only if you use the feature it describes; the rest are informational. Full per-release detail lives in each GitHub release.
Use the filter below to narrow the list to your setup: pick the version you
are coming from and tick the modules your nsclient.ini enables, and only the
entries that concern that installation stay visible. The selection is shared
with the Security notices page, remembered in your browser and reflected in
the page address, so a filtered view can be bookmarked or shared.
Modules: all
Tick the modules your nsclient.ini enables. Entries that touch none of the ticked modules are hidden; service covers the agent itself, which every installation runs. The selection is shared between the Upgrading and Security notices pages.
0.19.0¶
- ๐ A
check_servicefilter that matched no service no longer kills the agent. On Windows,check_service "filter=name = 'nosuchservice'"terminated the wholenscpprocess instead of returning a result;check_logfile’scolumn()keyword could be driven into the same fault. Both now return the documented empty-result contract (UNKNOWN: No services found). Nothing to configure โ if you worked around it withservice=<exact name>and nofilter=, service name patterns are usable again. See the security notice.
- ๐ Check your setup A client target no longer lets a request send its configured credentials to
a destination the request names.
host=,port=andaddress=used to move the destination while the target’spasswordortokencame along, so anyone able to run a client module’ssubmit_*/check_*command โ a REST user in the seededmonitoringrole, or an NRPE peer withallow arguments = trueโ could have the agent send that credential to a host of their choosing. That combination is now refused. A target with no credential, a request supplying its ownpassword=/token=, and a request that does not move the destination are unaffected. If you relied on one credentialed target plushost=to reach several servers: pass the credential with the request, configure each server as its own target and select it withtarget=(which now works for queries too), or setallow host override = trueon the target. See the security notice.
- ๐ REST script uploads are staged in a private, randomly named file.
PUT /api/v2/scripts/โฆno longer writes the upload to${temp}/<name>, where a local user could plant a file of the same name and have it imported as a command. No configuration change; a staging failure is now reported as an HTTP 500 instead of importing whatever was on disk. See the security notice.
- ๐ Check your setup Modern layout (opt-in, experimental): the shared folder must be a real
directory. A
%ProgramData%\NSClient++that is a junction or symbolic link is refused by the installer, bynscp settings --migrate-layout modern, and at service start (which fails rather than loading a configuration from behind a link). Relocate the folder with a[paths]override inboot.iniinstead. Legacy (default) installs are unaffected. See the security notice.
- ๐ ๐ฅ Check your setup A multicast collectd target now sends through one interface, not all of
them. A
CollectdClienttarget with no address goes to collectd’s default multicast group (239.192.74.66:25826), and every such target used to send a copy of the metrics through every local interface of the matching address family โ putting them on segments the operator never meant to reach. The new per-targetmulticast interfacesetting decides this:auto(the default) sends one copy through the interface the routing table picks,allrestores the previous fan-out, and a comma-separated list of local IP addresses sends through exactly those. Unicast targets are unaffected. If you depend on a multicast target reaching several segments, setmulticast interface = allon it. See the security notice.
- ๐ Repeated rapid-fire failed WEB logins from one IP are now blocked for longer
each time. The block that follows
auth rate limit max failuresconsecutive failures used to be a fixedauth rate limit block secondswindow (default 60 s) that let an attacker resume guessing at a steady rate forever. A further block now doubles the wait, up to an hour, and resets after a successful authentication or an hour of quiet. Only failures arriving at machine speed escalate: a client retrying a stale password on a schedule keeps the base delay, so it cannot lock out everyone sharing its address behind NAT or a proxy. Nothing to do on a default install.auth rate limit max failures = 0still disables the limiter outright. See the security notice.
- ๐ An NRDP submission over an unverified
httpslink now says so in the log. When a target’sverify moderesolves to no peer verification, the module logs a message naming the endpoint โ the token then goes to whichever server answers โ matching what the Icinga client already does. The connection itself is unchanged. Theverify modehelp text was corrected at the same time: it listed three values the client-side parser rejects (client-once,workarounds,single) and recommendednonefor self-signed certificates; usepeer-certwithcapointing at the certificate instead. See the security notice.
-
๐ง Check your setup
nscp testnow has a real prompt, and writes a command history file. On a terminal the prompt gained line editing, persistent history, tab completion against the command registry, syntax highlighting that shows an unknown query or module name in red before you press enter, and log messages that redraw around what you are typing instead of landing in the middle of it. See Test mode. Two things to know:- History is written to a per-user file โ
%APPDATA%\NSClient++\console-history.txton Windows,$XDG_STATE_HOME/nscp/console-historyor~/.nscp_historyelsewhere (created mode0600on POSIX). Commands typed at the prompt can carry credentials, so if you would rather keep nothing on disk sethistory size = 0under[/settings/cli]. That section also holdshistory fileandcolor. - With stdin not a terminal nothing changes โ no prompt, no history, no colour, and the core keeps writing the log itself. Piping commands in does now work on Windows, where the readiness check used a console-only API and silently ignored a file or pipe; an exhausted stdin no longer spins at 100% CPU on POSIX; and end of input is no longer treated as a reason to exit, which is how the agent is normally started under a supervisor.
Completion after
load/enableoffers the modules that are not already loaded or enabled, andunload/disablethe ones that are. The first such completion in a session pauses while the agent reads the module directory, once per process. - History is written to a per-user file โ
-
๐ง The console log is no longer held back in a buffer. Nothing to do. The console log backend installs a 64 KB buffer on standard output and nothing emptied it, so on Windows log output sat there until something else happened to flush the stream โ in
nscp testthe only thing that did was reading the next line of input, which is why the log appeared to catch up only when you pressed a key. Every message is flushed as it is written now.The same fix means a redirected console log streams instead of accumulating:
nscp test > log.txt, a container, or a supervisor capturing standard output now see each line as it is logged rather than nothing until 64 KB has built up or the process exits.
- ๐ง
--no-stderrand the oneline log format now take effect. Nothing to do unless you were working around the old behaviour. Both were passed to the log level parser, which does not know them, so they were rejected withInvalid log level: no-std-errin the log and never applied. They reach the log driver now.
- ๐ง
target=now selects a configured target on the query path as well. The shared client machinery only ever appliedtarget=/-twhen a command was run as an exec; as a query โ which is what a REST or NRPE caller gets forcheck_*andsubmit_*โ the argument was accepted and then ignored, so the call silently went to thedefaulttarget. It is applied on both paths now, with any explicit option (host=,timeout=, โฆ) still winning over what the selected target says. A query that passedtarget=and relied on reachingdefaultanyway will now reach the target it named.
- โฑ๏ธ Check your setup collectd submissions now honour
timeoutandretries, and report failed sends. Both target settings were read into the connection but never used on the UDP path, and the send discarded its result entirely โ an unreachable target, a full socket buffer or an oversized datagram looked exactly like a successful send, so metrics that never arrived left nothing in the log. A failed send is now retried up toretriestimes (default 3, 20 ms apart) and logged once per distinct failure; the whole send โ name resolution included โ is bounded bytimeout(default 30 seconds), after which the remaining datagrams are abandoned with a message. Retries apply to sends that failed locally, so no datagram the receiver already has is ever sent twice. Nothing to do unless you set a largeretrieson a collectd target โ it now costs real time, bounded bytimeout.
- ๐ง Check your setup An Icinga target address with a path prefix is now honoured. The path of a
target
address(https://proxy.example.com/icinga/) was parsed and then dropped: every call went to/v1/...on the host, so an Icinga 2 master published under a reverse-proxy subpath could not be reached and requests landed on a path the operator did not intend. The prefix is now prepended to every API path (/icinga/v1/actions/process-check-result). Addresses without a path โ the normalhttps://icinga.example.com:5665form, with or without a trailing/โ are unaffected. If a target address carries a path that is not a subpath of the API (a leftover from another client’s configuration, say), remove it: it is now sent.
0.18.1¶
- ๐ Check your setup Icinga API submissions now honour the configured
timeout, and credentials no longer reach the trace log. TheIcingaClientmodule’s HTTP calls previously waited forever โ the target’stimeoutsetting (default 30 s) was read but never applied โ so a stalled Icinga endpoint could silently wedge passive-result submission; settimeout = 0on the target if you depend on the old unbounded wait. The same pass maskedpassword/tokenvalues in the trace-level target dump (this also covers the other client modules sharing that machinery) and added a log message when anhttpssubmission runs with certificate verification disabled (verify modeempty ornone). See Security notices.
- ๐ Filter expressions are now bounded in length and nesting depth. A
filter/warning/criticalexpression โ and a%(...)expression placeholder inside a syntax template โ longer than 1024 characters or nested more than 64 parentheses deep is now rejected at parse time instead of being evaluated, failing with a clear “exceeds the maximum length/depth” error. The where-parser and the expression evaluator both recurse with the shape of the input, so an unbounded or deeply nested expression could exhaust the stack and crash the agent. Real filters are a small fraction of these limits, so the default install and every normal configuration are unaffected โ only a pathologically large or deeply nested expression is refused. See the security notice.
- ๐ Check your setup CheckExternalScripts hardening. A hardening pass over the external
scripts module tightened several rough edges: the
ext-scr installargument lockdown now writes to the setting the module actually reads (previously it was a no-op, so a lockdown could silently not apply), the command timeout is enforced on every execution path with output capped, theshow/deletesandbox resolves symlinks, and%/^are blocked on the shell-fallback path. The default install is unaffected (arguments are off by default). If you rely onext-scr installto disable arguments, re-run it after upgrading so the effective setting is written. See the security notice.
- ๐ Check your setup NSCA-NG cert mode now actually verifies the server (and presents the
client certificate). In 0.18.0 the
NSCANgClientcert mode (use psk = false) applied its TLS configuration to the OpenSSL context after the connection object had been created from it, and OpenSSL copies the verify mode, client certificate, cipher list and TLS-version bounds out of the context at creation time โ soverify mode = peer-certwas silently ignored, any certificate the server presented was accepted, and the configured client certificate was never sent. The configuration is applied before the connection is created now. Two operator-visible consequences: a cert-mode target that “worked” against a server whose certificate does not chain to the configuredca(or does not match the host name) will now fail to connect โ that is the verification working; fix the server certificate, or opt out explicitly withinsecure = trueif you accept the MITM risk. And servers that require a client certificate will start seeing it. The default PSK mode (use psk = true) is unaffected. See Security notices.
- ๐ Check your setup NSCA: an unrecognized
encryptionvalue is now a hard error instead of silently running without encryption. A typo’d algorithm name (aes-256), or one not compiled into the build, used to fall back to no encryption on the end carrying it. Since the ciphers must match, a one-sided typo showed up as the peer rejecting every submission with a CRC error rather than as accepted plaintext โ but that failure gave no hint of its cause, and a value broken the same way on both ends did run plaintext while looking encrypted. Now theNSCAServermodule refuses to load and anNSCAClientsubmission fails, each naming the problem and listing the available algorithms. Default installs (aes256) are unaffected. Breaking only for setups relying on the fallback: fix the algorithm name, or setencryption = noneexplicitly if plaintext was intended โ this includes builds compiled without crypto++, where any cipher name previously degraded to plaintext and the server now refuses to start. See the security notice.
- ๐ Check your setup NSCA hardening: empty-password warning,
performance data = falsehonoured, wire-field validation. Enabling NSCA encryption with an emptypasswordnow logs an error on both ends (the password is the key, so an empty one is a well-known key) โ set the same password on both ends to clear it.NSCAServer’sperformance data = falsenow actually strips perfdata from forwarded submissions (it was silently ignored). Inbound host/service names are stripped of control characters and out-of-range status codes are clamped to UNKNOWN. No action needed on a default install. See the security notice.
- ๐ Check your setup NRDP submissions now honour
timeoutandretry. Both settings were parsed but never applied: a submission to a server that accepted the connection and then stalled hung the submission thread forever, and failed submissions were never retried. Every step of the exchange (connect, TLS handshake, proxy tunnel, request, response) now runs under the configuredtimeout(default 30 seconds for configured targets, 10 for one-shotnscp clientsubmissions), and transport failures are retried up toretrytimes. Nothing to do unless your NRDP endpoint legitimately takes longer than the timeout to answer โ raisetimeouton that target. The response body is also capped at 5 MB, far above any real NRDP reply. See the security notice.
- ๐ A
tls versionwith a trailing+now means “that version or later”.1.2+(the common default) previously negotiated TLS 1.2 only; it now also permits TLS 1.3, andanyis accepted as the documentation always claimed. This applies everywhere the setting exists: NRDP and the other HTTP-based clients, the NRPE/NSCA clients and servers, andcheck_tcp. No action needed; pin an exact version (tls version = 1.2) if a peer misbehaves when TLS 1.3 is offered. See the security notice.
- ๐ Check your setup The Elastic module now verifies HTTPS server certificates and can
authenticate.
ElasticClientpreviously hardcoded TLS verification off; anhttps://address now defaults toverify mode = peeragainst the platform CA bundle, with newtls version,verify modeandcasettings to tune it. Newuser/passwordandapi keysettings authenticate against secured clusters (Elasticsearch 8+ defaults), and a newtimeout(default 30s) bounds each submission. If you rely on a self-signed certificate, pointcaat it or setverify mode = noneexplicitly. See Security notices.
- ๐ค Check your setup The Elastic module no longer sends the legacy
_typeparameter by default. Mapping types were removed in Elasticsearch 8, which rejects bulk requests carrying them, so theevent type,metrics typeandnsclient log typedefaults are now empty. Only Elasticsearch 6.x or older needs them: set the old values (eventlog,metrics,nsclient log) explicitly to keep the previous behaviour. Batched documents also now get distinct ids โ previously all documents in one bulk request shared an id and overwrote each other, so multi-entry events show up completely now.
- ๐ Check your setup check_mk client targets: configured TLS settings are honoured again.
check_mk_target_object::read()added the SSL keys (use ssl,certificate,verify mode,ca, โฆ) to its settings registry but never calledregister_all()/notify(), so values set on a[/settings/check_mk/client/targets/โฆ]section were silently ignored and the client connected in plaintext regardless of configuration. The keys are read (and documented) again. If you configureduse ssl = trueon a check_mk target, the connection becomes TLS on upgrade โ make sure the server side actually speaks TLS, or the check starts failing. Details in the security notice.
- ๐จ Check your setup Syslog client targets: configured severities and templates are honoured
again. The same defect existed in the syslog client’s target object: the
severity,facility,tag_syntax,message_syntaxand per-status severity keys on a[/settings/syslog/client/targets/โฆ]section were never read, so the built-in defaults (error/kernel/โฆ) always won. Values you configured โ perhaps years ago, without effect โ now apply; if your syslog routing depends on the previously effective defaults, review the target sections for stale keys.
-
๐จ ๐ Check your setup Syslog messages now carry the RFC 3164 HOSTNAME field, and several syslog options work for the first time (see the security notice). What changes on the wire and in behaviour:
- Datagrams now read
<PRI>TIMESTAMP HOSTNAME TAG MESSAGE. Thehostnamesetting under[/settings/syslog/client]โ until now read but never used โ fills the HOSTNAME field (defaultauto, the machine name). Receivers that promoted the tag (defaultNSCA) to origin host will now file records under the real host name; adjust any log-parsing rule that keyed on the old, hostname-less format. - The
tag_syntaxandmessage_syntaxtarget settings now reach the wire. They were stored under keys the sender never read, so a settings-defined target sent an empty tag and dropped the message text. - The per-state severity options (
ok-severity,warning-severity,critical-severity,unknown-severity) passed as command arguments now take effect; they too were stored under keys that were never read. - An unknown
severityorfacilityname now degrades to priority<13>(user.notice) instead of<0>โ which is kernel.emergency, a priority many receivers page or broadcast on. A missing per-state severity now falls back to the baseseverityinstead of tripping that fallback. - All C0 control bytes and DEL in the outgoing line are replaced with spaces (previously only CR, LF and NUL), so check output cannot smuggle ANSI escape sequences into the receiver’s log.
- Datagrams now read
- โฑ๏ธ Check your setup The Graphite
timeoutis now enforced โ as a budget for the whole submission. It was doubly dead before: the value the operator configured never reached the connection (the lookup read a map the well-knowntimeoutkey is not stored in, so the default 30 always won), and the connection did not use even that โ resolution, connect, the TLS handshake and every write ran with no deadline, so a stalled carbon endpoint could hold the submitting thread for the OS-level TCP timeout, or indefinitely on a stuck write. The configuredtimeout(default 30s) is read now and bounds the whole submission as one budget, like the SMTP client’s. A target whose submissions previously completed by quietly taking longer will now fail at the configured value; raisetimeouton targets talking to a slow relay. See Security notices.
- ๐งน The Graphite
retrysetting is not honoured and is no longer read. The module always made exactly one attempt per submission; reading the value made it look otherwise, and a retry loop would multiply the worst-case time a stalled endpoint can hold the submitting thread by the retry count.retry/retriesare registered for every client module centrally, so they still appear in the GraphiteClient reference, butGraphiteClientdoes not act on them. Mirrors the SMTPretrychange in 0.18.0.
0.18.0¶
-
๐ฅ Check your setup
check_nscpis now a filter check, and it can see crash reports again. Crash reporting itself has always worked โ the agent has archived crash dumps since 0.4.x โ butcheck_nscp’s count of them has not, for two independent reasons that accumulated over the years:- The count matched files whose extension equalled
txt, while the helper it used returns the extension with its leading dot (.txt). That comparison has been false since the check was written in 0.4.2, so the crash count read 0 whatever was in the folder. - 0.6.10 dropped Google Breakpad, whose vendored submodule and build
machinery had become a dependency burden, along with the separate
crash-report sender tool that shipped with it. The handler that replaced
it writes one plain-text
<timestamp>.crashfile per crash โ the exception, the faulting address and the module it landed in โ where breakpad left a<guid>.dmpminidump plus a<guid>.dmp.txtdescription. From 0.6.10 on, even a corrected.txtmatch would have found nothing.
Separately, 0.4.3 stopped the module reading the archive folder from
[/settings/crash]archive folderand hardcoded the compile-time default instead, so on any installation that had moved the archive folder the check was looking in the wrong place as well.All of that is fixed. Crash reports are recognised by
.crash, and still by.dmpand.txtso pre-0.6.10 archives keep counting; the configuredarchive folderis read again; andcheck_nscpnow exposescrashes,last_crash,crash_age,errors,last_error,uptime,versionanddateas filter keywords, accepts the usualfilter/warning/criticalandtop-syntax/detail-syntaxoptions, and emits perfdata for whichever keywords the thresholds name.The default verdict is unchanged โ any crash report or any logged error is CRITICAL โ but two things change for existing users. The message is now
N crash(es), M error(s), uptime <duration>without the appendedlast crash:/last error:fragments. To put them back, pass adetail-syntax:check_nscp "detail-syntax=${crashes} crash(es) (${last_crash}), ${errors} error(s) (${last_error}), uptime ${uptime}"And because the crash count now works, an agent with an old report still sitting in the crash archive folder will start reporting CRITICAL where it previously read 0 โ threshold on
crash_age(for example"crit=crash_age < 7d") if you only care about recent crashes, or clean the folder out. Crash reports remain a Windows-only concept; on Linux there is no crash handler, socrashesis always 0. - The count matched files whose extension equalled
-
๐ค Check your setup
check_and_forwardnow actually submits the result. The command ran the wrapped check, answeredMessage submittedand delivered nothing: the submission was built in a form the channels could not read, so NSCA, NRDP, Graphite and every other client module received a message with no results in it. It now submits like a scheduled check does. If you had given up on the command, it works โ and if you built a workaround around its silence (for instance a schedule that exists only to be triggered), that workaround is no longer needed. The command also gainedchannel(targetstill works as a synonym),alias,destinationandsourceoptions, and names the result after the command when no alias is given.Breaking: the command no longer accepts positional arguments for the wrapped command.
check_and_forward command=check_cpu warn=load>80used to (try to) hand the bare trailing tokens to the wrapped command; it now fails to parse. Pass each wrapped-command argument througharguments=instead, one per argument:check_and_forward command=check_cpu "arguments=warn=load>80" "arguments=crit=load>90"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 and made it fail. If a submission is rejected by the channel the command now reports that failure instead ofMessage submittedโ including when only one channel of a comma list (channel=NSCA,GRAPHITE) fails, which previously was silently reported as success.
- โ๏ธ A reload now loads modules enabled in an included file since the last one.
An
[/includes]file is read once when the configuration is loaded and served from memory after that, so a module switched on in one โ most importantlyfleet.ini, which the fleet sync rewrites whenever a bundle changes โ was not actually loaded until the service next restarted. Nothing said so: the file on disk was right and a fleet host reported itself in sync, while the module quietly did nothing. The visible case was a fleet bundle turning onNRDPClientandSchedulerto start passive submissions, which then never arrived. A reload now re-reads the included files first, so settings changed in them take effect and newly enabled modules are loaded; modules already running are left alone. Two things are unchanged: a module disabled since the last load keeps running until the service restarts, and[/modules]in the mainnsclient.iniis still only read at startup.
- ๐
New: run scheduled checks on demand with
run_schedules. After editingnsclient.iniyou no longer have to wait out the interval to see the new result on the monitoring server โnscp client --boot --query run_schedules(optionally--argument schedule=<alias>) runs the configured schedules now and submits their results on their normal channel. It is a regular check command, so it also works over NRPE, REST and innscp test. Nothing changes for existing configurations; the timers are untouched. See Passive monitoring โ Step 6.
- ๐ซ A denied check is no longer submitted as a passive result. The permission
layer answers a denied query as a successful query carrying an UNKNOWN
“Permission denied” payload, and both
run_schedulesandcheck_and_forwardforwarded that to the monitoring server โ overwriting the last real result for that service while telling the caller it had succeeded. Both now refuse withPermission denied: not allowed to run <command>, nothing was submittedand touch no channel. If you restrict what a REST or NRPE identity may run, expect the denial as an error where you previously saw a stale UNKNOWN appear on the server. See Permissions.
- ๐ง Check your setup
settings --update --add-defaults --use-samplesnow writes the sample objects. The flag was parsed and never read, so it behaved exactly like the plain invocation. It now writes the registered/samplesections, and--remove-defaultsstrips them again โ the two are now exact inverses. An edited sample is still never overwritten or removed, and without the flag the output is byte-for-byte what it was. If you have scripted--add-defaults --use-samplesexpecting today’s (sample-free) output, drop the flag.
- ๐ฌ Check your setup
nscp client --query <cmd>no longer appends “No module was specifiedโฆ”. The line was appended to every result when no--modulewas named. Scripts that stripped or matched it can stop; naming a module is unchanged.
- ๐ง Settings writes work again when
[/includes]names a directory. Saving handed the directory path to the INI writer, and the resulting “Is a directory” error aborted the whole save โ sonscp settings --setand the web UI failed and the main file was never written. Directory includes are read-only by nature and are now skipped when saving. If you worked around this by removing a directory include, you can put it back.
- ๐ Check your setup NRDP HTTPS submissions now verify the server certificate by default on
the
nscp client/REST path. Previously anhttps://submission made that way with noverify modeset trusted any certificate silently (configured targets already defaulted topeer). If you submit to a self-signed NRDP endpoint that way and want to keep skipping verification, pass--verify none(or point--caat the certificate) explicitly. Plainhttp://is unaffected. Two further NRDP hardening fixes ship in the same release โ a malformed server response can no longer crash the agent, and the NRDP token and any proxy-URL credentials are redacted from the trace log. See Security notices.
- ๐ก Check your setup
--source-host/--sender-hostnow name the sending host, on every client module. They were registered against the destination container, where the well-knownhostkey 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.SMTPClientandNRDPClienthad each worked around this by registering their own copies, which made the option name ambiguous and so unusable on those two modules (option '--source-host' is ambiguous). The options are registered once now, against the sender. If you had scripted around the old behaviour by passing--source-hostto redirect a connection, use--hostor--addressfor that instead.
- ๐ Check your setup SMTP submissions now verify the server certificate against the agent’s
CA bundle.
SMTPClientrelied on OpenSSL’s built-in default verify paths, which on Windows do not include the Windows certificate store โ so the defaultsecurity=starttlsfailed verification against Gmail, Microsoft 365 and every other public provider there, andinsecure-skip-verifywas the only way through. A newcatarget setting (and--caargument) defaults to${ca-path}, the same trusted bundle the other TLS clients use: the distribution’s CA store on unix, the exported Windows ROOT store on Windows. If a Windows SMTP target was only working because you setinsecure-skip-verify = true, remove it and retry โ verification should now succeed. For an internal relay with a private CA, pointcaat that bundle instead of waiving verification. Setca = noneto restore the old behaviour. A bundle that cannot be loaded now fails the submission with a message naming the file, rather than failing the handshake later with an unrelated-looking issuer error. A target that names nocaat all โ a one-shot command line, or a default target โ falls back to the same bundle, resolved once at module load, so no submission path is left on OpenSSL’s built-in verify paths by accident.
- ๐ SMTP client security hardening. The same pass closed a set of
trust gaps in
SMTPClient: data pipelined into the STARTTLS greeting is refused rather than trusted as post-handshake input, the EHLO name is validated for command injection before it reaches the wire, and ESMTP capabilities are matched per reply line instead of by substring search. No configuration change is needed. See Security notices.
- โฑ๏ธ Check your setup The SMTP
timeoutis now a budget for the whole submission rather than a fresh deadline per operation (and per resolved address). A target that previously completed by using several times its configuredtimeoutacross a slow session will now give up at the configured value; raisetimeouton targets talking to a slow relay.
- โ๏ธ SMTP messages now carry a
Message-IDheader. Nothing to do โ it improves deliverability and gives mail administrators a handle to trace a notification by.
- ๐งน The SMTP
retrysetting is not honoured and is no longer read. The module always made exactly one attempt per submission; reading the value made it look otherwise.retry/retriesare registered for every client module centrally, so they still appear in the SMTPClient reference, butSMTPClientdoes not act on them.
- ๐ NRPE hardening: a new
expose versionsetting, and the metachar guard now also checks decoded input. Nothing to do on a default install. Two things are worth knowing.NRPEServergainedexpose version(defaulttrue, which keeps the legacy bannercheck_nrpeexpects); set it tofalseto answer the unauthenticated_NRPE_CHECKping with a generic message instead of the exact build. Andallow nasty characters = falsenow re-checks the decoded command and arguments, not just the raw wire bytes โ with a non-UTF-8encodingset, a multi-byte sequence could previously decode into a metacharacter that was never literally on the wire, so a request the guard was always meant to block may now be rejected. Separately,nscp nrpe installreads the storedverify modeagain instead of silently resetting it on a re-run. See the security notice.
- ๐ Check your setup WEB server security hardening. A review of the
WEBServermodule produced several defense-in-depth fixes (session tokens now come from the OpenSSL CSPRNG, cookie-name matching requires a name boundary, the installer refuses an HTTPSโHTTP redirect, and thelegacygrant’s startup warning now names/settings/query.pb). The default install needs no action. Two changes touch observable behaviour: a script or module name that begins with-is now rejected (rename it; interior dashes are fine), and the legacyPOST /auth/logoutroute now enforcesallowed hostslike the rest of the API (a caller outside the perimeter gets 403). A third only bites on a broken system: if the OpenSSL CSPRNG fails, the server now refuses to issue a session token (HTTP 500, logged asSECURITY:) rather than falling back to a weaker generator. See the security notice.
- ๐ The bundled OpenSSL is updated from 3.5.4 to 3.5.8 in the Windows
builds, picking up the fixes from four upstream security releases on the
3.5 LTS line. The most relevant fix for NSClient++ is
CVE-2025-11187, a stack
buffer overflow parsing a crafted PKCS#12 file โ reachable through
check_certificatewhen it scans certificate files that less-trusted principals can write to โ alongside further PKCS#12/ASN.1 and TLS-stack fixes. No configuration change is needed. The Linux packages link the distribution’s OpenSSL and are unaffected. See Security notices.
0.17.0¶
-
๐ท๏ธ Check your setup Check-specific filter keywords that clashed with the generic summary keywords are renamed. A handful of checks registered their own keyword named
status,countortotalโ the same names as the built-in summary keywords (%(status),%(count), โฆ). The check-specific value won infilter/warning/criticalanddetail-syntax, whiletop-syntaxand the reference documentation showed the generic one, which was confusing. Each such keyword now has a distinct, documented name:Check Old New check_cpu,check_cpu_utilizationtotalusagecheck_batterystatusbattery_statuscheck_networkstatus,totallink_status,throughputcheck_os_updatescountupdatescheck_patch_agecountpatchescheck_pending_rebootcountsignalscheck_printjobsstatusjob_statuscheck_printqueuestatusprinter_statuscheck_installed_software(Linux)statuspackage_statuscheck_activationstatusactivation_statuscheck_dockerstatuscontainer_statuscheck_connectionscount,totalconnections,total_connectionscheck_dnscountrecordscheck_httpstatusstatus_messagecheck_shadowcopycountcopiescheck_disk_healthtotalsizeExisting configurations keep working: the old names remain as undocumented deprecated aliases with unchanged behaviour, so filters like
check_cpu "warn=total > 80"still work. Migrate to the new names at your convenience; only they appear in the reference documentation. A few behaviour changes ride along:check_os_updates’ default output now reports the actual number of updates (rendered from the detail line via${list}) instead of the matched-row count the old default showed.- Three default perfdata keys change because the default
perf-configlists the renamed keyword:check_cpu_utilization(Linux)cpu_totalโcpu_usage,check_patch_agepatch_countโpatch_patches,check_pending_rebootreboot_countโreboot_signals. Pass your ownperf-config=extra(...)with the old (alias) keyword to keep the old key. All other perfdata keys are unchanged (the renames keep their original perf suffixes, andcheck_connections’ default perf-config intentionally still uses the alias for series continuity).
- ๐ Check your setup
${host}and friends now resolve in attachment paths and in[/includes]. Host name placeholders were only expanded in settings urls and in the url an attachment is fetched from, not in the path it is written to nor in an included file name. An unknown${...}token in a path is not an error - it resolves to the installation directory - so a configuration such as[/attachments] ${shared-path}/${host}.ini = ...did not fail, it quietly wrote one file with the installation directory in its name. Those paths now name the host, which changes where such a file lands: check any${host},${hostname}or${domain}you already have under[/attachments]or[/includes], and remove the workaround if you scripted around this. Configurations without a host name placeholder are unaffected. When the substitution lands in a local path, the value is sanitized to the characters a legal host name can contain (see the security notice). Likenscp settings --switch,nscp settings --migrate-to(and the REST migrate) now keeps a placeholder you pass it as-is inboot.iniwhile migrating into the expanded per-host file, so the template survives on a fleet-managed machine.
- ๐ข Check messages can now be told how to render their numbers. Every filter
check gained four options -
decimals,byte-unit,decimal-separatorandthousands-separator- socheck_drivesizecan report141.06GB/1006.85GB(or141,06GB/1.006,85GB) instead of140.293GB/0.983TB. The defaults are unchanged, so an installation that does not set them renders exactly what it rendered before. The options only touch the message: performance data keeps its full precision and its.radix, and so do the numbers you write in a filter or a threshold. Real-time filters take the same settings asdecimals,byte unit,decimal separatorandthousands separatorkeys, inheritable from the default template.decimalsis capped at 15 (adoublecarries no more than that): the query option and theformat_bytes()/format_number()argument reject a larger value, and the settings key clamps it, so a typo likedecimals=1000000can no longer make a check try to render a multi-megabyte number. Note that setting any of the four options also moves plain float keywords in the message onto the number format: withdecimalsunset they then render with up to three decimals (trailing zeros stripped) instead of the legacy 6-significant-digit form โ2.71094becomes2.711, and large values stop rendering scientific (1.23457e+07). A pipeline that matches float text in the message may need its pattern relaxed when you first set one of these options; leave all four unset and the message is byte-for-byte unchanged.
- ๐ข Check your setup An unknown unit in
format_bytes()is now reported instead of rendering nonsense.format_bytes(used, 'gb')used to render1.27055e-10because the unit comparison was case sensitive, and any misspelled unit renderedvalue/1024^7. Lowercase units now work, and a unit that names nothing (say'ZB') makes the check reportFilter processing failed: format_bytes failed: Unknown byte unit: ZB. A syntax string with such a typo returns UNKNOWN rather than a quietly wrong number - fix the unit, or the check will stay UNKNOWN.
- ๐ Check your setup A
unit:inperf-configthat names no unit no longer divides the metric by 1024โท. An unrecognised unit now leaves the value alone. If a graph of yours has been flat at a near-zero value, check theunit:spelling in itsperf-config: the metric will jump to its real magnitude on upgrade.
- ๐ Check your setup
perf-config’sunit:now converts plain byte series instead of relabelling them. On series that are byte counts but do not auto-scale (most byte keywords outsidecheck_drivesize),unit:KBused to change the label only, shipping=1536KBfor a value of 1536 bytes - a metric off by the unit ratio to any consumer that trusts the label. The value and the warn/crit bounds now convert into the requested unit, matching what the auto-scaling series always did. A dashboard that compensated for the mislabelling will see the metric drop by that ratio on upgrade. Series not measured in bytes (ms,%,s, …) and explicitminimum:/maximum:overrides are unaffected, and aunit:that names no byte unit still only changes the label.
- ๐งฉ Check your setup Errors raised while a template renders are now reported. A function that
failed inside
detail-syntaxortop-syntaxused to leave the placeholder empty and say nothing; the check now returns UNKNOWN withFilter processing failed: โฆ. This surfaces template mistakes that have been silently producing incomplete messages.
- ๐ช Check your setup
check_pending_reboot’s default message now names the pending-since time. When the reboot was queued by Component Based Servicing or Windows Update, the message gains a suffix:Reboot required: Windows UpdatebecameReboot required: Windows Update (pending since 2026-08-16 09:41:12). Notification pipelines that match the exact message text (an anchored regex, a string equality) need their pattern relaxed; thresholds, states and existing keywords are unchanged.
- ๐ข Check your setup Filter comparisons between a text keyword and a bare number are now
numeric. A string-typed keyword compared against an unquoted number used
to order lexically โ
filter=value > 90onfilter_perfmatchedvalue=100as false (“100” sorts before “90”) โ or, with the operands reversed (90 > value), failed to evaluate at all. Both now compare as numbers, whichever side the keyword is on: the row’s text is parsed per record, and a value that is not a number simply never matches (the check logs one warning naming the value; the result stays a certain non-match, not UNKNOWN). This applies to keywords such asvalue/warn/crit/min/max(filter_perf,render_perf),speed(check_network),string_value(check_registry_value) and thecolumn()function (check_logfile). Quoted literals keep the lexical comparison โversion < '8'still orders as text โ as dolike,regexpandin, keyword-specific converters (state = 'running',age > 30m), and the= 'unknown'/= 'never'sentinels for optional values. Review any filter that deliberately relied on text ordering against a bare number: quote the number to keep the old behaviour.
- ๐ข Check your setup Fractional numbers in thresholds are no longer truncated or rounded.
count > 2.5used to evaluate ascount > 3(the literal was rounded into the counter’s integer domain); unit literals lost their fraction entirely, soworking_set > 1.5gmeant 1g anduptime < 2.5hmeant 2h. Fractions now mean what they say. Whole-number thresholds are unchanged; only expressions that already used a decimal point can behave differently.
- ๐ Check your setup
filter_perf/render_perf/xform_perf: themaxandminfilter keywords were swapped.maxread the perf-data minimum bound andminthe maximum. They now read the bounds they name โ a filter that compensated for the swap needs the two names exchanged back.
- ๐จ Check your setup Syslog submission works again, so a configured syslog server will start
receiving traffic.
SyslogClientread its connection settings from the wrong place, so the target’s address, port, facility, severity and templates were all ignored: the agent loggedUndefined facility:and sent nothing. Broken since 0.4.3 (2015). If you have a syslog target configured, check it still points where you want before upgrading - it has not been delivering, and it will now.CheckMKClienthad the same defect on its query path.
- โ๏ธ Check your setup SMTP notifications now announce this host in EHLO instead of
localhost. The sender’s host name was read from the wrong place, so it was always empty and the EHLO fell back tolocalhost. If your mail server applies HELO/EHLO policy (SPF checks, or a rule that rejectslocalhost), the agent will now identify itself properly - setehlo-hostnameon the target if you need a specific name.
- ๐ง Check your setup
nscp settings --shownow says so when--keyis missing.--show --path /some/pathwithout a--keyused to print nothing and exit 0; it now reportsInvalid command line please use --path and --key with showand exits non-zero. A bare--showstill describes the active settings store, and--show --path โฆ --key โฆis unchanged. Scripts that relied on the silent success need the missing--keyadded.
- ๐ฌ Client commands shorter than eight characters work again. A command
such as
cpuorrunansweredException processing command line: basic_string::substr โฆinstead of running, in every module built on the shared client machinery (NRPE, NSCA, NRDP, Graphite, โฆ). Remove any workaround that renamed such commands to a longer alias; no configuration change is needed.
- ๐ง The settings diff no longer reports changes that were already saved.
get_changes()โ behind the REST settingsdiffendpoint and any operator-facing “what am I about to save?” view โ kept listing an edit for the lifetime of the process after it had been written, reporting it as amodifiedentry whose old value equalled its new one. Tooling that treated a non-empty diff as “unsaved work pending” no longer needs to special-case that; no configuration change is needed.
0.16.4¶
- ๐ The bundled Mongoose web server is upgraded to 7.23, fixing two
critical (CVSS 9.1) HTTP request-smuggling vulnerabilities in its HTTP
parser (CVE-2026-73256,
CVE-2026-73257). This
affects the Windows builds of the
WEBServermodule (REST API / web UI) and is exploitable when NSClient++ sits behind a reverse proxy or WAF โ upgrade promptly in that topology. The Linux packages use the Boost.Beast backend and are unaffected. No configuration change is needed. See Security notices.
0.16.3¶
- ๐
check_nt(NSClientServer) answers the real nagios-plugins client again. Requests without a trailing newline used to hang until the client timed out (No data was received from host!) โ broken since 0.12.2. Remove any client-side timeout/retry workarounds; no configuration change is needed. If you expose this legacy endpoint, see the new guidance on securing it (password,allowed hostsand theallowcommand list).
0.16.2¶
- ๐ Check your setup Sensitive settings values are redacted on read. The REST settings
read endpoints (
GET /api/v2/settings/...,/descriptions) andnscp settings --list/--shownow return***for keys registered sensitive, matching thediffendpoint. No action required; tooling that read such a value back now receives***. See Security notices.
- ๐ The built-in
legacyWEB role is no longer seeded on fresh installs, and any role granting thelegacypermission now triggers aSECURITYwarning at startup (and fromnscp web add-role/add-user). Existing installs are unaffected โ the role stays in their config. Thelegacygrant unlocks the deprecated/query.pband/query/{name}query-dispatch endpoints, so a token with it can run any registered check/command; only grant it to trusted legacy systems. See Security notices.
0.16.1¶
- ๐ง RHEL/SUSE: workaround
ca=arguments can be dropped โ${ca-path}now resolves on its own (the explicit form still works). Packagers cross-building for another distribution should set-DCONFIG_CA_PATH=.
- ๐
check_logfileis unchanged unless you opt in tobookmark/max-lines. Adoptingbookmarkis a trade-off: a line is consumed when the check runs (not when its result is submitted) and positions are saved on clean shutdown, so a crash re-reports the backlog. Prefer an explicit bookmark name for a check whose filter changes often.
- ๐ง Check your setup Settings URLs with a query string now send it. A server that relied on receiving the bare path will now see the parameters. The offline-boot cache file is migrated to the query-aware name once on first start.
- ๐ท๏ธ Check your setup
${hostname}in an existing config changes meaning โ it is now expanded everywhereexpand_hostnameis used (including submit clients’hostname), where it used to be left as literal text.
- โฑ๏ธ
run on startupis off by default so the default install is unaffected; if you enable it for thedefaultschedule usestartup windowto avoid a thundering herd.
- ๐ Check your setup Building the HTML docs on non-Windows now needs
-DNSCP_BUILD_DOCS_HTML=ON.
0.14.1¶
- โ๏ธ Licence change: now distributed as Apache-2.0 OR GPL-2.0-only โ a clarification/relicensing with no code or runtime behaviour change. Review it if your organisation tracks bundled-software licences.
- ๐ Check your setup CheckNet perfdata is on by default. If you added perfdata manually, make sure you are not now emitting it twice.
- โ๏ธ Boolean check arguments (
option=true/option=false) now work from the CLI as well as REST; bare-flag usage is unchanged.
- ๐ Action required
CheckSecurityis not loaded by default. Enable it before using its checks (nscp settings --active-module CheckSecurity). Windows-only checks return UNKNOWN elsewhere.
0.12.6¶
- ๐ New permission policy layer, disabled by default. Existing installs
behave exactly as before until an operator sets
/settings/permissions/enabled = true. If you opt in: per-command rules apply to queries only (exec is gated by the separateallow execboolean, which defaultstrue); roll out withlog allows = truefirst to inventory real traffic. See Permissions.
- ๐ NRPEServer
client identity sourcedefaults tonone(previous behaviour). Set tocnonly after configuringverify_mode = peer-certand aca pathpinned to your private monitoring CA โ the system trust store would accept any public cert’s CN.
- ๐ Check your setup
[/paths]overrides from an older install moved to[paths]inboot.ini(same section name, different file). No automatic migration โ copy each entry across and delete the old section.
- ๐ WEB
disable admin user = trueis a new opt-in for status-only WEB exposure; existing installs keep their admin unchanged.
- ๐ Check your setup NRPEServer now survives a failed listener (logs an ERROR, leaves the module loaded) instead of failing the whole module. Add “NRPE listener failed” as a signal if you alerted on module-load failure.
- ๐ Check your setup
insecure = trueon NRPEServer now logs at ERROR (louder, behaviour unchanged) โ whitelist the message on agents intentionally run insecure.
0.12.5¶
- ๐ Check your setup
[/paths]users: copy entries into[paths]inboot.ini; the settings-side section is no longer consulted. Default installs are unaffected.
- ๐งฉ Check your setup Custom-plugin authors: implement the new optional
prepare_shutdowncallback if your module manages sockets or background threads โunloadis now a last-resort teardown.
- ๐ Monitoring-only WEB deployments:
disable admin user = trueunder[/settings/WEB/server]suppresses the built-in admin even on first boot; define your own read-only users (or a tightly scopedanonymousrole).
0.12.4¶
- ๐ Icinga
check_nscp_apiworks again after upgrade with no config change. For a non-stock probe, set[/settings/WEB/server] legacy query auth user agentsto a substring of its User-Agent. For the strict 0.12.3 behaviour (no query-string credentials at all), set that key to empty.
0.12.3¶
- ๐ Action required Audit
allowed hostson every node โ empty values now reject everything.
- ๐ Action required
check_nt(NSClientServer) now defaults tossl = true; setssl = falseexplicitly if your clients don’t speak TLS.
- ๐ Check your setup Replace clients that call
/auth/tokenor/auth/logoutwith the/api/v2/loginflow, and any that pass?TOKEN=/?__TOKEN=in the query string with a header-based token.
- โฑ๏ธ Action required Scheduler cron expressions on non-UTC hosts shift to local time โ update
them or set
[/settings/scheduler] timezone = utc.
- ๐ง Check your setup Review
check_service/check_process/check_filesfilters that relied on the old (now corrected) behaviours.
- ๐ Action required Restart and review the log for new “refused alias” / “rejected connection” warnings โ configurations that were previously silently accepted.
0.11.33¶
- โ
Check your setup No configuration migration required (new
proxykeys are opt-in). Thecheck_filesfixes change a few corner cases:max-depth=0now scans the top directory (#730); missing paths return UNKNOWN (#613); junction loops are not double-counted (#605); empty results return OK instead of UNKNOWN (#717). Review alerting that relied on the old corner-case behaviour.