By the SNMP Monitoring team · Reviewed July 2026
A read-only SNMP agent still hands over its entire MIB tree by default — inventory, interface tables, running config details, the lot. Least privilege says otherwise: expose only the handful of subtrees your poller actually reads, and nothing else. That's what a VACM allowlist does. Build one and a leaked community or a compromised credential reveals a few metrics instead of a full device profile. This is the how-to — define a view, bind it to access, and prove that out-of-view OIDs return nothing.
Parent: setup. Related: snmpd.conf, community string, SNMPv3.
What VACM is
VACM — the View-based Access Control Model — is net-snmp's mechanism for deciding which OIDs a given requester may read. Instead of "this community can read everything," VACM lets you say "this group may read only these subtrees." You describe a view as a set of OID subtrees, each either included or excluded, and then attach that view to a security group. A request for an OID inside the view returns data; a request for anything outside it returns no such object. VACM is standardised as part of the SNMP framework and implemented directly by net-snmp's view/access directives.
Why allowlist OIDs
The point is blast-radius reduction. Even with a random community and a source-IP restriction, anything that can talk to the agent can still walk the whole tree unless you scope it. An allowlist caps the damage:
- A leaked community string then dumps only your chosen metrics, not the full MIB.
- A compromised v3 credential is similarly limited to its authorised view.
- Reconnaissance is starved — an attacker can't enumerate software, routes, or ARP tables you never exposed.
- Accidental over-collection is prevented — your poller sees exactly what it's meant to.
An allowlist is the difference between "read-only" and "read-only, and only these three subtrees." The second is the one you want.
Step 1: Define a view
A view line names an allowlist and adds one subtree to it. List each subtree your monitoring needs:
view monitoring included 1.3.6.1.2.1.1
view monitoring included 1.3.6.1.2.1.25
view monitoring included 1.3.6.1.2.1.2
To build a view methodically:
- Start empty — nothing is visible until you
includeit. - Add the system group (
1.3.6.1.2.1.1) so sysDescr/sysUpTime work. - Add each MIB subtree your poller reads — host-resources (
...25), interfaces (...2), and so on. - Use
excludedto carve a sensitive leaf back out of an otherwise-included subtree.
Each line is additive. An optional trailing hex mask lets you match specific table columns, but for most allowlists whole-subtree includes are enough.
Step 2: Bind the view to access
A view does nothing until a group is bound to it. net-snmp's chain maps a community (or user) to a group, then a group to a view:
| Directive | Role |
|---|---|
com2sec | Map a community + source to a security name |
group | Put that security name into a named group |
view | Define the allowed OID subtrees |
access | Bind the group to the view with a permission |
Written out:
com2sec monnet 10.0.0.5 S3cr3tR0str1ng
group mongroup v2c monnet
access mongroup "" any noauth exact monitoring none none
The access line reads: group mongroup, any security model, no auth required, exact-match, read using the monitoring view, no write, no notify. For the shorthand, net-snmp also accepts a view directly on the community line:
rocommunity S3cr3tR0str1ng 10.0.0.5 -V monitoring
The -V monitoring binds that community to the same view without spelling out the full chain — simpler when you only need one community scoped to one allowlist.
Step 3: Verify
Restart snmpd, then test both an in-view and an out-of-view OID. An allowed subtree returns data:
snmpwalk -v2c -c S3cr3tR0str1ng <host> 1.3.6.1.2.1.1
SNMPv2-MIB::sysDescr.0 = STRING: Linux edge01 5.15.0-generic x86_64
SNMPv2-MIB::sysUpTime.0 = Timeticks: (98765432) 11 days, 10:20:54.32
Now query something you did not include — say the ARP/routing area — and confirm it's invisible:
snmpget -v2c -c S3cr3tR0str1ng <host> 1.3.6.1.2.1.4.22.1.2.2.10.0.0.1
SNMPv2-MIB::... = No Such Object available on this agent at this OID
Data on the allowed subtree and No Such Object on the excluded one means the allowlist is working. That "no such object" is VACM doing its job — the OID may well exist on the box, but it's outside the view, so the agent behaves as though it isn't there.
Example: a monitoring allowlist
A typical server poller needs surprisingly few subtrees. A sensible default allowlist:
1.3.6.1.2.1.1— system group (identity, uptime, contact).1.3.6.1.2.1.25— host-resources (CPU, storage, running processes).1.3.6.1.2.1.2and1.3.6.1.2.1.31— interfaces (traffic, errors).
That covers the bread-and-butter of server and network monitoring while leaving routing tables, ARP caches, and the full software inventory unexposed. Add only what a real dashboard queries; if you're unsure what an OID is, the glossary and the sensor index map metrics to their subtrees.
How ostr.io uses this
An allowlist is exactly the pattern an external checker should rely on. ostr.io recommends exposing only a narrow OID allowlist for its off-box SNMPv2c polling, so the agent surfaces just the health metrics it needs and nothing sensitive — a good fit because an external, agentless path should have the least possible visibility into the host. See external monitoring for how that off-box check fits alongside on-box hardening.
Frequently asked questions
How do I restrict which SNMP OIDs are readable?
Define a VACM view listing the subtrees to expose — view <name> included <oid> — then bind it to a group with an access line (or use rocommunity <string> <source> -V <view> for the short form). Restart snmpd; anything outside the view returns no such object.
What is VACM?
VACM is the View-based Access Control Model, net-snmp's way of controlling which OID subtrees a requester may read. You group OIDs into a named view using included/excluded subtrees and attach that view to a security group, so each community or user sees only its authorised slice of the MIB.
How do I limit a community to a view?
The quickest way is rocommunity <string> <source> -V <viewname>, which binds that community to the named view. For finer control use the full com2sec → group → access chain and reference the view in the access line's read column.
What happens when I query an OID that's out of view?
The agent returns No Such Object (or no response), exactly as if the OID didn't exist — even though it may be present on the device. VACM filters the response, so an out-of-view query yields no data, which is the whole point of the allowlist.
Key takeaways
- A read-only agent still exposes its whole tree — scope it with a VACM allowlist.
- Build a
viewfromincludedsubtrees; carve exceptions withexcluded. - Bind it via
com2sec/group/access, or the shorthand-V <view>onrocommunity. - Verify both ways: in-view returns data, out-of-view returns No Such Object.
- A typical poller needs only system, host-resources, and interface subtrees.
- The ostr.io external check leans on exactly this allowlist pattern — see external monitoring.