SNMPv3
SNMPv3 is the current, actively maintained version of SNMP, and the only one with real security built in - individual authenticated users instead of a shared plain-text community string, optional encryption of the payload, and fine-grained control over exactly which parts of the OID tree a given user can see or change. The SNMP Fundamentals guide covers where v3 fits next to v1 and v2c at a high level; this guide goes deep on v3 specifically - the security model, the protocols it supports, how a request actually gets authenticated and encrypted end to end, configuring it on a real agent, and the handful of things that commonly trip people up the first time they turn it on.
Why SNMPv3 exists
SNMPv1 and SNMPv2c both authenticate with a community string: a plain-text value sent, unencrypted, with every single request. Anyone with visibility into the traffic - a tap on the wire, a compromised switch, a packet capture - can read it directly and reuse it, and because every user of a given community string looks identical to the agent, there's no way to tell who actually sent a request or to revoke one person's access without changing the string for everyone. SNMPv3, finalized in 1998 and still the version actively developed and extended today, was built specifically to close that gap. It keeps the same underlying GET/GETNEXT/GETBULK/SET/TRAP/INFORM operations described in SNMP Fundamentals - the wire-level request/response pattern is unchanged - but replaces the community string with a real security architecture: the User-based Security Model (USM) for authentication and encryption, and the View-based Access Control Model (VACM) for authorization.
The three security levels
Every SNMPv3 message is sent at exactly one of three security levels, and the level is part of the request itself, not a separate agent-wide setting - different users on the same agent can operate at different levels.
- noAuthNoPriv - a username is presented, but nothing is verified and nothing is encrypted. This is barely an improvement over v2c's community string in practice (anyone who knows a valid username can impersonate that user), and exists mainly for testing or for a handful of operations, like discovering an agent's engine ID, that have to happen before authentication is even possible.
- authNoPriv - the request is cryptographically authenticated (the agent verifies it genuinely came from that user and wasn't tampered with in transit), but the payload itself - the OIDs and values being requested or returned - travels unencrypted and readable to anyone watching the wire.
- authPriv - authenticated and encrypted. The agent verifies the sender, and the entire payload is encrypted so it isn't readable in transit even if captured. This is the level worth using for anything beyond an isolated lab, and the level MIB Viewer's own command generator defaults v3 commands to.
Each level is a strict superset of the one before it: authPriv requires authentication to be configured (you can't have encryption without also verifying who's sending), but authNoPriv doesn't require encryption, and noAuthNoPriv requires neither.
USM: users, not community strings
Where v1/v2c authenticate a request against a shared string, SNMPv3's User-based Security Model authenticates it against an individual user account configured on the agent, each with its own authentication and (optionally) privacy credentials. This is the architectural shift that makes everything else possible: per-user accountability (the agent genuinely knows which user sent a given request, not just "someone who knew the string"), per-user revocation (disable one user's account without touching anyone else's), and per-user security settings (one user could run authNoPriv while another runs authPriv, depending on what each needs).
A USM user is defined by: a username, an authentication protocol and passphrase (used to compute a per-message integrity check), and - for authPriv - a privacy protocol and passphrase (used to encrypt the payload). The auth and priv passphrases are typically different values, though some tooling allows reusing the same one for both; treating them as two separate secrets is the safer default.
Authentication protocols
The authentication protocol determines how a per-message integrity check (effectively a keyed hash of the message, so the agent can verify both who sent it and that it wasn't altered in transit) is computed:
- MD5 - the original protocol, now considered cryptographically weak and deprecated in modern guidance. Still supported for compatibility with old hardware, but shouldn't be chosen for a new configuration.
- SHA-1 (usually just written
SHAin tool flags) - the long-standing default across most tooling and devices, including net-snmp. Not cutting-edge cryptography by today's standards, but far better than MD5 and universally supported. - SHA-224 / SHA-256 / SHA-384 / SHA-512 - newer, stronger variants defined in later RFCs (the SNMP-USM-HMAC-SHA2-MIB extensions). Supported by recent net-snmp versions and most current-generation network hardware, but not universally available on older agents - worth confirming support before standardizing on one of these across a mixed-age fleet.
Plain SHA (SHA-1) remains a reasonable, broadly compatible default today; SHA-256 is a reasonable choice where every device in the fleet is confirmed to support it.
Privacy (encryption) protocols
For authPriv, the privacy protocol determines how the message payload is encrypted:
- DES - the original privacy protocol, using a 56-bit key considered weak by modern standards and deprecated in current guidance. Still present in older tooling for compatibility.
- 3DES (Triple DES) - an improvement over plain DES, but slower and now largely superseded by AES; not every implementation supports it.
- AES-128 - the de facto modern default, and what both net-snmp and MIB Viewer's command generator default to. Strong, fast, and supported essentially everywhere SNMPv3 itself is supported.
- AES-192 / AES-256 - longer-key AES variants, supported by some but not all implementations (net-snmp supports them via a separate build option on some platforms). Worth the extra setup only if a specific compliance requirement calls for it; AES-128 is already strong enough for the vast majority of deployments.
The net-snmp-style command flags for all of this: -a selects the auth protocol (MD5, SHA, SHA-256, etc.), -A is the auth passphrase, -x selects the privacy protocol (DES, AES, etc.), -X is the privacy passphrase, and -l sets the security level (noAuthNoPriv, authNoPriv, or authPriv).
snmpget -v3 -u monitoring -l authPriv -a SHA -A '<authpassword>' -x AES -X '<privpassword>' 192.168.1.1 sysDescr.0
Engine IDs and key localization
Every SNMPv3 agent has a unique engine ID - a hex string that identifies that specific SNMP engine, generated automatically on first startup (typically derived from the device's IP address or MAC address plus some engine-specific data, though the exact algorithm varies by implementation) and normally left alone rather than set manually. The engine ID plays a specific cryptographic role: a user's actual authentication and privacy keys aren't the plaintext passphrase itself, but a value derived from that passphrase localized to the specific agent's engine ID, via a one-way key derivation process defined in the USM specification.
The practical consequence: the same username and passphrase configured on two different agents produce two different localized keys, because each agent has a different engine ID. This is a deliberate security property - it means a captured or leaked key from one agent can't be directly reused against a different agent, even if the operator (perhaps carelessly) reused the same passphrase everywhere. It also means that if an agent's engine ID ever changes (a factory reset, a reinstalled SNMP daemon, certain firmware upgrades), every USM user's localized keys change along with it, and users typically have to be reconfigured on that agent.
Time synchronization and the discovery handshake
USM includes anti-replay protection: each authenticated message carries the sending engine's boot count and uptime, and a receiving engine rejects a message whose timing falls too far outside what it expects (by default, roughly 150 seconds of drift is tolerated). This is why a manager has to go through a brief discovery exchange with an agent before it can send authenticated requests: an initial unauthenticated (noAuthNoPriv) probe to learn the agent's engine ID, boot count, and current time, after which the manager can synchronize its own view of that agent's clock state and begin sending properly authenticated requests. Most tooling, including net-snmp's command-line utilities, handles this discovery step automatically and transparently - it's rarely something you configure by hand - but it's worth knowing it happens, because a firewall or network path that blocks the very first unauthenticated probe packet will cause every subsequent v3 request to that agent to fail with no obviously related explanation.
A large, sustained clock skew between manager and agent - well beyond what the discovery handshake accounts for - can also cause authenticated requests to be rejected outright; this is covered as a specific symptom in Troubleshooting SNMP.
VACM: views, groups, and access
USM answers "who is this, and can we trust this message." VACM (View-based Access Control Model) answers a separate question: "what is this specific, now-authenticated user actually allowed to see or change." The two are intentionally decoupled - VACM also governs access for v1/v2c community-string-based requests, not just v3 ones, though it's far more commonly configured and relied on in v3 deployments where per-user distinctions actually exist to enforce. VACM has three building blocks:
- Views - a named subtree (or set of subtrees) of the OID namespace, e.g. "everything under
1.3.6.1.2.1" or "everything except the private enterprises branch." - Groups - one or more users (or community strings) mapped to a named group; permissions are actually granted to groups, not directly to individual users, which keeps a fleet of many users manageable.
- Access entries - tie a group, a security level, and a view together: "members of this group, at this security level, can read this view" (and, separately, which view - if any - they can write to, and which notification view they can receive).
In practice this is what lets an SNMPv3 configuration express something v1/v2c community strings fundamentally can't: a monitoring user with read-only access to everything, a separate automation user with read-write access to one narrow configuration subtree and nothing else, and both coexisting on the same agent with completely independent, individually revocable access.
Configuring an SNMPv3 user on net-snmp
On net-snmp, a v3 user is created with net-snmp-create-v3-user (which writes directly to /var/lib/snmp/snmpd.conf, the persistent store for v3 user keys - not the main /etc/snmp/snmpd.conf file) or by hand-editing that persistent file directly:
sudo net-snmp-create-v3-user -A '<authpassword>' -a SHA -X '<privpassword>' -x AES monitoring
Then, in /etc/snmp/snmpd.conf, grant that user access via a rouser (read-only) or rwuser (read-write) line, specifying the minimum security level required:
rouser monitoring authPriv
This single line is effectively a minimal VACM configuration generated for you by net-snmp's simplified syntax - it implicitly creates a group, a view covering the whole tree, and an access entry, without requiring the full view/group/access directives to be written out by hand. A full hand-written VACM configuration (separate views, groups, and access entries for finer-grained control) is also supported, documented in man snmpd.conf under the VACM section, and used where rouser/rwuser's whole-tree default isn't granular enough.
After restarting snmpd, that user can query the agent:
snmpwalk -v3 -u monitoring -l authPriv -a SHA -A '<authpassword>' -x AES -X '<privpassword>' 192.168.1.1 system
SNMPv3 traps and informs
Notifications work under USM too, with one added wrinkle specific to the push direction: for authenticated/encrypted traps or informs, the receiving manager also needs to know the sending agent's engine ID in advance, since USM keys are localized to it (see Engine IDs and key localization above). For a TRAP (fire-and-forget, no acknowledgment - see TRAP and INFORM in SNMP Fundamentals), this engine ID typically has to be configured on the receiving side ahead of time, since there's no handshake to discover it live. An INFORM, being acknowledged, can perform its own discovery exchange with the receiver first, which is one practical reason some deployments prefer INFORM over TRAP specifically when running v3 - it removes the need to pre-configure the sending agent's engine ID on the receiving manager.
Common pitfalls
- Wrong engine ID on the receiving side - the most common cause of an SNMPv3 trap that the agent clearly sent but the manager never seems to process correctly; the manager can't localize the right key without the matching engine ID.
- Auth/priv protocol mismatch - specifying
-a SHAon the command line when the user was actually created with MD5 (or vice versa) produces an authentication failure that looks identical, from the error message alone, to a wrong password. - Large clock skew - see Time synchronization above; keeping agent and manager clocks reasonably in sync (NTP) avoids this entirely.
- Treating noAuthNoPriv as "basically fine" - it authenticates nothing; a username alone is not meaningfully more secure than a community string, and shouldn't be used as a production security level.
- Forgetting that engine ID changes invalidate existing users - a factory reset or certain firmware upgrades can regenerate an agent's engine ID, silently breaking every previously-configured v3 user on that device until they're recreated.
Migrating from v2c to v3
A practical path for moving an existing v2c-based monitoring setup to v3: create v3 users alongside the existing community strings rather than removing v2c access immediately, point a small subset of monitoring (one device, one poller) at the new v3 user to confirm authentication, encryption, and VACM access all behave as expected, then roll it out device by device rather than all at once - a fleet-wide cutover makes any misconfiguration (a wrong auth protocol, a VACM view that's too narrow) a fleet-wide outage in monitoring visibility instead of a single, easily-diagnosed device. Once v3 is confirmed working across the fleet, the old v2c community strings can be removed from each agent's configuration.