Skip to the content.

← All documentation · English | 繁體中文

SNMPv3

Status: verified. Checked on the wire against net-snmp, which is what LibreNMS polls with, and then on four real machines: Windows 10, Windows 11, Server 2016 (a domain controller) and Server 2022. The three of them that a production LibreNMS monitors were switched from v2c to v3, and none was rediscovered — ports, storage and sensors all kept their existing entries.

SNMPv2c sends its community string in clear text and authenticates nothing. Anyone who can see the traffic can read it, and anyone who can guess the community can poll the host. SNMPv3 fixes both: every request is authenticated with an HMAC, and with authPriv the contents are encrypted as well.

jt-snmpd serves v3 beside v2c rather than in place of it, so upgrading does not take an existing deployment off the map.


1. Provisioning an account

Passphrases are prompted for. They are never accepted as command-line arguments, because an argument is visible in the process list to every user on the machine while the command runs, and it lands in console history. For the same reason the installer does not take keys as MSI properties: those end up in the msiexec log and in Event IDs 1033 and 11707.

From an elevated command prompt on the monitored host:

"C:\Program Files\jt-snmpd\jt-snmpd.exe" user add librenms

It asks for an authentication passphrase and a privacy passphrase, twice each. Both must be at least 12 characters, and they must differ from one another: one compromise should not be two.

"C:\Program Files\jt-snmpd\jt-snmpd.exe" user list
"C:\Program Files\jt-snmpd\jt-snmpd.exe" user remove librenms

Restart the service for a change to take effect:

sc stop jt-snmpd && sc start jt-snmpd

Algorithms

The default is SHA-256 for authentication and AES-128 for privacy, which is what net-snmp can talk to everywhere. Override with --auth and --priv:

Setting Accepted
--auth SHA-224, SHA-256 (default), SHA-384, SHA-512
--priv AES-128 (default), AES-192, AES-256

MD5, SHA-1, DES and 3DES are refused. pysnmp implements all four, so a configuration naming one of them would otherwise work, and working is the wrong outcome: the operator would believe the traffic was protected when it was not.

AES-192 and AES-256 warn. That is an interoperability risk rather than a weakness in the cipher. Neither was standardised for USM, two incompatible key-extension schemes exist (the Blumenthal draft and Reeder’s), and Debian and Ubuntu build net-snmp without the one pysnmp uses. An agent configured that way can be unreachable from the very LibreNMS installation it was set up for. AES-128 is the interoperable choice.


2. Provisioning across many machines

The command above is fine for ten hosts and impossible for a thousand. The installer deliberately accepts no SNMPv3 parameter, and that is not an oversight to be worked around: an MSI property is written to the msiexec log and to Windows Event IDs 1033 and 11707, where it stays on every machine it reached. A Group Policy startup script is no better — it keeps the passphrase in SYSVOL, readable by every domain computer, which is the shape of the Group Policy Preferences password problem Microsoft eventually withdrew the feature over.

Instead, a deployment tool drops a file, and the agent consumes it once:

C:\ProgramData\jt-snmpd\provision.json
{
  "schema_version": 1,
  "users": [
    {
      "name": "librenms",
      "auth": "SHA-256",
      "auth_passphrase": "the authentication passphrase",
      "priv": "AES-128",
      "priv_passphrase": "the privacy passphrase"
    }
  ]
}

At the next service start the agent turns the passphrases into keys localized to this machine’s engineID, stores them under DPAPI machine scope exactly as user add does, and overwrites and deletes the file. What is left on disk afterwards is what user add would have left, and nothing else.

The log says what happened, by name and algorithm and never by passphrase, so a rollout can be checked from Get-WinEvent without opening a session on each host:

provisioning file found at C:\ProgramData\jt-snmpd\provision.json
provisioned SNMPv3 user 'librenms' (SHA-256 + AES-128)
provisioning: 1 user(s) stored; only the localized keys were kept, the passphrases were not
provisioning file overwritten and deleted
SNMPv3 user 'librenms' registered (SHA-256 + AES-128)

The file is deleted even when it could not be used. A typo must not leave passphrases on disk for ever; the log gives the reason, with the position in the file, and a corrected one can be dropped in. If the deletion itself fails, that is logged as an error naming the file, because a file meant to be gone and still there is an exposure nobody would otherwise learn about.

Re-running a rollout is safe: an account that already exists is replaced, so the machines already reached converge instead of failing.

What this does and does not protect

It bounds how long and where a passphrase exists on the monitored host: in a directory the installer has restricted to SYSTEM and Administrators, for one service start. It does nothing for the copy at the other end.

Getting the file there is still yours to solve, and it is the weak part. A Group Policy Preferences file copy reads from a share every domain computer can read. If that matters to you — and on a domain it usually does — put the source on a share whose ACL names the target computer accounts, deploy, and remove it. Treat the passphrase as compromised if it sat somewhere broadly readable, and rotate it: user remove, then provision again.

A different passphrase per machine is better than one for the estate, and this file makes that practical: the deployment tool writes a different file per host. One machine’s keys are localized to its own engineID and are useless anywhere else, but the passphrase they came from is not.


3. Adding the device in LibreNMS

Devices → Add Device, then:

Field Value
SNMP Version v3
Auth Level authPriv
Auth User Name the name given to user add
Auth Password the authentication passphrase
Auth Algorithm SHA-256
Crypto Password the privacy passphrase
Crypto Algorithm AES

To check from the LibreNMS server before adding it:

snmpwalk -v3 -l authPriv -u librenms \
  -a SHA-256 -A '<auth passphrase>' \
  -x AES     -X '<privacy passphrase>' \
  <host> 1.3.6.1.2.1.1

4. Turning v2c off

Once every manager is on v3, set v3_only in C:\ProgramData\jt-snmpd\config.json and restart the service:

{ "v3_only": true }

The agent then does not register v2c at all. If v3_only is set and no v3 user can be loaded, the service refuses to start. That is deliberate: listening with no way in would look healthy from Windows while answering nobody, and the operator would go looking at the network for a fault that is in a configuration file.


5. Where the keys live, and what that protects

%ProgramData%\jt-snmpd\secrets\usm.dat, encrypted with DPAPI machine scope. The directory is restricted to SYSTEM and Administrators.

What is stored is the localized key, not the passphrase. A localized key is derived from the passphrase and this machine’s engineID, so it authenticates to exactly one agent. The passphrase is used once, at provisioning time, and is not written down.

The point of that is the estate. If passphrases were stored, reading one machine’s secrets file — a stolen backup or a misconfigured share is enough — would hand over an account on every machine that shares the credential. Hundreds provisioned from one policy is the normal deployment, so that is the realistic shape of the loss. With localized keys, the same theft is worth one machine.

What DPAPI does not do. The blob is decryptable only on the machine that wrote it, which is what stops a copied file being useful elsewhere. It does not, and cannot, stop an administrator on that machine: the service runs unattended as LocalSystem and has to be able to read the keys without anyone present, so any protection it can undo unattended is protection an administrator can undo too. The honest claim is that it defends the file at rest and in transit, not the machine’s own administrators.


6. Cloned VMs, and the one thing that will bite you

An engineID must be unique. jt-snmpd derives it from the Windows MachineGuid and records which MachineGuid it was derived from.

If the MachineGuid changes — the machine was cloned from a template, or reimaged — the agent generates a new engineID, resets snmpEngineBoots and says so in its log. It has to: fifty clones answering with the same engineID make a manager keep one boots/time pair for what it believes is a single engine, and authentication then fails intermittently across the whole estate for reasons nothing in the logs explains.

A localized key cannot survive that. It is bound to the engineID it was made for and cannot be converted, and the passphrase it came from was deliberately not kept. So a clone’s stored users are dead. The agent detects this and says exactly that, rather than failing to authenticate for reasons nobody can see:

[!] SNMPv3: the SNMPv3 keys were localized against engineID 8001869f04... but
    this engine is 8001869f04... A localized key is bound to the engineID it was
    made for and cannot be converted, so every SNMPv3 user has to be provisioned
    again. This normally means the machine was cloned from a template or
    reimaged

So do not bake accounts into an image. Provision after first boot, from Group Policy or from the CLI. A template captured before any v3 user is added has nothing to lose.


7. What SNMPv3 does not change