Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions docs/providers/documentation/snmp-provider.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
title: "SNMP"
sidebarTitle: "SNMP Provider"
description: "Ingest SNMP traps into Keep as alerts via a lightweight `snmptrapd` bridge."
---
import AutoGeneratedSnippet from '/snippets/providers/snmp-snippet-autogenerated.mdx';

<AutoGeneratedSnippet />

## How it works

The SNMP provider is passive. It does not bind to UDP port 162 itself (which
would require root and conflict across tenants). Instead, traps are received
by the standard `snmptrapd` daemon on a host you already operate, and a small
exec hook forwards each parsed trap to Keep's webhook endpoint as JSON.

```
network device ──(SNMP trap UDP:162)──> snmptrapd ──(HTTPS JSON)──> Keep webhook
```

## Connecting with the Provider

1. Install `net-snmp` (ships `snmptrapd`) on a host that can receive traps
from your network devices. Linux: `sudo apt install snmp snmptrapd` or
`sudo yum install net-snmp`. macOS: `brew install net-snmp`.
2. In Keep, add the SNMP provider. Copy the generated webhook URL and API key
shown after install.
3. Configure `snmptrapd` to forward each trap to Keep (see below).

## `snmptrapd` bridge

Add this to `/etc/snmp/snmptrapd.conf` (create it if missing):

```conf
# Accept v2c traps with community "public" — adjust for your environment.
authCommunity log,execute,net public

# Forward every trap to the Keep webhook.
traphandle default /usr/local/bin/keep-snmp-bridge
```

Create `/usr/local/bin/keep-snmp-bridge` and make it executable
(`chmod +x /usr/local/bin/keep-snmp-bridge`):

```bash
#!/usr/bin/env bash
# Minimal snmptrapd → Keep webhook bridge.
# snmptrapd invokes this script with the raw trap on stdin.

KEEP_WEBHOOK_URL="${KEEP_WEBHOOK_URL:?KEEP_WEBHOOK_URL is required}"
KEEP_API_KEY="${KEEP_API_KEY:?KEEP_API_KEY is required}"

read -r source_address
read -r _source_hostname
trap_oid=""
declare -A varbinds
while read -r line; do
# Each varbind line is: OID<space>VALUE
oid="${line%% *}"
val="${line#* }"
if [ -z "$trap_oid" ] && [ "$oid" = "1.3.6.1.6.3.1.1.4.1.0" ]; then
trap_oid="$val"
else
varbinds["$oid"]="$val"
fi
done

# Build JSON payload.
vars_json="{"
sep=""
for k in "${!varbinds[@]}"; do
vars_json+="$sep\"$k\":\"${varbinds[$k]//\"/\\\"}\""
sep=","
done
vars_json+="}"

payload=$(cat <<JSON
{
"trap_oid": "${trap_oid}",
"source_address": "${source_address}",
"variables": ${vars_json},
"version": "2c"
}
JSON
)

curl -fsS -X POST "${KEEP_WEBHOOK_URL}" \
-H "Content-Type: application/json" \
-H "X-API-KEY: ${KEEP_API_KEY}" \
--data "${payload}" \
>/dev/null
```

Export `KEEP_WEBHOOK_URL` and `KEEP_API_KEY` in the service environment for
`snmptrapd`, then restart it:

```bash
sudo systemctl restart snmptrapd # or: sudo /usr/sbin/snmptrapd -Lsd
```

## Trap payload shape

The bridge posts a JSON object with these fields; any can be omitted:

| Field | Type | Description |
|---|---|---|
| `trap_oid` | string | The trap OID (e.g. `1.3.6.1.6.3.1.1.5.3` for `linkDown`). |
| `trap_name` | string | Optional human name used as the alert name. |
| `source_address` | string | IP/host of the device that emitted the trap. |
| `variables` | object | Varbinds as `{ "oid": "value" }`. Exposed on the alert as `labels["var:<oid>"]`. |
| `severity` | string | Optional override: `critical`, `high`, `warning`, `info`, `low`. |
| `description` | string | Optional human-readable description. |
| `community` | string | SNMP community string (v1/v2c). |
| `version` | string | SNMP version: `1`, `2c` or `3`. |

If `severity` is not provided, the provider uses the following built-in
mapping for standard trap OIDs (RFC 1907 / RFC 3418):

| OID | Name | Severity |
|---|---|---|
| `1.3.6.1.6.3.1.1.5.1` | `coldStart` | info |
| `1.3.6.1.6.3.1.1.5.2` | `warmStart` | info |
| `1.3.6.1.6.3.1.1.5.3` | `linkDown` | high |
| `1.3.6.1.6.3.1.1.5.4` | `linkUp` | info |
| `1.3.6.1.6.3.1.1.5.5` | `authenticationFailure` | warning |
| `1.3.6.1.6.3.1.1.5.6` | `egpNeighborLoss` | warning |

Anything else falls back to the `default_severity` you configured on the
provider.

## Testing

Send a synthetic trap with `snmptrap` from the CLI:

```bash
snmptrap -v 2c -c public localhost '' 1.3.6.1.6.3.1.1.5.3 \
1.3.6.1.2.1.2.2.1.1 i 2 \
1.3.6.1.2.1.2.2.1.7 i 2
```

You should see a new alert in Keep within a second or two.

## Useful links

- [net-snmp documentation](http://www.net-snmp.org/docs/)
- [`snmptrapd.conf` reference](http://net-snmp.sourceforge.net/docs/man/snmptrapd.conf.html)
- [RFC 1907 — Standard SNMPv2 traps](https://www.rfc-editor.org/rfc/rfc1907)
Binary file added keep-ui/public/icons/snmp-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions keep/providers/snmp_provider/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from keep.providers.snmp_provider.snmp_provider import (
SnmpProvider,
SnmpProviderAuthConfig,
)

__all__ = ["SnmpProvider", "SnmpProviderAuthConfig"]
42 changes: 42 additions & 0 deletions keep/providers/snmp_provider/alerts_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""Sample SNMP trap payloads used by `SnmpProvider.simulate_alert`."""

TRAPS = {
"linkDown": {
"trap_oid": "1.3.6.1.6.3.1.1.5.3",
"trap_name": "linkDown",
"source_address": "192.0.2.10",
"community": "public",
"version": "2c",
"variables": {
"1.3.6.1.2.1.2.2.1.1": "2",
"1.3.6.1.2.1.2.2.1.7": "2",
},
"description": "Interface eth1 went down",
"severity": "high",
},
"linkUp": {
"trap_oid": "1.3.6.1.6.3.1.1.5.4",
"trap_name": "linkUp",
"source_address": "192.0.2.10",
"community": "public",
"version": "2c",
"variables": {"1.3.6.1.2.1.2.2.1.1": "2"},
"description": "Interface eth1 came back up",
},
"coldStart": {
"trap_oid": "1.3.6.1.6.3.1.1.5.1",
"trap_name": "coldStart",
"source_address": "192.0.2.20",
"community": "public",
"version": "2c",
"description": "Device rebooted and reinitialised",
},
"authenticationFailure": {
"trap_oid": "1.3.6.1.6.3.1.1.5.5",
"trap_name": "authenticationFailure",
"source_address": "192.0.2.30",
"community": "public",
"version": "2c",
"description": "SNMP authentication failure from 10.0.0.5",
},
}
Loading
Loading