Skip to content
This repository has been archived by the owner on Dec 17, 2021. It is now read-only.

Commit

Permalink
fix(config): Change default port to 2162 (#22)
Browse files Browse the repository at this point in the history
fix: Accept connections on all interfaces

Accept ipv4 and ipv6 by default
Listen on all interfaces

fix(output): Add try catch for post and don't log sensitive data

fix: Update sourcetype for traps

Reuse the same HTTP Session object

Reuse the same HTTP Session object

fix: Names clashing

It looks like having the same name for python file inside a package with the same
name doesn't get resolved by Pycharm. It'll complain that
"no such module", I have changed the the name something different and it worked just
fine. Not sure if this is a Pycharm issue or not, but now with this change we can
run the server both in Pycharm and poetry.

refactor: removed useless logger.debug() statements

Reuse the same HTTP Session object

Reuse the same HTTP Session object

fix: Names clashing

It looks like having the same name for python file inside a package with the same
name doesn't get resolved by Pycharm. It'll complain that
"no such module", I have changed the the name something different and it worked just
fine. Not sure if this is a Pycharm issue or not, but now with this change we can
run the server both in Pycharm and poetry.

refactor: removed useless logger.debug() statements

Co-authored-by: rfaircloth-splunk <[email protected]>
  • Loading branch information
lstoppa and rfaircloth-splunk authored Feb 2, 2021
1 parent 739528e commit 505913c
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 64 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pytest = "*"
black = "20.8b1"

[tool.poetry.scripts]
sc4snmp-traps = "splunk_connect_for_snmp_traps.splunk_connect_for_snmp_traps:main"
sc4snmp-traps = "splunk_connect_for_snmp_traps.snmp_trap_server:main"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
Expand Down
3 changes: 3 additions & 0 deletions splunk_connect_for_snmp_traps/manager/hec_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import logging
import os

logger = logging.getLogger(__name__)


class HecConfiguration:
endpoints_env_variable_name = 'SPLUNK_HEC_URL'
Expand Down
56 changes: 56 additions & 0 deletions splunk_connect_for_snmp_traps/manager/hec_sender.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import concurrent.futures
import logging
import threading

import requests

from splunk_connect_for_snmp_traps.manager.hec_config import HecConfiguration
from splunk_connect_for_snmp_traps.manager.os_config_utils import (
max_allowed_working_threads,
)

logger = logging.getLogger(__name__)


class HecSender:
def __init__(self, server_config):
self._server_config = server_config
self._hec_config = HecConfiguration()
self._thread_local = threading.local()
self._thread_pool_executor = self.configure_thread_pool()

def configure_thread_pool(self):
user_suggested_working_threads = self._server_config["thread-pool"][
"max-suggested-working-threads"
]
max_workers = max_allowed_working_threads(user_suggested_working_threads)
logger.debug(f"Configured a thread-pool with {max_workers} concurrent threads")
return concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)

def get_session(self):
if not hasattr(self._thread_local, "session"):
self._thread_local.session = requests.Session()
return self._thread_local.session

def post_data_to_thread_pool(self, variables_binds):
headers = {
"Authorization": f"Splunk {self._hec_config.get_authentication_token()}"
}
splunk_trap_data = ",".join(
[str(key) + str(value) for key, value in variables_binds]
)
data = {"sourcetype": "trap-server", "event": splunk_trap_data}
try:
session = self.get_session()
for endpoint in self._hec_config.get_endpoints():
response = session.post(
url=endpoint, json=data, headers=headers, verify=False
)
logger.debug(f"Response code is {response.status_code}")
except requests.ConnectionError as e:
logger.error(f"Connection error when sending data to HEC: {e}")

def post_data(self, variables_binds):
self._thread_pool_executor.submit(
self.post_data_to_thread_pool, variables_binds
)
76 changes: 13 additions & 63 deletions splunk_connect_for_snmp_traps/manager/trap_server.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import concurrent.futures
import logging

import requests
from pysnmp.carrier.asyncore.dgram import udp, udp6
from pysnmp.entity import engine, config
from pysnmp.entity.rfc3413 import ntfrcv

from splunk_connect_for_snmp_traps.manager.hec_config import HecConfiguration
from splunk_connect_for_snmp_traps.manager.os_config_utils import (
max_allowed_working_threads,
)
from splunk_connect_for_snmp_traps.manager.hec_sender import HecSender

logger = logging.getLogger(__name__)

Expand All @@ -19,36 +14,18 @@ def __init__(self, server_config):
self._server_config = server_config
self._snmp_engine = engine.SnmpEngine()
self.configure_trap_server()
self._hec_config = HecConfiguration()
self._thread_pool_executor = self.configure_thread_pool()

def configure_thread_pool(self):
user_suggested_working_threads = self._server_config["thread-pool"][
"max-suggested-working-threads"
]
max_workers = max_allowed_working_threads(user_suggested_working_threads)
logger.debug(f"Configured a thread-pool with {max_workers} concurrent threads")
return concurrent.futures.ThreadPoolExecutor(max_workers=max_workers)
self._hec_sender = HecSender(self._server_config)

def configure_trap_server(self):
self._snmp_engine.observer.registerObserver(
self.request_observer,
"rfc3412.receiveMessage:request",
"rfc3412.returnResponsePdu",
)
snmp_config = self._server_config["snmp"]
if snmp_config["ipv4"]:
config.addTransport(
self._snmp_engine,
udp.domainName,
udp.UdpTransport().openServerMode(("0.0.0.0", snmp_config["port"])),
)
if snmp_config["ipv6"]:
config.addTransport(
self._snmp_engine,
udp6.domainName,
udp6.Udp6Transport().openServerMode(("::0", snmp_config["port"])),
)
self._snmp_engine.observer.registerObserver(self.request_observer, 'rfc3412.receiveMessage:request',
'rfc3412.returnResponsePdu')
snmp_config = self._server_config['snmp']
if snmp_config['ipv4']:
config.addTransport(self._snmp_engine, udp.domainName,
udp.UdpTransport().openServerMode(('0.0.0.0', snmp_config['port'])))
if snmp_config['ipv6']:
config.addTransport(self._snmp_engine, udp6.domainName,
udp6.Udp6Transport().openServerMode(('::0', snmp_config['port'])))
# SNMPv1/2c setup
# SecurityName <-> CommunityName mapping
for community in snmp_config["communities"]["v1"]:
Expand All @@ -57,33 +34,6 @@ def configure_trap_server(self):
# Register SNMP Application at the SNMP engine
ntfrcv.NotificationReceiver(self._snmp_engine, self.snmp_callback_function)

def post_trap_to_hec(self, variables_binds):
logger.debug("Task received, sleeping for few seconds ...")
for endpoint in self._hec_config.get_endpoints():
headers = {
"Authorization": f"Splunk {self._hec_config.get_authentication_token()}"
}
splunk_trap_data = ",".join(
[str(key) + str(value) for key, value in variables_binds]
)
data = {"sourcetype": "sc4snmp:trap", "event": splunk_trap_data}
logger.debug(
f"Posting trap to HEC using {endpoint} and tlsmode {self._hec_config.is_ssl_enabled}"
)
try:
response = requests.post(
url=endpoint,
json=data,
headers=headers,
verify=self._hec_config.is_ssl_enabled(),
)
logger.debug(f"Response code is {response.status_code}")
except:
logger.warning(
f"Exception posting trap to HEC using {endpoint} and tlsmode {self._hec_config.is_ssl_enabled} with headers {headers}",
exc_info=True,
)

# Register a callback to be invoked at specified execution point of
# SNMP Engine and passed local variables at code point's local scope
# noinspection PyUnusedLocal,PyUnusedLocal
Expand Down Expand Up @@ -123,8 +73,8 @@ def snmp_callback_function(
% (context_engine_id.prettyPrint(), context_name.prettyPrint())
)
for name, val in var_binds:
logger.debug("%s = %s" % (name.prettyPrint(), val.prettyPrint()))
self._thread_pool_executor.submit(self.post_trap_to_hec, var_binds)
logger.debug('%s = %s' % (name.prettyPrint(), val.prettyPrint()))
self._hec_sender.post_data(var_binds)

def run_trap_server(self):
self._snmp_engine.transportDispatcher.jobStarted(1)
Expand Down

0 comments on commit 505913c

Please sign in to comment.