fix: _action_fix_snmp adds floating firewall rule for UDP/161
OPNsense default-drops traffic arriving on non-LAN interfaces (e.g. WireGuard tunnels used as management networks). Even with os-net-snmp running and configured, SNMP is unreachable from external management hosts because no firewall rule allows it. Now adds a floating pass rule for UDP/161 → (self) after configuring the service, then applies the firewall. Skips the rule if one with the same description already exists (idempotent). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0928584b5d
commit
8e413e4c60
+49
-13
@@ -1541,24 +1541,29 @@ class OPNsenseDriver(FirewallDriver):
|
|||||||
raise NotImplementedError(f"Unknown action: {action!r}")
|
raise NotImplementedError(f"Unknown action: {action!r}")
|
||||||
|
|
||||||
def _action_fix_snmp(self) -> dict[str, Any]:
|
def _action_fix_snmp(self) -> dict[str, Any]:
|
||||||
"""Install os-net-snmp plugin, configure community 'public', start service.
|
"""Install os-net-snmp plugin, configure community 'public', open firewall.
|
||||||
|
|
||||||
Steps:
|
Steps:
|
||||||
1. Install os-net-snmp via firmware API (idempotent — no-op if installed)
|
1. Install os-net-snmp via firmware API (idempotent)
|
||||||
2. Configure via POST /api/netsnmp/general/set
|
2. Configure via POST /api/netsnmp/general/set
|
||||||
3. Start/restart via POST /api/netsnmp/service/start
|
3. Start/restart the service
|
||||||
|
4. Add a floating firewall rule allowing UDP/161 from any source
|
||||||
|
(OPNsense default-drops traffic arriving on non-LAN interfaces
|
||||||
|
such as WireGuard; without this rule SNMP is unreachable from
|
||||||
|
management networks even though the daemon is running)
|
||||||
|
5. Apply firewall and verify
|
||||||
"""
|
"""
|
||||||
lines: list = []
|
lines: list = []
|
||||||
|
|
||||||
# 1. Install os-net-snmp plugin (POST /api/core/firmware/install/os-net-snmp)
|
# 1. Install os-net-snmp plugin
|
||||||
try:
|
try:
|
||||||
result = self._post("/api/core/firmware/install/os-net-snmp")
|
result = self._post("/api/core/firmware/install/os-net-snmp")
|
||||||
lines.append(f"[install] {result}")
|
lines.append(f"[install] {result}")
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.debug("os-net-snmp install skipped or failed: %s", exc)
|
logger.debug("os-net-snmp install skipped or failed: %s", exc)
|
||||||
lines.append(f"[install] skipped or already installed: {exc}")
|
lines.append(f"[install] already installed or skipped")
|
||||||
|
|
||||||
# 2. Configure SNMP: enable + set community 'public'
|
# 2. Configure SNMP
|
||||||
try:
|
try:
|
||||||
self._post("/api/netsnmp/general/set", {
|
self._post("/api/netsnmp/general/set", {
|
||||||
"general": {
|
"general": {
|
||||||
@@ -1585,10 +1590,45 @@ class OPNsenseDriver(FirewallDriver):
|
|||||||
self._post("/api/netsnmp/service/start")
|
self._post("/api/netsnmp/service/start")
|
||||||
lines.append("[service] net-snmp started.")
|
lines.append("[service] net-snmp started.")
|
||||||
except Exception as exc2:
|
except Exception as exc2:
|
||||||
logger.debug("SNMP service start failed: %s", exc2)
|
|
||||||
lines.append(f"[service] start failed: {exc2}")
|
lines.append(f"[service] start failed: {exc2}")
|
||||||
|
|
||||||
# 4. Verify
|
# 4. Add floating firewall rule for SNMP (UDP/161 → self)
|
||||||
|
# OPNsense blocks traffic arriving on non-LAN interfaces by default.
|
||||||
|
# A floating pass rule makes SNMP reachable from all management nets.
|
||||||
|
try:
|
||||||
|
existing = self._post("/api/firewall/filter/searchRule",
|
||||||
|
{"current": 1, "rowCount": -1, "searchPhrase": "[netork] Allow SNMP"})
|
||||||
|
if not (existing.get("rows") or []):
|
||||||
|
self._post("/api/firewall/filter/addRule", {
|
||||||
|
"rule": {
|
||||||
|
"enabled": "1",
|
||||||
|
"sequence": "1",
|
||||||
|
"action": "pass",
|
||||||
|
"quick": "1",
|
||||||
|
"interface": "", # floating — all interfaces
|
||||||
|
"direction": "in",
|
||||||
|
"ipprotocol": "inet",
|
||||||
|
"protocol": "udp",
|
||||||
|
"source_net": "any",
|
||||||
|
"source_not": "0",
|
||||||
|
"destination_net": "(self)",
|
||||||
|
"destination_not": "0",
|
||||||
|
"destination_port": "161",
|
||||||
|
"log": "0",
|
||||||
|
"floating": "yes",
|
||||||
|
"descr": "[netork] Allow SNMP",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
lines.append("[firewall] Floating pass rule added for UDP/161.")
|
||||||
|
else:
|
||||||
|
lines.append("[firewall] Pass rule already present.")
|
||||||
|
self._post("/api/firewall/filter/apply", {})
|
||||||
|
lines.append("[firewall] Rules applied.")
|
||||||
|
except Exception as exc:
|
||||||
|
logger.debug("Firewall rule for SNMP failed: %s", exc)
|
||||||
|
lines.append(f"[firewall] error: {exc}")
|
||||||
|
|
||||||
|
# 5. Verify SNMP config
|
||||||
try:
|
try:
|
||||||
cfg = self._get("/api/netsnmp/general/get")
|
cfg = self._get("/api/netsnmp/general/get")
|
||||||
general = cfg.get("general", cfg)
|
general = cfg.get("general", cfg)
|
||||||
@@ -1597,11 +1637,7 @@ class OPNsenseDriver(FirewallDriver):
|
|||||||
logger.debug("SNMP verification failed: %s", exc)
|
logger.debug("SNMP verification failed: %s", exc)
|
||||||
success = False
|
success = False
|
||||||
|
|
||||||
if success:
|
lines.append("[ok] SNMP active." if success else "[warn] Could not verify SNMP state.")
|
||||||
lines.append("[ok] SNMP is active with community 'public'.")
|
|
||||||
else:
|
|
||||||
lines.append("[warn] Could not verify SNMP state via API.")
|
|
||||||
|
|
||||||
return {"success": success, "output": "\n".join(lines)}
|
return {"success": success, "output": "\n".join(lines)}
|
||||||
|
|
||||||
def get_firewall_aliases(self) -> list[dict[str, Any]]:
|
def get_firewall_aliases(self) -> list[dict[str, Any]]:
|
||||||
|
|||||||
Reference in New Issue
Block a user