Conversation
|
Target branch is not in the allowed branches list. |
|
@natkon20043 is attempting to deploy a commit to the KeepHQ Team on Vercel. A member of the Team first needs to authorize it. |
|
I signed in and approved it did that work? Im sorry im really new to this and dont really know how to do it |
|
@natkon20043 Thanks for the contribution. I have a requirement on snmp trap and want to send the alerts to KeepHQ. I used your code and able to push the trap message and alert is pushed (as it's saying from the logs) just like how you showed in the video demo. How can we see the alerts on the KeepUI? can you share some inputs |
|
Absolutely I will begin working on that now! |
There was a problem hiding this comment.
Pull request overview
Adds a new SNMP trap receiver provider so Keep can ingest SNMP traps as alerts.
Changes:
- Introduces an SNMP provider implementation intended to listen for traps and push them into Keep.
- Adds a Pydantic dataclass for SNMP authentication/UI configuration.
- Exposes the provider via the snmp_provider package
__init__.py.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 16 comments.
| File | Description |
|---|---|
| keep/providers/snmp_provider/snmp_provider.py | Implements SNMP trap receiving and alert construction/push logic. |
| keep/providers/snmp_provider/config.py | Defines the SNMP provider authentication configuration dataclass for UI/config. |
| keep/providers/snmp_provider/init.py | Exports the provider class from the package. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| from pysnmp.carrier.asyncio.dgram import udp | ||
| from pysnmp.entity import engine, config | ||
| from pysnmp.entity.rfc3413 import ntfrcv |
There was a problem hiding this comment.
This module imports pysnmp, but the repository dependency manifest doesn't currently include any pysnmp package. Without adding the dependency (and pinning an appropriate distribution, per the PR description), Keep will fail to import this provider and CI/docs validation will break. Add the required pysnmp dependency to pyproject.toml/poetry.lock (or guard imports similarly to other optional providers).
| "fingerprint": f"{source_ip}-{ next( iter( trap_data ), 'empty' ) }", | ||
| "trap_data": trap_data, # Structured data | ||
| "details": "\n".join( msg ), # Readable string | ||
| "host": source_ip, | ||
| } |
There was a problem hiding this comment.
Keys like 'trap_data', 'details', and 'host' are not part of AlertDto and will be dropped when BaseProvider._push_alert() builds the model. If you need varbinds stored/searchable in Keep, map them into supported fields such as 'labels' and/or include them in 'description'.
| port: int = pydantic.Field( | ||
| default=DEFAULTPORT, # SNMP default port | ||
| metadata={ | ||
| "required": True, | ||
| "description": "SNMP port for listening for traps", | ||
| "hint": "SNMP defaults to 1162; elevate to port 162 for standard SNMP (requires root)" | ||
| } | ||
| ) | ||
| community: str = pydantic.Field( |
There was a problem hiding this comment.
pydantic.Field does not accept a 'metadata' kwarg; this will raise a TypeError at import time. For pydantic dataclasses, use dataclasses.field(metadata={...}, default=...) (see other providers' *ProviderAuthConfig classes).
| port: int = pydantic.Field( | |
| default=DEFAULTPORT, # SNMP default port | |
| metadata={ | |
| "required": True, | |
| "description": "SNMP port for listening for traps", | |
| "hint": "SNMP defaults to 1162; elevate to port 162 for standard SNMP (requires root)" | |
| } | |
| ) | |
| community: str = pydantic.Field( | |
| port: int = dataclasses.field( | |
| default=DEFAULTPORT, # SNMP default port | |
| metadata={ | |
| "required": True, | |
| "description": "SNMP port for listening for traps", | |
| "hint": "SNMP defaults to 1162; elevate to port 162 for standard SNMP (requires root)" | |
| } | |
| ) | |
| community: str = dataclasses.field( |
| from keep.providers.snmp_provider.config import SNMPProviderAuthConfig | ||
|
|
||
|
|
||
| class SNMPProvider( BaseProvider ): # SNMP Provider that listens for SNMP traps and pushes them as alerts to Keep. |
There was a problem hiding this comment.
ProvidersFactory resolves provider classes by name (e.g., for provider_type='snmp' it imports keep.providers.snmp_provider.snmp_provider and looks for a class named 'SnmpProvider'). The class here is named 'SNMPProvider', so provider initialization will fail with AttributeError. Rename the provider class to 'SnmpProvider' (or adjust factory naming, but that would be a wider change) and ensure exports/use-sites follow the same name.
| class SNMPProvider( BaseProvider ): # SNMP Provider that listens for SNMP traps and pushes them as alerts to Keep. | |
| class SnmpProvider( BaseProvider ): # SNMP Provider that listens for SNMP traps and pushes them as alerts to Keep. |
| super().dispose() | ||
|
|
||
| def _process_trap( self, snmpEngine, stateReference, contextEngineId, contextName, varBinds, cbCtx ): # Callback function executed when an SNMP trap is received. | ||
| transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo( stateReference ) |
There was a problem hiding this comment.
transportDomain is assigned but never used, which will fail ruff/flake8 (F841) in CI. Use '_' for the unused value or remove the assignment.
| transportDomain, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo( stateReference ) | |
| _, transportAddress = snmpEngine.msgAndPduDsp.getTransportInfo( stateReference ) |
| "name": "SNMP Trap", | ||
| "source": ["snmp"], | ||
| "description": f"Trap received from { source_ip }", | ||
| "status": "info", # Default status could be mapped from specific OIDs |
There was a problem hiding this comment.
The emitted alert payload uses status='info', but Keep's AlertStatus only accepts values like 'firing', 'resolved', etc. This will cause AlertDto validation to fail inside BaseProvider._push_alert(). Use a valid status (typically 'firing' for traps) and keep 'info' in the severity field.
| "status": "info", # Default status could be mapped from specific OIDs | |
| "status": "firing", # Default status for traps; detailed level stays in severity |
|
|
||
| self.logger.info( f"Starting SNMP Trap listener on { host }:{ port } with community '{ community }'" ) | ||
|
|
There was a problem hiding this comment.
The startup log line includes the SNMP community string. Community strings function as shared secrets and shouldn't be logged. Avoid logging it (or mask it), and mark the community field as sensitive in the auth config metadata so it’s not displayed in cleartext in the UI.
| self.logger.info( f"Starting SNMP Trap listener on { host }:{ port } with community '{ community }'" ) | |
| # Do not log the community string in cleartext; mask it in logs | |
| masked_community = "*" * max(len(str(community)), 8) | |
| self.logger.info( f"Starting SNMP Trap listener on { host }:{ port } with community '{ masked_community }'" ) |
| from keep.providers.models.provider_config import ProviderConfig | ||
| from keep.providers.snmp_provider.config import SNMPProviderAuthConfig | ||
|
|
||
|
|
There was a problem hiding this comment.
ProvidersFactory.get_provider_required_config expects the auth config class to be named 'SnmpProviderAuthConfig' and to be defined in keep.providers.snmp_provider.snmp_provider (same module as the provider). Defining 'SNMPProviderAuthConfig' in a separate config.py means the UI/config discovery will return {}. Move/duplicate/re-export the auth config class into snmp_provider.py with the expected name, or adjust the factory if you want a different pattern.
| class SnmpProviderAuthConfig(SNMPProviderAuthConfig): | |
| """ | |
| Compatibility wrapper so ProvidersFactory.get_provider_required_config can | |
| discover the SNMP provider auth config in this module under the expected name. | |
| """ | |
| pass |
| @@ -0,0 +1 @@ | |||
| from .snmp_provider import SNMPProvider No newline at end of file | |||
There was a problem hiding this comment.
keep/providers/snmp_provider/init.py exports SNMPProvider, but provider loading via ProvidersFactory expects 'SnmpProvider' for provider_type='snmp'. After renaming the class for factory compatibility, update this export accordingly.
| from .snmp_provider import SNMPProvider | |
| from .snmp_provider import SnmpProvider |
| class SNMPProvider( BaseProvider ): # SNMP Provider that listens for SNMP traps and pushes them as alerts to Keep. | ||
| def __init__( self, context_manager, provider_id: str, config: ProviderConfig ): | ||
| super().__init__( context_manager, provider_id, config ) | ||
| self.logger = logging.getLogger( __name__ ) |
There was a problem hiding this comment.
init overwrites BaseProvider's logger (which is configured with provider_id, log level env vars, and optional DB-backed ProviderLoggerAdapter). Reassigning self.logger to logging.getLogger(name) drops that context/config. Use the logger provided by BaseProvider instead of overwriting it.
| self.logger = logging.getLogger( __name__ ) |
2026-02-28.16-32-01.mp4Here is the new verison of the snmp provider. Sorry it has taken me so long to complete, I am currently in college aswell as a teacher so my time is thin, I hope the newest version is good though! |
|
I had to change my pydantic.Field metadata to dataclass.field, this was why it would not show up on the GUI, I also updated the API since it seems pydantic switched from camel case to snake case. I also fixed my commenting style and removed excess ai comments in my functions |
|
I am just waiting on vercel auth now |
Closes #2112
📑 Description
Implemented a new SNMP Provider that allows Keep to act as an SNMP Trap receiver. This enables real-time alert ingestion from network devices and servers.
SNMPProviderlogic with asynchronous trap listening.SNMPProviderAuthConfigusing Pydantic for UI integration.✅ Checks
ℹ Additional Information
Demo Video
Technical Details
pysnmp-lextudiofor modern Python 3.13 support.runDispatcher(0.1)pattern to ensure Keep remains responsive.snmptrapcommands.Verification Command
To verify, I ran the provider and sent a test trap:
snmptrap -v 2c -c public localhost:1162 '' 1.3.6.1.4.1.8072.2.3.0.1 1.3.6.1.2.1.1.1.0 s "Keep-Bounty-Verification"Video_2026-01-13_18-59-38.mp4