Skip to content

Releases: mickem/nscp

0.13.0

08 Jun 03:55
972c01f

Choose a tag to compare

0.13.0 Pre-release
Pre-release

Improved Linux compatiblity

🌟 Highlights

The headline changes in this release — see the sections below for details:

  • Disk and file checks now work on Linux. CheckDisk (check_drivesize,
    check_files, disk I/O) is no longer Windows-only.
    (details)
  • First-class Linux packaging. The build follows the FHS / install prefix,
    with official .deb/.rpm for /usr.
    (details)
  • Easy installs everywhere. Windows via winget / Chocolatey / Scoop; the
    Linux web UI via nscp web install-ui.
    (details)
  • Secure by default. The web server refuses to serve cleartext HTTP without
    an explicit opt-in, plus check_nt command allow-listing and stricter
    external-script argument checks.
    (details)
  • Run Lua scripts straight from the CLI with nscp lua execute, backed by
    Lua thread-safety hardening.
    (details)
  • TLS improvements: outbound SNI and explicit Op5 client TLS options.
    (details)

⚠️ Upgrade — please read first

A few defaults were tightened for security and the Linux packaging layout
changed. None of these affect a normal Windows MSI upgrade, but Linux users
and anyone running the web server in cleartext should read this section.

1. The web server now refuses to run unencrypted by default

To stop NSClient++ from silently serving the REST API / web UI over plain HTTP,
the WEB server now refuses to start without a certificate unless you
explicitly opt in.

If you intentionally run the web server in cleartext (e.g. behind a TLS-
terminating proxy, or on an isolated network), set:

[/settings/WEB/server]
allow insecure = true

Otherwise, provide a certificate (certificate = …). If you do nothing and the
server has no certificate, it will log an error and not start the listener.

📖 WEBServer reference
· Web interface setup

2. The web UI is a separate download on Linux (.deb / .rpm)

The Linux packages no longer bundle the React/Vite web frontend (Debian/
Fedora policy forbids npm install during package builds). The daemon, REST
API, NRPE/NSCA listeners and every check module are still in the package — only
the browser UI ships separately.

After installing the package, fetch the matching UI bundle as root:

sudo nscp web install-ui      # downloads + verifies NSCP-Web-<version>.zip
sudo nscp web ui-status       # show installed version / source
sudo nscp web uninstall-ui    # remove only what install-ui put down

Until you do, the web port shows a small built-in placeholder page; the REST
API and all listeners work normally without it. The Windows MSI still bundles
the UI inline.

📖 Installing on Linux

3. Linux install layout now follows the FHS / install prefix

The Linux build honours CMAKE_INSTALL_PREFIX like a normal CMake project, and
the official .deb/.rpm are built for /usr. The file layout is now:

What Location
Daemon /usr/sbin/nscp
Modules /usr/lib/nsclient/modules
Private libs /usr/lib/nsclient
Config /etc/nsclient
State / logs /var/lib/nsclient · /var/log/nsclient

If you previously patched hardcoded paths to build for a custom location,
that is no longer needed — pass -DCMAKE_INSTALL_PREFIX=/opt/nsclient (or the
standard CMAKE_INSTALL_*DIR knobs) instead. To point an already-installed
daemon at a boot.ini in a non-standard place, there is a new override:

nscp service --run --path-override boot-conf=/etc/nsclient/boot.ini

📖 Choosing an install prefix
· File locations


✨ New features

Disk and file checks now work on Linux

CheckDisk is no longer Windows-only. Linux builds gain free-space, file and
disk-I/O checks, so the familiar commands work cross-platform:

# Free space on the root filesystem
check_drivesize --argument "drive=/" --argument "warn=used>80%" --argument "crit=used>90%"

# Age / size of a log file
check_files --argument "path=/var/log" --argument "pattern=*.log" --argument "crit=size>100M"

(Some Windows-only legacy commands are not registered on Linux.)

📖 Disk space scenario
· CheckDisk reference

Run Lua scripts straight from the command line

Useful for developing and debugging check scripts without wiring them into the
config first:

nscp lua execute --script myscript.lua

Lua also got thread-safety hardening (a proper GIL), new helpers for targeted
and forwarded queries, and clearer errors when a script fails to load.

Install on Windows with winget / Chocolatey / Scoop

NSClient++ is now published to the common Windows package managers:

winget install Mickem.NSClient
choco install nsclient
scoop install nsclient # Still pending approval

TLS improvements: SNI + Op5 client TLS options

  • SNI is now sent on outbound TLS connections (Graphite and the generic TLS
    client), so a TLS proxy hosting several certificates returns the right one.

  • The Op5 client gained explicit TLS settings:

    [/settings/op5/client/targets/default]
    tls version = 1.2+
    verify mode = peer
    ca = ${ca-path}

📖 Graphite reference
· Graphite scenario


🔒 Security hardening

Beyond the cleartext-HTTP default above:

  • check_nt can now be restricted to specific commands. The legacy
    check_nt protocol is password-only (and source-IP filtering is spoofable),
    so you can now limit which of its ten request codes are answered. Default is
    any (unchanged behaviour):

    [/settings/NSClient/server]
    # Answer only harmless system metrics; deny arbitrary counter/file reads
    # and service/process enumeration:
    allow = metrics, info

    A request outside the list is rejected with ERROR: Command not allowed.

  • Stricter shell-metacharacter checks in external scripts. User-supplied argument
    values containing more shell metacharacters are now rejected.

    📖 External scripts scenario

  • Graphite metric paths are sanitized before being written to the line
    protocol, preventing injection of extra metrics.

  • Python sys.path handling hardened to prevent code-injection via path
    manipulation.


🐛 Notable fixes & reliability

  • IPv6: listeners set IPV6_V6ONLY on Linux to avoid port conflicts with
    IPv4, and IPv6 address resolution was improved.
  • Thread-safety: logger subscriber management, the scheduler, and timer
    callbacks were made properly thread-safe; CommandClient now shuts down
    gracefully on POSIX signals.
  • collectd client: correct (little-endian) gauge encoding, working IPv6
    multicast, a configurable send interval (default 10s), and previously
    dropped metric types (counter/derive/absolute) are now mapped instead of
    discarded.
  • check_mk server: fixed a memory leak.
  • Lua Lua log lines report the actual script line number.

📦 Packaging & distribution notes

  • The bundled check_nsclient Nagios plugin moved to its own repository
    (mickem/check_nsclient) and is
    pulled in at build time. This only matters if you build from source.
  • Package/file names were normalised — double-check the exact asset name on
    the releases page if you script
    downloads.
  • Reduced Linux build dependencies: the build now uses libzip (instead of
    vendored Miniz), can use the system Google Test, and degrades cleanly when an
    optional dependency is missing. Linux uses the Boost.Beast web backend by
    default.

New Contributors

Full Changelog: 0.12.6...0.13.0

0.12.6

15 May 11:30
277d31b

Choose a tag to compare

New permission system

The release has three big stories — a new core permission system with optional client-cert principals on NRPE, a
PDH overhaul that fixes long-standing counter-collection crashes and adds counter functions, and a WEB hardening
option
that lets monitoring-only deployments expose the WEB UI without seeding a privileged admin account. Everything
else is bug fixes, small features, and follow-ups around those three threads.


Highlights

  • Core permission system — opt-in policy layer that gates which caller can run which command. Configured under
    /settings/permissions. Disabled by default; existing installs keep working.
    See https://nsclient.org/docs/concepts/permissions/ for the model, identity table, and rollout recipe.
  • NRPE client identity from cert CN — when client identity source = cn is set on NRPEServer and the listener
    verifies the client cert, the CN is stamped as the policy principal so rules can be written per-cert (
    NRPEServer:icinga-master = ...). Hard guardrail at module start refuses to load the module if the TLS verify mode
    would let the CN be attacker-supplied.
  • Global allow exec toggle — exec is now gated by a single on/off switch under /settings/permissions. The
    per-command rule table applies to queries only. Default true so enabling the policy system does
    not break exec callers.
  • PDH (performance counter) overhaul — fixes for service crashes when PDH misbehaves (#592, #547), counter retry
    when temporarily unavailable (#634), reliable English counter lookup (#652, #906), a resource leak in the
    counter-lookup path, and a refactor to smart-buffer-based PDH enumeration. Most users running CheckSystem on Windows
    should see meaningfully better reliability.
  • check_pdh counter scaling and functions (#281) — details-syntax and related rendering paths can now apply
    scaling and other functions, e.g. '${counter}'=${value:scale(/1024)}MB.
  • check_network — human-readable strings, scaling, speed, and percentages (#329); team-network statistics (#625).
    See https://nsclient.org/docs/reference/check/CheckNet.
  • Nagios range syntax in performance data (#748) — 1:10, ~:5, @10:20 etc. work in perfdata thresholds,
    matching the Nagios plugin spec.
  • disable admin user on WEBServer — monitoring-only deployments can expose the WEB UI without ever seeding the
    built-in admin (and previously seeded admin entries are ignored). Pairs naturally with the new permission system to
    lock down reconfiguration surfaces.
  • Path overrides moved to boot.ini + new --path-override CLI flag — path tokens (module-path,
    certificate-path, etc.) are now declared early in boot.ini so they take effect before the main config is loaded.
    Per-invocation overrides via --path-override KEY=VALUE. See https://nsclient.org/docs/concepts/settings.
  • NRPE startup is no longer fatal on listener failure — bad bind address / port already in use logs a clear error
    and leaves the module loaded so settings and commands stay usable for diagnostics.
  • Dual-stack listening fixed (#312) — v4 and v6 acceptors no longer trample each other's pending connection slot.
  • disable admin user, client identity source, allow exec, and the policy table are all documented in
    https://nsclient.org/docs/concepts/permissions/ and https://nsclient.org/docs/setup/securing. Treat those two as the
    starting point for any new
    install.

Detailed changes

Security and permissions

Core permission system
A policy layer in the core decides whether a given caller may run a given command. Disabled by default; when enabled,
rules form a strict allow-list.

[/settings/permissions]
enabled = true
log denials = true
log allows = false      ; noisy, only flip on while rolling out
allow exec = true       ; queries-only rule table; exec is a global toggle

[/settings/permissions/policies]
NRPEServer = CheckHelpers.*, CheckSystem.check_cpu
WEBServer:admin   = *
WEBServer:viewer  = CheckSystem.check_cpu, CheckSystem.check_drivesize
Scheduler = CheckHelpers.*, CheckSystem.*

Subject is module[:principal]; object is module.command. Wildcards (*, ?) supported. Rules combine additively.
See https://nsclient.org/docs/concepts/permissions/ for the full identity model, the
CheckHelpers identity-forwarding behaviour, and a step-by-step rollout recipe.

NRPE client cert CN as principal
When two-way TLS is configured and verifying client certs against your CA, the Common Name is stamped as the policy
principal:

[/settings/NRPE/server]
client identity source = cn        ; default: none
verify mode = peer-cert
ca = /etc/nsclient/ca.pem
[/settings/permissions/policies]
NRPEServer:icinga-master   = CheckHelpers.*, CheckSystem.*
NRPEServer:metrics-shipper = CheckSystem.check_cpu, CheckSystem.check_drivesize

Guardrails: the module refuses to start if client identity source = cn is configured without SSL, without
verify_mode containing peer and fail-if-no-peer-cert (or the peer-cert alias), or without a non-empty
ca path. The CN is logged at debug level on every accepted handshake for diagnostics. CN-only (not full DN) because
INI key syntax uses = as the key/value separator and would corrupt DN-shaped policy keys; see the "Why CN-only"
section of the permissions doc. See https://nsclient.org/docs/reference/client/NRPEServer.

Global allow exec toggle
Per-command rules apply to queries only. The exec surface (WEB scripts UI, lua/python core:simple_exec(...), CLI
exec) is gated by a single boolean:

[/settings/permissions]
allow exec = false   ; hard lockdown; default is true

When false and enabled = true, every exec call returns
Permission denied: exec is globally disabled (/settings/permissions/allow exec = false).
See "Why exec is a single toggle" in https://nsclient.org/docs/concepts/permissions/.

disable admin user on WEBServer
For installations that expose the WEB UI for status/visualisation only and never want a remote-reconfiguration surface:

[/settings/WEB/server]
disable admin user = true

With this set, the built-in admin is not seeded on first boot, and any existing admin entry in the user settings is
ignored at load time.

Security guide updates
https://nsclient.org/docs/setup/securing was rewritten with concrete configurations for NRPE (with
and without mTLS) and the WEB server. Read it before exposing either to a network you don't fully control.


Performance counters / PDH

The PDH subsystem (the Windows performance-counter collection backbone behind CheckSystem, check_cpu, check_pdh,
check_network, etc.) got a substantial reliability pass. Most users running NSClient++ as a long-running service on
Windows should see fewer crashes and more consistent results.

  • Service crashes when PDH misbehaves on a particular machine (#592, #547) — root-caused and fixed. Misbehaving
    counter registrations no longer take the service down.
  • Counter not retried if unavailable (#634) — counters that fail to bind at first sight now get retried on
    subsequent collection cycles, instead of being permanently unhealthy for the lifetime of the process.
  • English counter lookup improved (#652, #906) — addresses reading of localised counters by their canonical English
    names on non- English Windows installs.
  • Resource leak in PDH counter lookup fixed.
  • PDH enumeration refactored to smart buffers — clearer memory ownership across the enumeration path, fewer footguns
    for future changes.
  • check_pdh counter scaling and functions (#281) — all the details-syntax / rendering paths can now apply functions.
    Examples:
    check_pdh "counter=\Processor(_Total)\% Processor Time" \
              "details-syntax=${counter} = ${value:round(2)}%"
    
    See https://nsclient.org/docs/reference/check/CheckSystem for the function reference.

check_network

  • Human-readable strings, scaling, speed, and percentages (#329) — perfdata and message output now render numbers in
    a way operators actually want to read:
    check_network 'filter=interface=Ethernet' \
                  'top-syntax=${list}' \
                  'detail-syntax=${interface}: ${total_rx_human}/s in, ${total_tx_human}/s out'
    
  • Team network statistics (#625) — aggregate stats across Windows NIC teams.

See https://nsclient.org/docs/check/CheckNet.


Performance data formatting

  • Nagios range syntax in performance data (#748) — the perfdata threshold fields now accept the standard Nagios
    range syntax: 5:10, ~:5, @10:20, etc. Brings NSClient++ into line with what Nagios consumers already expect.

Settings, paths, and CLI

  • Path overrides moved to boot.ini — path tokens (module-path, certificate-path, data-path, log-path, …)
    now live under [paths] in boot.ini (next to nscp.exe), not in nsclient.ini. Overrides take effect before the
    main config is loaded — including the bootstrap step that decides where the main config itself lives.
    ; boot.ini
    [paths]
    module-path = D:\monitoring\modules
    certificate-path = D:\monitoring\certs
  • --path-override CLI flag — per-invocation override, repeatable. (Renamed from --path to avoid colliding with
    the nscp settings --path subcommand option.)
    nscp client --path-override module-path=/build/modules --path-override log-path=. ...
    
  • See https://nsclient.org/docs/concepts/settings for the precedence rules and the migration note for installs that had
    a [/paths] section in nsclient.ini.

Aliases and command registration

  • CheckHelpers alias — aliases can ...
Read more

0.12.5

13 May 20:00
f774742

Choose a tag to compare

0.12.5 Pre-release
Pre-release

Windows PDH overhaul, expression functions, boot.ini paths

This release lands a long-overdue stabilisation pass on the Windows PDH subsystem (multiple long-standing crashes and
counter-availability issues), adds first-class functions in detail-syntax / warn / crit expressions, and moves
path-resolver overrides from settings into boot.ini to unblock future moves of config and certificate storage.

Highlights

  • Windows PDH subsystem overhaul. Fixes #547 / #592 (service crash when PDH misbehaves on a particular machine),
    #634 (counters now retried when initially unavailable instead of staying broken until restart), and #652 / #906 (
    better English-counter fallback on non-English Windows).
  • Functions in expressions and templates (#281). format_bytes, convert_bytes, scale, composable with and/
    or/not — usable in detail-syntax, top-syntax, warn, crit, and filter. Today exposed by check_pdh;
    rolling out elsewhere.
  • check_network understands NIC teams (#625). New mode=adapter / mode=both reads
    Win32_PerfRawData_Tcpip_NetworkAdapter, which is the only source that reports the team aggregate.
  • Aliases in CheckHelpers. A new alias section under [/settings/check helpers/alias] provides the historical
    CheckExternalScripts alias mechanism without dragging in the external-scripts machinery. Preferred place for new
    aliases.
  • WEB: disable admin user option. Suppresses the built-in admin user entirely — for monitoring-only exposures
    where remote reconfiguration must be impossible even if credentials leak.
  • Plugin prepare-shutdown hook. Modules get a clean teardown phase before unload — listening sockets and pollers
    stop accepting work cleanly. Wired up in the network/scheduler modules.
  • Path overrides moved from settings to boot.ini. [/paths] in nsclient.ini is no longer consulted; a new
    [paths] section in boot.ini (and a --path KEY=VALUE CLI flag) take its place. This is a breaking change for
    the small number of users who relied on [/paths] — see Upgrade notes below.

Detailed changes

Windows PDH — stability overhaul

Long-standing instability in the PDH-based Windows performance-counter subsystem, addressed in one pass:

  • #547 / #592 — service crash when PDH misbehaves. Hardened the enumeration and lookup paths against the partial /
    inconsistent results PDH returns on certain machine states. PDH enumeration buffers were refactored to use smart
    buffers throughout, removing the manual sizing loops where the bug lived.
  • #634 — counters now retried when initially unavailable. Previously a counter that wasn't ready at boot would stay
    broken until the service was restarted; the collector now re-attempts on the normal collection cadence.
  • #652 / #906 — non-English Windows counter lookup. Improved the English-counter fallback path so checks that
    reference counters by English name keep working on localised installs.
  • Resource leak in PDH counter lookup — handle leaked on the error path of counter-name → counter-path resolution.

CheckSystem — expression functions and counter scaling

#281. The expression language now supports function calls, usable in any context that takes an expression (filter,
warn, crit) or a template (detail-syntax, top-syntax, perf-syntax). Use the %(...) placeholder form —
the legacy ${...} form cannot capture nested parentheses and cannot call functions.

Built-ins exposed by check_pdh today:

Function Purpose
format_bytes(value) Auto-scaled human bytes — 4194304 → "4MB" (1024-based)
format_bytes(value, 'MB') Fixed unit. B, K/KB, M/MB, G/GB, T/TB
convert_bytes(value, 'MB') Numeric value in the named unit — for thresholds
scale(value, divisor) Divide by an arbitrary divisor (e.g. 1 000 000 for Mbps)
# Threshold in MB, display human-friendly
check_pdh counter=memory_bytes \
  "warning=convert_bytes(value, 'MB') > 500" \
  "detail-syntax=%(alias) = %(format_bytes(value))"

# Network rates as Mbps (decimal — use scale, not convert_bytes)
check_pdh counter=bytes_per_sec \
  "detail-syntax=Speed = %(scale(value, 1000000)) Mbps"

check_pdh also exposes variable-style shortcuts (value_human, value_mb, value_gb, …) — syntactic sugar for the
corresponding format_bytes / convert_bytes calls. Reach for variables when one of the prebuilt units fits; reach for
functions when you need a custom unit, a custom divisor, or composition with other expressions.

CheckSystem — check_network NIC team support

#625. The default mode=interface reads Win32_PerfRawData_Tcpip_NetworkInterface (one row per physical adapter —
does not report team aggregates). New modes:

  • mode=adapter — reads Win32_PerfRawData_Tcpip_NetworkAdapter, which includes the team aggregate as a virtual
    interface named after the team. The team aggregate is the row with no matching Win32_NetworkAdapter MAC entry, so it
    can be selected with filter=MAC = ''.
  • mode=both — returns both sources, tagged with a new source keyword for filtering.
# Monitor a NIC team aggregate
check_network mode=adapter "warn=total > 100M" "crit=total > 500M"

# Alert only on the team adapter
check_network mode=adapter "filter=MAC = ''"

CheckHelpers — aliases

Aliases (a fixed command + fixed argument list exposed under a new name) have historically lived in
[/settings/external scripts/alias], requiring CheckExternalScripts to be loaded even when the alias only
invoked internal commands. A new section under [/settings/check helpers/alias] provides the same mechanism in
CheckHelpers, with no external-scripts dependency.

[/modules]
CheckHelpers = enabled

[/settings/check helpers/alias]
my_check_cpu = check_cpu warn=load>80 crit=load>90
my_check_process = check_process "process=$ARG1$" "crit=state != 'started'"

Both modules can coexist; each reads its own section. Last-loaded wins on name collisions — pick one as the home for
new aliases so you don't have to remember which is which.

WEBServer — disable admin user (cccc14e4)

New boolean under [/settings/WEB/server] that suppresses the built-in admin user entirely: it is not seeded on first
boot, any pre-existing admin row in [/settings/WEB/server/users] is dropped at load time, and the "no users → re-add
admin" fallback is skipped. For monitoring-only WEB exposures where remote reconfiguration must be impossible even if
credentials leak.

[/settings/WEB/server]
disable admin user = true

[/settings/WEB/server/users/readonly]
password = ...
role = monitoring

Mirrored on the install command:

nscp web install --disable-admin

Mutually exclusive with --password (the install would create no user, so a password would have nowhere to go — the
command refuses explicitly).

Service — prepare_shutdown plugin hook

Plugins now receive a prepare_shutdown callback before unload, giving them a chance to flush state, stop accepting
new work, and tear down listening sockets cleanly rather than racing the unload. Wired up in NRPEServer, NSCAServer,
NSClientServer, CheckMKServer, WEBServer, and Scheduler. The callback is optional — custom plugins built against
the older API continue to work unchanged.

Service — path overrides via boot.ini and --path CLI (fbdfe257, d2075b99)

Path-resolver tokens (module-path, certificate-path, log-path, cache-path, scripts, web-path, …) used to be
overridden via [/paths] in nsclient.ini. That doesn't work for the upcoming move of writable state out of the
install directory: the path resolver is needed before the main INI is opened, so overriding where the INI lives must
happen earlier.

The override location is now boot.ini:

; boot.ini
[settings]
common = ini://${shared-path}/nsclient.ini

[paths]
module-path = C:\Program Files\NSClient++\modules
log-path = D:\nscp\logs
cache-path = D:\nscp\cache

A --path KEY=VALUE CLI flag layers on top of boot.ini and wins — useful for build tooling and CI:

nscp service --run \
  --path module-path=C:\build\modules \
  --path log-path=C:\build\log

IcingaClient — built-in alias and container test

Adds a built-in alias for the standard Icinga submission flow and a Docker-based end-to-end test so the integration is
exercised on every build.

simpleini — NUL-termination fix for non-UTF-8 INI files

The INI loader passed an explicit length to mbstowcs, but per POSIX mbstowcs(NULL, src, n) ignores n and scans
until \0. On non-UTF-8 stores the size probe could walk past the buffer. The buffer now carries an explicit
terminator.

Upgrade notes

  • [/paths] users: if you had a [/paths] section in your nsclient.ini, copy the entries into [paths] in
    boot.ini. The settings-side section is no longer consulted. The default install does not use [/paths] and is
    unaffected.
  • Custom-plugin authors: the new prepare_shutdown callback is optional. If your module manages sockets or
    background threads, you should implement it — unload is now expected to be a last-resort teardown rather than the
    place where listeners get stopped.
  • check_pdh configs using ${...} for function calls: there are none today (the feature is new), but if you adapt
    examples from third-party docs that use ${format_bytes(...)}, rewrite to %(format_bytes(...)). The ${...} form
    stops at the first } and cannot parse nested parentheses.
    ...
Read more

0.12.4

13 May 03:20
58b7142

Choose a tag to compare

0.12.4 — Regression fixes for Icinga and CheckSystem

This is a maintenance release focused on regressions introduced since 0.12.3.
No new features; no breaking changes for configurations that don't hit the items below.

Highlights

  • Icinga check_nscp_api works again. The query-string credential path was removed in 0.12.3 for security (commit
    340b8db1). That hardening broke Icinga's bundled check_nscp_api plugin, which still passes the password as
    ?password=.... This release reinstates the legacy path behind a User-Agent allowlist (default: clients whose
    User-Agent matches Icinga/check_nscp_api) — every other client keeps the strict post-340b8db1 rejection.
  • Better "module not found" messages on Windows. When a configured module fails to load, the error now points at the
    WiX installer feature that ships the module (e.g. NRPEServer → "NRPE Support"), so operators
    can fix the cause (re-run the installer and tick that feature) without reading source.
  • IcingaClient.dll is now in the installer. The DLL was being built but not packaged, so the corresponding
    Op5/Icinga client features were unusable on stock Windows installs.
  • os_updates.status keyword renamed to update_status. The previous name clashed with the built-in status
    keyword every check exposes, which made filter / detail-syntax expressions ambiguous on check_os_updates. Any custom
    config that referenced os_updates.status must be updated — see Behaviour change below.
  • check_wmi no longer crashes on warn/crit filters. A use-after-mutation in the WMI row iterator caused an access
    violation whenever a warn= or crit= filter touched a column value (e.g.
    check_wmi "query=Select Version from win32_OperatingSystem" "warn=Version not like '6.3'"). Affected every
    filter that exercised the post-iteration deferred-evaluation path.

Detailed changes

WebServer — legacy query-string authentication restored for specific clients (94b2057d)

The 0.12.3 hardening removed three paths because URL-borne credentials and tokens leak into browser history, proxy logs,
and Referer headers:

  • GET/POST /auth/token?password=...
  • GET/POST /auth/logout?token=...
  • ?TOKEN=... / ?__TOKEN=... as a session-token fallback on any endpoint

Removing them broke Icinga's bundled check_nscp_api plugin, which still ships with the query-string mechanism. To
unblock that integration without re-opening the vector to browsers and arbitrary scrapers, this release gates the legacy
paths on a User-Agent allowlist:

  • New setting [/settings/WEB/server] legacy query auth user agents. Comma-separated list of User-Agent substrings (
    case-insensitive). A request whose User-Agent contains any pattern is allowed to use the legacy query-string
    mechanism; everything else still gets the 0.12.3 rejection (410 Gone on /auth/*, 403 on ?TOKEN=).
  • Default: Icinga/check_nscp_api — anchors on the specific plugin name, so unrelated tooling that merely mentions "
    Icinga" in its User-Agent doesn't slip through.
  • Set to an empty string to disable the fallback entirely (matches the strict 0.12.3 behaviour).
  • The 410 / 403 rejection log lines now mention this setting as the escape hatch so operators don't have to dig through
    source to find it.

Security posture, in short: this is not a defence against malicious clients — an attacker can spoof the User-Agent — but
it keeps the legacy vector off the default surface for browsers, scrapers, and anything else that isn't
check_nscp_api.

Service — installer-feature hints in module-load errors (793c3ee1)

When a referenced module's DLL isn't on disk (typically because the operator didn't tick the relevant feature in the
Windows installer), the error now ends with a hint:

Failed to load NRPEServer: <reason> (module 'NRPEServer' is part of the 'NRPE Support' installer feature; re-run the 
NSClient++ installer and enable that feature, or see installers/installer-NSCP/Product.wxs for the full feature map)

Covers every module shipped by the MSI: CheckPlugins (the bulk of check_* modules), NRPE Support, Check MK Support,
NSCA / NSCA-NG, WEB Server, Lua / Python scripting, OP5 / Elastic / Icinga client, etc.

Hint is Windows-only — on Linux the package manager handles module installation and the hint would be misleading.

Installer — IcingaClient.dll added (3a9af3cf)

IcingaClient.dll is built by the CheckSystem solution but was missing from Product.wxs, so it was never shipped. The
Op5 → Icinga integration path was effectively broken on stock Windows installs. The DLL is now in the "Various client
plugins" feature alongside GraphiteClient, SMTPClient, SyslogClient, etc.

CheckSystem — check_os_updates keyword rename (cf3613e2)

The check_os_updates filter previously exposed a per-item field called status (overall update status: up_to_date /
pending / error).
Every check also exposes a built-in top-level status (OK / WARNING / CRITICAL / UNKNOWN), so filter and detail-syntax
expressions like status = 'pending' were ambiguous — a regression caught by users upgrading from 0.11.x. The per-item
field has been renamed to update_status.

The built-in status keyword (OK/WARNING/CRITICAL) is unaffected.

Upgrade notes

  • Icinga users: check_nscp_api should start working again after the upgrade with no config changes. If you have a
    non-stock Icinga probe that uses a different binary name, set [/settings/WEB/server] legacy query auth user agents
    to a substring matching its User-Agent (or to plain Icinga to broaden the match beyond the default).
  • Strict-deployment operators: if you want the strict 0.12.3 behaviour (no query-string credentials, no exceptions),
    set [/settings/WEB/server] legacy query auth user agents = (empty).

Full Changelog: 0.12.3...0.12.4

0.12.3

11 May 04:17
85137ca

Choose a tag to compare

Protocols, Security and bug fixes

This release rolls up everything since the last stable: five pre-releases (0.11.31, 0.11.32, 0.11.33, 0.12.1,
0.12.2) plus the latest in-development changes.

The headline themes are:

  1. New monitoring scenarios — first-class Checkmk and Icinga 2 integration, plus a real check_net family.
  2. A modern Web UI and REST API — events, metadata, settings DELETE, filterable lists, dedicated widgets for PDH
    counters and real-time filters.
  3. Hardened by default0.12.2 is a security release that closes listener defaults that used to be silently
    permissive (empty allowed hosts, plaintext check_nt, query-string tokens, etc.).
  4. Many long-standing check fixescheck_service, check_process, check_files, check_drivesize,
    check_uptime, CheckLogFile, and the shared filter/threshold engine all behave correctly now.

Read the Breaking changes section before upgrading — several long-standing-but-incorrect
behaviours have been corrected and a number of listener defaults are now fail-closed. If you have an existing
configuration, plan to review it.


TL;DR for end users

  • New scenario: Checkmk agent integration. Point a Checkmk site at port 6556 and you get a native-looking agent
    dump. See scenarios/check-mk.md.
  • New scenario: Icinga 2 passive submission. A new IcingaClient module submits passive results to Icinga 2's REST
    API as an alternative to NSCA / NRDP.
  • New scenario: NSCA-ng. A new hardened NSCAngClient with PSK and AEAD-first cipher selection.
  • Native cross-platform network checks: check_tcp, check_dns, check_http, check_ntp_offset,
    check_connections.
  • Native Windows registry checks: check_registry_key, check_registry_value.
  • HTTP proxy support for every HTTP-based client (NRDP, Elastic, Op5, Icinga, the configuration loader, ...).
  • Windows ROOT trust store auto-export — HTTPS-bound checks validate certificates against the system trust store
    automatically.
  • A modern Web UI with filterable lists, settings diff, dashboard, and dedicated CheckSystem widgets.
  • New REST endpoints: GET/DELETE /api/v2/events, GET /api/v2/metadata, DELETE /api/v2/settings/.... Covered
    in api/rest/.
  • Linux real-time metrics — the same background CPU/memory/disk/ network/load sampling that Windows has had for
    years.
  • Many bug fixes in check_service, check_process, check_files, CheckLogFile, the filter/threshold engine and
    the HTTP stack.

Major new features

Checkmk agent integration

NSClient++ can now serve a Checkmk-compatible agent dump on TCP port 6556. A real Checkmk site can register the host
with tag_agent = cmk-agent, discover services, and run checks — no proxy, no NSCA gateway.

Enable it:

[/modules]
CheckMKServer = enabled
LUAScript = enabled
CheckSystem = enabled
CheckDisk = enabled
CheckHelpers = enabled

[/settings/check_mk/server]
port = 6556
allowed hosts = 127.0.0.1, <checkmk-site-ip>
submission ttl = 60          ; seconds, default 60
mrpe channel = check_mk-mrpe
local channel = check_mk-local

Out-of-the-box sections (no extra config):

Section Contents
<<<check_mk>>> Version, OS, hostname
<<<systemtime>>> Unix epoch (Windows clock-skew check)
<<<uptime>>> Seconds since boot (read from internal metrics store)
<<<mem>>> MemTotal:/MemFree:/SwapTotal:/SwapFree: (from metrics store)
<<<df>>> Per-volume size/used/free/mountpoint (Windows)
<<<services>>> name state/start_type display_name per Windows service
<<<ps>>> (user,vsz_kb,rss_kb,cputime,pid) cmdline per process

Expose any nscp check as a Checkmk service under <<<local>>>:

[/settings/check_mk/server/local]
CPU Load = command=check_cpu warn=load>80 crit=load>95
Disk C = command=check_drivesize drive=C: "warn=free<20%" "crit=free<10%"

MRPE relay under <<<mrpe>>>:

[/settings/check_mk/server/mrpe]
Uptime = command=check_uptime warn=uptime<2d
Memory = command=check_memory type=committed warn=used>80% crit=used>90%

Documentation: https://nsclient.org/docs/scenarios/check-mk.md`.

IcingaClient — Icinga 2 REST API submission

A new client module submits passive check results directly to an Icinga 2 master/satellite via the
/v1/actions/process-check-result REST endpoint, as an alternative to NSCA or NRDP.

[/modules]
IcingaClient = enabled

[/settings/IcingaClient/targets/default]
address = https://icinga2.example.com:5665
username = nscp
password = secret
hostname = ${hostname}
nscp client --module IcingaClient \
            --command submit_icinga \
            --address https://icinga2.example.com:5665 \
            --username nscp --password secret \
            --command heartbeat \
            --result 0 \
            --message "Hello from NSClient++" \
            --ensure-objects

NSCA-ng client

A new NSCAngClient module ships a hardened NSCA-ng submission client with PSK support, AEAD-first cipher selection,
and connection retry logic.

Native support for Windows CA-store

On startup NSClient++ now exports the machine's ROOT certificate store as a single PEM bundle, so any check that does
TLS (check_http, IcingaClient, NRDP, ...) can validate certificates against the trust store the rest of Windows
already uses.

check_http url=https://www.ibm.com
OK: https://www.ibm.com -> 303 ok (0B in 33ms)

check_http url=https://self-signed.badssl.com/
CRITICAL: https://self-signed.badssl.com/ -> 0 error: Failed to connect ... certificate verify failed

CheckNet — five new (cross-platform) checks

CheckNet graduated from a placeholder into a full network-check module. All five commands work over NRPE as well as
locally:

  • check_tcp — open a TCP socket to one or more host/port pairs, optionally send a payload and require an expected
    substring.
  • check_dns — resolve a hostname and optionally assert which addresses come back.
  • check_http — fetch one or more URLs, check status code, response time and body content; supports custom headers
    and user-agent.
  • check_ntp_offset — query one or more NTP servers and alert on offset / stratum.
  • check_connections — Windows TCP/UDP connection table inspection (counts per protocol/family/state).
check_tcp host=smtp.gmail.com port=25 send="EHLO nsclient.org" expect="250"
check_dns host=google.com expected-address=172.217.20.174
check_http url=https://nsclient.org/ expected-body="NSClient" \
    "warn=time > 500 or code >= 400" \
    "crit=time > 2000 or code >= 500 or result != 'ok'"
check_ntp_offset "servers=0.pool.ntp.org,1.pool.ntp.org" timeout=2000
check_connections "filter=protocol = 'tcp' and state = 'TIME_WAIT'" \
    "warn=count > 200" "crit=count > 1000"

CheckSystem (Windows) — registry checks

Two new commands let you monitor the Windows registry directly from NSClient++ instead of relying on external scripts.
They support recursion, exclude lists, 32/64-bit (WoW64) views, custom filters and the usual warn=/crit= expression
syntax.

  • check_registry_key — verify that a key exists, count sub-keys/values, watch its last-write time.
  • check_registry_value — read a single value assert its type, size or content.
check_registry_key "key=HKLM\Software\NSClient" \
    "warn=age > 7d" "crit=age > 30d or not exists"

check_registry_key "key=HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" \
    recursive max-depth=1 exclude=KB5005463 exclude=KB5005539

check_registry_value "key=HKLM\System\CurrentControlSet\Services\W32Time\Config" \
    value=MaxPollInterval "warn=int_value > 14" "crit=int_value > 17"

CheckSystem — check_os_updates (Windows)

A new check using the Windows Update Agent (WUA) reports pending OS updates. By default any pending update returns
warning; thresholds let you alert only on security/critical:

check_os_updates "warning=important > 0" "critical=security > 0 or critical > 0"

CheckSystem (Linux) — real-time metrics

The Linux build of CheckSystem now ships with the same real-time metric collection that has been available on Windows
for a long time: CPU, memory, disk, network and load are sampled in the background and exposed both to
dashboards/metrics and to real-time filters (filter=... rules that fire when a threshold is crossed). Existing
real-time filter configuration just works on Linux now.

Real-time filter metrics

CheckSystem's real-time filters now publish per-filter match and error counts under
system.realtime.<filter_name>.fired / system.realtime.<filter_name>.errors. Visible via:

  • The metrics REST endpoint (/api/v2/metrics + filter)
  • Prometheus scrape
  • The new Metrics() Lua API in default_check_mk.lua

Useful for spotting filters that never fire (typo in the where-clause) or filters that always error (broken
expression).

CheckDisk — check_single_file

A focused variant of check_files for inspecting a single, known path. Compared to using check_files for the same
job:

  • Only one required argument (file=<path>).
  • A clear error when the input is empty.
  • UNKNOWN: File not found: <path> when the file is missing — in...
Read more

0.12.2

08 May 18:03
57b05dc

Choose a tag to compare

0.12.2 Pre-release
Pre-release

What's Changed

This release follows 0.12.1 and is a security-hardening release. Many listener-side defaults are now safer, several long-standing bugs are fixed, and a handful of new modules and commands ship. Read the breaking changes section before upgrading — a few defaults changed in ways that can refuse connections or fail startup if your existing configuration relied on the old behaviour.


⚠️ Breaking changes

Empty allowed hosts now rejects all connections (fail-closed)

Previously an empty allowed hosts = setting was treated as "allow any source". It now rejects every connection and logs the reason.

If you genuinely want to expose the agent to any source, set it explicitly:

allowed hosts = 0.0.0.0/0,::/0

The change is to prevent configurations from drifting to empty by mistake silently turning into open listeners.

Scheduler cron expressions now evaluate in local time by default (fixes #570)

The Scheduler module previously used UTC as its reference clock, so a cron entry like 40 15 * * * fired at 15:40 UTC regardless of the host time zone. The default has changed to local time, which matches standard cron semantics — that entry now fires at 15:40 host-local time. Hour and minute fields in cron schedules will shift accordingly on non-UTC hosts.

A new timezone setting under the [/settings/scheduler] section controls the reference clock:

[/settings/scheduler]
timezone = local                          ; default — standard cron semantics
; timezone = utc                          ; restore the pre-0.12 behaviour
; timezone = EST-05EDT,M3.2.0,M11.1.0     ; any POSIX TZ string is honoured

Accepted values match the existing NSClient++ timezone semantics (shared with check_uptime): local (default), utc/gmt, or any POSIX TZ string parseable by Boost. IANA names such as Europe/Stockholm are not supported — use the POSIX form. Unparseable values fall back to UTC and surface as UTC? in any timezone label so the misconfiguration is visible.

check_nt (NSClientServer): TLS is on by default

The legacy check_nt protocol carries the password in every request, so the listener now defaults to ssl = true. The server does not refuse to start if TLS is off — check_nt's SSL path has always been best-effort and many third-party clients never implemented it — but it will log a startup warning when TLS is off and another when a password is configured, suggesting a switch to NRPE or NSCA-ng.

If you need to keep the old plaintext behaviour for legacy clients, set ssl = false explicitly in the [/settings/NSClient/server] section.
The change is intentionally an opt-out: defaulting on makes "no transport security" a deliberate, visible choice rather than a silent
default.

check_nt: the literal password None no longer authenticates

Previously, if no server password was configured, a client sending the literal word None was accepted. An empty server password now rejects all requests outright. Configure a password (or, preferably, switch to a modern protocol such as NRPE or NSCA-ng).

The listener now also returns a generic ERROR: Bad request. instead of distinct Invalid password / No command specified errors, removing the oracle that allowed online password guessing.

WEBServer: /auth/token and /auth/logout removed (HTTP 410)

These legacy endpoints accepted the password and session token as URL query parameters, which leaked credentials into browser history, proxy logs, and Referer headers. They now return 410 Gone.

Migrate to:

  • POST /api/v2/login with Authorization: Basic to obtain a token
  • DELETE /api/v2/login with Authorization: Bearer to log out

WEBServer: ?TOKEN= / ?__TOKEN= query-string token auth removed

For the same reason, supplying the session token as a URL query parameter on any other endpoint is no longer accepted. The server logs the rejection and returns 403. Send the token in one of the header-based forms instead:

  • Authorization: Bearer <token>
  • TOKEN: <token>
  • X-Auth-Token: <token>

WEBServer: anonymous access is now opt-in

A role named anonymous registered in settings is silently ignored unless the new allow_anonymous flag is enabled. Two knobs are required to expose endpoints without authentication; one accidental setting is no longer enough.

CheckExternalScripts: malformed alias commands are refused at startup

The fallback "split-on-space" parser used when the principal command parser failed has been removed. Aliases whose command line does not parse cleanly are now refused with an error in the log instead of being silently registered with surprising tokenisation. Review your logs after upgrading and fix any flagged alias definitions.

WEBServer: existing admin user is no longer overwritten

If you manually configured the admin user, it will no longer be overwritten on subsequent boots. This may change behaviour for
deployments that relied on the password being reset to the default on restart.


✨ New modules and features

NSCA-ng client (#1255)

A new NSCAngClient module ships, providing a hardened NSCA-ng submission client with PSK support, AEAD-first cipher selection, and connection retry logic.

SMTPClient module rewrite

The SMTPClient module has been substantially rewritten with proper SMTP handling, integration tests, and a Python-based test harness.

check_nscp_update command

A new check command that queries the GitHub releases API (with caching) and reports whether the running NSClient++ is up to date. The User-Agent includes the current version.

nscp settings --sort

The settings CLI gained a --sort flag to produce stable, sorted output — useful for diffing exported settings between hosts.

Performance threshold min/max bounds

Performance data threshold expressions can now declare minimum and maximum bounds, propagated into the emitted perfdata.

check_pdh "counter=\\Processor(_Total)\\% Processor Time"  "perf-config=*(minimum:0;maximum:100)"

CheckDisk: filesystem filtering for drive size checks

check_drivesize can now filter drives by filesystem type (for example, to exclude tmpfs or nfs mounts).

check_drivesize drive=* "filter=fs = 'NTFS'"

Timezone-aware check_uptime

check_uptime now correctly applies a timezone cache on both Windows and Unix, so absolute boot-time output and filter expressions agree with the host's local time.

WEBServer: cookie attribute support

Cookies can now declare Secure, HttpOnly, SameSite, Path, Domain, Expires, and Max-Age attributes.

WEBServer: password hashing with constant-time verification

User passwords are now hashed and verified with a constant-time comparison, removing the timing oracle on the previous plaintext
equality check.

WEBServer: authentication rate limiter

A new per-source rate limiter throttles failed authentication attempts to slow online brute-force attacks. Two settings tune (or disable) it:

[/settings/WEB/server]
auth rate limit max failures   = 10   ; 0 disables the limiter
auth rate limit block seconds  = 60

🐛 Bug fixes

  • #581CheckLogFile now honours the line-split argument (previously hard-coded to \n); multi-character delimiters such as
    \r\n are also handled correctly.
  • #589 — Time/duration arguments such as time=3000foobar or time=3000mfoobar are no longer silently accepted; malformed inputs are now rejected with a clear error.
  • #669 — The literal U (Nagios "undefined") in performance data is preserved end-to-end instead of being coerced to 0. Only an exact U, u, U%, or u% token matches; values like Unknown or Unicorn no longer trigger the marker.
  • #570 — Scheduler cron expressions now run in local time (see breaking change above).
  • #419, #595, #370, #205 — Several long-standing settings/format issues, fixed alongside nscp settings --sort.
  • CheckLogFile real-time seek behaviour fixed; CRLF handling harmonised.
  • Metrics collection regression fixed (some metrics were silently dropped).
  • NSCA wire timestamps are now correctly built in UTC. Previously both the server (IV packet) and client (data packet) emitted
    seconds-since-epoch derived from second_clock::local_time(), which drifted by the host's TZ offset. The new replay-window check exposed the bug as skew=-7200s-style errors on non-UTC senders; the wire format itself was always supposed to be Unix time.
    A timezone setting is available on both ends for legacy interop:
    • [/settings/NSCA/server] timezone = utc (default)
    • [/settings/NSCA/client/targets/<target>] timezone = utc (default;
      also --timezone on the CLI)
      Set both ends to local (or any POSIX TZ string) only when peering with an old agent that emits local-clock-as-Unix-time stamps. Both
      ends must agree.
  • Connection and process logging is more verbose at error/timeout boundaries to aid traceability.

🔒 Security hardening (defense in depth)

These do not change documented behaviour for well-formed traffic but close down attacker-controlled edge cases.

DoS / resource-exhaustion limits

  • Authorization header capped at 8 KiB to mitigate amplification.
  • Per-connection parser buffer cap to prevent memory pinning from oversized or never-completed requests.
  • Session token cap with eviction to prevent unbounded memory growth.
  • Payload lengths below the protocol minimum are rejected before allocation.
  • Path expansion now detects cycles and refuses to recurse, preventing stack overflow on pathological configurations.

NSCA hardening

  • Packet version is checked.
  • Timestamp validation tightened to mitigate replay attacks.

Log/output ...

Read more

0.12.1

03 May 17:05
446c410

Choose a tag to compare

0.12.1 Pre-release
Pre-release

NSClient++ Release Notes

This release is dominated by a long-overdue cleanup of the Windows check_service / check_process paths, a
substantial overhaul of how thresholds (warn= / crit= / ok=) are evaluated against summary variables such as
count, a brand-new * IcingaClient* module for submitting passive results to Icinga 2's REST API, an HTTP stack
refactor, and finally support for Windows CA-store.

A large number of long-standing GitHub issues are closed by this release.


TL;DR

  • New module: IcingaClient (Icinga 2 REST API passive submission).
  • Windows ROOT store auto-export — service exports the system trust store to ${ca-path} at every boot, so HTTPS-bound
    checks (check_http, Elastic, Op5, Icinga, Graphite, NRDP, Syslog) "just work" without manually staging a CA bundle.
  • check_service is finally correct on busy machines, on mixed start-type services, and when perf-syntax=none is
    used.
  • check_process now sees protected / cross-user processes when running as NETWORK SERVICE, and the realtime path is
    finally case-insensitive like Windows itself.
  • The filter engine now evaluates warn= / crit= after iteration, so summary variables such as count are stable,
    and mixed expressions are evaluated correctly even when no rows match.
  • HTTP client/server code: separate request/response types, chunked transfer decoding, normalised headers.

See Breaking changes at the bottom for behavioural changes you may need to react to in existing configurations.


New features

IcingaClient — Icinga 2 REST API submission

A new client module that submits passive check results directly to an Icinga 2 master/satellite via the
/v1/actions/process-check-result REST endpoint, as an alternative to NSCA or NRDP.

Real-world example — submit a check from a scheduled task / NSCP console:

[/modules]
IcingaClient = enabled

[/settings/IcingaClient/targets/default]
address = https://icinga2.example.com:5665
username = nscp
password = secret
hostname = ${hostname}
# submit a passive result
nscp client --module IcingaClient \
            --command submit_icinga \
            --address https://icinga2.example.com:5665 \
            --username nscp --password secret \
            --command heartbeat \
            --result 0 \
            --message "Hello from NSClient++" \
            --ensure-objects

Native support for Windows CA-store

On startup NSClient++ will export the machine's ROOT certificate store as a single PEM bundle, so any check that does
TLS (check_http, IcingaClient, NSCA over TLS, ...) can automatically validate certificates.

Real-world example — verify an internal HTTPS endpoint signed by your enterprise CA that is already trusted by Windows:

heck_http url=https://www.ibm.com
OK: https://www.ibm.com -> 303 ok (0B in 33ms)

check_http url=https://self-signed.badssl.com/
CRITICAL: https://self-signed.badssl.com/ -> 0 error: Failed to connect to self-signed.badssl.com:443: certificate verify failed (SSL routines) (0B in 0ms)

check_service fixes

"Failed to enumerate service: 6f7" on busy hosts

Enumerating service might fail on server with many services.
The enumeration is now properly looped until the SCM signals end-of-data.

perf-syntax=none actually suppresses perfdata

check_service was emitting a stream of empty perfdata aliases (''=4;0;1 ''=4;0;1 ...) even when the user set
perf-syntax=none, making the output unusable over size-limited transports such as NRPE.

Real-world example — quietly checking 200 services over NRPE:

# before: blew past the 1 KB / 4 KB NRPE response limit
# after:  no perfdata at all, message stays small
check_service "filter=start_type='auto'" "warn=state!='running'" "perf-syntax=none"

no more TODO leaking into ${desc}

When using service=<name> instead of a filter=, the display name was constructed with the literal string "TODO"
and overwritten later. In some instances this was read before being populated causing TODO to end up in check results.

Real-world example:

# before
$ check_service service=Spooler "syntax-detail=${name}: ${desc}"
OK: Spooler: TODO

# after
OK: Spooler: Print Spooler

delayed only reported for SERVICE_AUTO_START

QueryServiceConfig2(SERVICE_CONFIG_DELAYED_AUTO_START_INFO) only returns a meaningful value for auto-start services.
The old code checked the delayed flag before the start type, so manual / boot / system / disabled services could
randomly show up as delayed / delayed-trigger.

Real-world example:

# A service configured "Manual" used to render as start_type=delayed.
# Now:
$ check_service service=MyManualSvc "syntax-detail=${start_type}"
OK: manual

check_process fixes

see protected / cross-user processes as NETWORK SERVICE

When NSClient++ runs under a non-administrative account it cannot OpenProcess(PROCESS_QUERY_INFORMATION) on critical
processes (csrss.exe, smss.exe, services.exe, winlogon.exe, ...) or on processes owned by other users — they
were silently dropped from the enumeration, causing false CRITICAL: <name>=stopped.

A third fallback using PROCESS_QUERY_LIMITED_INFORMATION + QueryFullProcessImageName is now attempted. The process
is visible by name and PID; detailed metrics (handle counts, VM, command line, modules) remain unavailable for those
processes because they require broader rights.

Real-world example — service is installed to run as NT AUTHORITY\NetworkService:

# before:  CRITICAL: winlogon.exe=stopped
# after:   OK: winlogon.exe=started
check_process "process=winlogon.exe" "crit=state!='started'"

case-insensitive process= in realtime

Processnames were not caompared case insensetive so process=notepad.exe failed to match a process whose on-disk image
name was NOTEPAD.EXE.

Real-world example:

[/settings/system/windows/real-time/checks/notepad]
alias = notepad
filter = process = 'notepad.exe'
crit = count > 0

This now fires regardless of how Windows happens to capitalise the image name.


Filter engine — stable summary thresholds

These changes touch the shared filter / threshold engine and therefore affect every modular check (check_files,
check_service, check_process, check_eventlog, ...).

stable count / total / *_count in warn= / crit=

warn= / crit= were evaluated during iteration. Summary variables such as count therefore exposed their running
value instead of the final post-iteration value, so a mixed expression like

crit = state = 'hung' OR count < 5

mis-fired on the very first row (count == 1 < 5) regardless of how many rows ultimately matched.

Per-row evaluation is now deferred: matched rows are recorded during iteration, and the warn/crit/ok engines run in
match_post() once the summary state is final. Realtime checks now also call match_post() so the deferred verdict is
materialised before the realtime helper inspects the return code.

Real-world impact:

# "alert if any process is hung, OR if fewer than 5 are alive"
check_process "filter=name='myworker.exe'" "crit=state='hung' OR count<5"
# Pre-fix: always CRITICAL on the first row.
# Post-fix: CRITICAL only when the final count of matching rows is < 5
#           (or any matched row is hung).

mixed warn= / crit= evaluated when no rows match

If a filter excluded every row, no per-row evaluation happened and the post-row pass only re-evaluated expressions whose
AST did not require an object. Pure-summary expressions like crit=count=0 worked, but mixed expressions like

crit = state = 'stopped' OR count = 0

were skipped entirely — leaving the check OK in the empty case.

A force-evaluation path is added: when no rows matched, object-bound variables resolve to their default (false) and
summary variables resolve to their final values, so the example above evaluates to (false OR true) = true and
correctly returns CRITICAL.

Real-world example:

# "CRITICAL if MyService is stopped, or if it doesn't exist at all"
check_service "filter=name='MyService'" "crit=state='stopped' OR count=0"
# Pre-fix: OK when MyService is missing.
# Post-fix: CRITICAL when MyService is missing.

Quieter, more predictable expression evaluation

  • Operators audited so is_unsure propagates consistently; invalid-type comparisons resolve to unsure-false instead
    of erroring.
  • String variables on no-object cases now return an empty string with is_unsure=true and produce a warning in the
    log instead of an error per row — log volume on complex queries drops dramatically.
  • Removed the misleading "most likely mutating" warnings.
  • Summary variables return sure-int during deferred evaluation so they don't get demoted to "unsure" by the new code
    path.
  • Substantial new test coverage for these paths.

HTTP refactor

  • HTTP request and response are now distinct types instead of one shared bag.
  • Chunked transfer-encoding is decoded properly (Icinga 2 responses use it).
  • Header storage is normalised — case-insensitive lookup, no more duplicate-header surprises.

Real-world impact: check_http against servers using Transfer-Encoding: chunked (most modern reverse proxies, Icinga
2, Kubernetes ingress, ...) now returns the full body instead of a truncated/garbled one. The IcingaClient module relies
on this.

plugin_manager response formatting

Performance data is now appended to the response message only when it exists, so checks with no perfdata no longer end
with a stray |. The CLI parser also gained tighter option handling and clearer logging.

-...

Read more

0.12.0

03 May 14:25
f5ce3e3

Choose a tag to compare

0.12.0 Pre-release
Pre-release

NSClient++ Release Notes

Release pulled due to a merge SNAFU see release 0.12.1 instead.
It is not harmful in any way, it is just missing a some changes...

Full Changelog: 0.11.33...0.12.0

0.11.33

01 May 05:01
48c2e68

Choose a tag to compare

0.11.33 Pre-release
Pre-release

What's Changed

NSClient++ 0.11.33 Release Notes

Changes since 0.11.32 (commit 06be00cc).

This release is focused on two user-facing areas:

  1. HTTP proxy support for every HTTP-based client (NRDPClient, ElasticClient, Op5Client, the configuration
    loader, ...).
  2. CheckDisk improvements — a new check_single_file command plus five long-standing check_files bug fixes.

HTTP proxy support

NSClient++ can now route HTTP and HTTPS traffic through a corporate proxy. The same configuration surface is used by
every component that builds on the internal http::simple_client (NRDPClient, ElasticClient, Op5Client, the remote
boot.ini loader, ...).

For HTTPS targets the client opens a CONNECT tunnel to the proxy, validates the proxy's response, and only then performs
the TLS handshake — so a single setting covers both http:// and https:// URLs.

Per-target proxy on submit (NRDP, Elastic, Op5)

Two new options are accepted on every HTTP client command and target:

Option Purpose
proxy Proxy URL — scheme://[user:pass@]host[:port]/. Empty value disables the proxy.
no-proxy Comma-separated list of hosts that bypass the proxy. A leading . is a suffix match.

Example — submit an NRDP check through a corporate proxy, but never proxy intranet hosts:

nscp client --module NRDPClient --query submit_nrdp \
    --host nagios.example.com --token mytoken \
    --proxy http://alice:s%40cret@proxy.corp.example:3128/ \
    --no-proxy localhost,127.0.0.1,.internal \
    --command check_disk --result OK --message "All disks healthy"

The same keys are accepted in nsclient.ini target definitions:

[/settings/NRDP/client/targets/nagios]
address = https://nagios.example.com/nrdp/
token = mytoken
proxy = http://proxy.corp.example:3128/
no proxy = localhost,127.0.0.1,.internal

Notes:

  • If the proxy needs authentication, embed credentials in the URL — they are sent as Proxy-Authorization: Basic. Both
    @ and : inside the username/password must be percent-encoded (alice:s%40cret decodes to alice:s@cret).
  • Bypass matching is case-insensitive. .internal matches both foo.internal and internal.
  • On a 407 Proxy Authentication Required the proxy's response body is now captured in the error message, so
    misconfigured credentials produce an actionable error instead of an opaque "connect failed".

Proxy for the configuration loader

If boot.ini itself lives behind a proxy, configure it in boot.ini:

[proxy]
url = http://proxy.corp.example:3128/
no_proxy = localhost,127.0.0.1,.internal

The proxy is applied to the initial configuration download, every refresh, and any attachments declared in the remote
configuration. HTTPS targets are tunnelled via CONNECT, so the same setting covers http:// and https:// settings
URLs.

Failed downloads still fall back to the cached copy of the configuration if one is present, so a transient proxy
outage will not stop NSClient++ from starting — but the very first run on a fresh machine needs the proxy to be
reachable.

What is not included

  • Only the http:// proxy scheme is supported. socks5:// / https:// proxies are not.
  • No automatic detection of system proxy settings (HTTP_PROXY env vars, WinINET / WPAD). The proxy must be configured
    explicitly.

CheckDisk: new command check_single_file

check_single_file is a focused variant of check_files for inspecting a single, known path. Compared to using
check_files for the same job it has:

  • Only one required argument (file=<path>) — no path + pattern, no max-depth, no total.
  • A clear error when the input is empty (No file specified (use file=<path>)).
  • UNKNOWN: File not found: <path> when the file is missing — instead of the empty-set / "No files found" workflow that
    surprised users in #613.
  • A useful default detail-syntax (%(filename) (size=%(size), age=%(age))) so a no-threshold run is informative on
    its own.

All existing filter keywords (size, age, written, version, line_count, type, ...) work, because the
implementation reuses the existing check_files filter machinery.

Examples

Confirm a file exists:

check_single_file file=C:/Windows/System32/notepad.exe
OK: notepad.exe (size=201728, age=12345)

Warn when a file becomes stale:

check_single_file file=C:/windows/WindowsUpdate.log "warn=age > 5m" "crit=age > 1h"
CRITICAL: WindowsUpdate.log (size=276, age=917)

Pin a specific binary version:

check_single_file file="C:/Windows/System32/notepad.exe" \
    "crit=version != '1.2.3.4'" \
    "detail-syntax=%(filename): %(version)"
CRITICAL: notepad.exe: 6.2.26100.8115

CheckDisk: check_files bug fixes

#730max-depth=0 is now "top directory only"

Previously max-depth=0 caused check_files to bail out before scanning anything, returning no files found for users
who simply asked for a non-recursive scan. It is now equivalent to max-depth=1 (scan the top directory, do not
recurse). max-depth=N for N >= 1 is unchanged, so this is not a breaking change for existing configurations.

check_files path=C:/logs pattern=*.log max-depth=0

#598 — Non-ASCII paths now work

Beware that this is still tricky to get right if you are checking from linux via for instance NRPE.

Recursive scanning previously called GetFileAttributes through boost::filesystem's system codepage conversion while
every other Win32 call in the same scan went through UTF-8 conversion. Paths containing accented letters, CJK
characters, etc. were silently mangled and reported as File was NOT found even when they existed. They now work
consistently.

#613 — Missing paths surface as UNKNOWN

If the path passed to check_files could not be opened (typo, deleted directory, permission denied) the failure was
previously hidden behind the configured empty-state — typically OK or a generic No files found. Operators monitoring
file/folder counts therefore got a green status for misconfigured paths.

Top-level paths that cannot be opened now produce an explicit UNKNOWN: Path was not found: <path>, so the failure is
visible in the monitoring system.

#605 — Reparse points are skipped during recursion

NTFS junctions, symlinks and mount points all look like directories, so a tree containing a junction back into itself
was walked twice and files were counted twice. check_files now skips entries whose attributes contain
FILE_ATTRIBUTE_REPARSE_POINT when recursing — the same default as Windows tools like robocopy /XJ. Files matched in
the top-level pass are unaffected.

#717 — Legacy CheckFiles defaults empty-state=ok

The legacy CheckFiles shim translates old-style arguments (path, pattern, MaxWarn, MaxCrit, …) into modern
check_files arguments. Modern check_files defaults empty-state to unknown, which meant a legacy command that
found zero files came back as UNKNOWN even though the original 0.4-era behaviour was to return OK. The shim now sets
empty-state=ok when translating, restoring the legacy semantics.


Other changes

  • Numerous small clean-ups across CheckSystem, CheckSystemUnix, CheckNet, NRDPClient, Op5Client, ElasticClient and the
    WEB server — internal only, no behaviour change.
  • New unit test infrastructure for the settings layer (settings_dummy_test, settings_http_test, settings_ini_test,
    settings_interface_impl_test) and 100+ new tests around the HTTP client / proxy code paths.
  • Linux build is back to green after the proxy work.

Upgrade notes

  • No configuration migration is required. All new keys (proxy, no-proxy, the [proxy] boot.ini section) are opt-in.
  • The check_files fixes change observable behaviour in a few corner cases:
    • max-depth=0 now scans the top directory instead of returning empty (#730).
    • Missing paths now return UNKNOWN instead of OK/empty (#613).
    • Junction loops are no longer double-counted (#605).
    • Legacy CheckFiles calls that previously returned UNKNOWN on empty results will now return OK (#717).
      If any existing alerting was relying on the old behaviour of these specific corner cases, review it before
      upgrading.

Full Changelog: 0.11.32...0.11.33

0.11.32

29 Apr 18:32
06be00c

Choose a tag to compare

0.11.32 Pre-release
Pre-release

What's Changed

This release is focused on three things:

  1. More built-in checks – a real check_net family, a real
    Windows registry checker, and real-time metrics on Linux.
  2. A much nicer Web UI – modern theme, filtering, settings diff
    dialog and a proper dashboard.
  3. A large documentation overhaul – restructured manual, every
    command has a samples page, and the HTTP client used by the
    Op5/Elastic clients has been modernized.

✨ New features

CheckSystem (Windows) – registry checks

Two new commands let you monitor the Windows registry directly from
NSClient++ instead of relying on external scripts. They support
recursion, exclude lists, 32/64-bit (WoW64) views, custom filters and
the usual warn=/crit= expression syntax.

  • check_registry_key – verify that a key exists, count
    sub-keys/values, watch its last-write time.
  • check_registry_value – read a single value (or enumerate all
    values under a key), assert its type, size or content.

Examples:

# Just verify a key exists (default crit = "not exists")
check_registry_key "key=HKLM\Software\Microsoft\Windows NT\CurrentVersion"
OK: All 1 registry key(s) are ok.

# Watchdog: alert when the key has not been written for over 30 days
check_registry_key "key=HKLM\Software\NSClient" \
    "warn=age > 7d" "crit=age > 30d or not exists"

# Recursive enumeration of every immediate sub-key, with exclusions
check_registry_key "key=HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall" \
    recursive max-depth=1 exclude=KB5005463 exclude=KB5005539

# Type assertion on a single value
check_registry_value "key=HKLM\Software\NSClient" value=InstallVersion \
    "crit=type != 'REG_SZ' or not exists"

# Numeric DWORD comparison
check_registry_value "key=HKLM\System\CurrentControlSet\Services\W32Time\Config" \
    value=MaxPollInterval "warn=int_value > 14" "crit=int_value > 17"

# Force the 32-bit (WoW64) view
check_registry_key "key=HKLM\Software\NSClient" view=32

CheckNet – five new (cross-platform) checks

CheckNet has graduated from a placeholder into a full network-check
module. Five new commands have been added, all using the new internal
http::simple_client and all unit-tested:

  • check_tcp – open a TCP socket to one or more host/port pairs,
    optionally send a payload and require an expected substring.
  • check_dns – resolve a hostname and optionally assert which
    addresses come back.
  • check_http – fetch one or more URLs, check status code,
    response time and body content; supports custom headers and
    user-agent.
  • check_ntp_offset – query one or more NTP servers and alert on
    offset / stratum.
  • check_connections – Windows-side TCP/UDP connection table
    inspection (counts per protocol/family/state).

Examples:

# Single host TCP probe
check_tcp host=127.0.0.1 port=8443

# Multiple hosts in one call
check_tcp host=www.google.com host=www.ibm.com port=443 timeout=2000

# SMTP banner check (send + expect)
check_tcp host=smtp.gmail.com port=25 send="EHLO nsclient.org" expect="250"

# DNS resolution with expected address
check_dns host=google.com expected-address=172.217.20.174

# HTTP with body match and tighter latency thresholds
check_http url=https://nsclient.org/ expected-body="NSClient" \
    "warn=time > 500 or code >= 400" \
    "crit=time > 2000 or code >= 500 or result != 'ok'"

# NTP offset against multiple servers
check_ntp_offset "servers=0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org" timeout=2000

# Alert on too many sockets stuck in TIME_WAIT
check_connections "filter=protocol = 'tcp' and state = 'TIME_WAIT'" \
    "warn=count > 200" "crit=count > 1000"

All five also work over NRPE, e.g.:

check_nscp_client --host 192.168.56.103 --command check_http \
    --argument "url=https://nsclient.org/"

CheckSystem (Linux) – real-time metrics

The Linux build of CheckSystem now ships with the same real-time
metric collection that has been available on Windows for a long time:
CPU, memory, disk, network and load are sampled in the background and
exposed both to dashboards/metrics and to real-time filters
(filter=... rules that fire when a threshold is crossed).

No new commands are exposed by name – existing real-time filter
configuration just works on Linux now.

Web UI – refresh

The bundled web interface has been heavily reworked:

  • Modern theme with active-navigation highlighting and a redesigned
    login page.
  • Filterable lists for Modules, Queries and Settings.
  • Settings diff dialog – the "settings changed" widget can now show exactly which keys changed.

🐛 Fixes / smaller improvements

  • Op5Client: fixed a 401 path and several reponse → response
    typos.
  • Unified the various http clients meaning Op5Client and ElasticClient should now work better.

📚 Documentation

The documentation has had its biggest pass in years:

  • Restructured manualconcepts/, checks-in-depth/,
    scenarios/, tutorial/ and reference/ are now clearly
    separated; mkdocs navigation rebuilt accordingly.
  • New scenario guides: disk space, event log, external scripts,
    network checks, passive monitoring, service monitoring, Windows
    server health.
  • New "checks in depth" pages: filters, performance data, syntax,
    thresholds.
  • Rewritten REST API reference (info, login, modules,
    queries).
  • **Many commands now has samples with copy-pasteable
    examples and expected output, including all new commands listed
    above.

⚠️ Upgrade notes

  • The documentation tree was reorganized; bookmarks/links might not be valid.

Full Changelog: 0.11.31...0.11.32