2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-01-11 18:42:29 +00:00

Allow specifiying SNMP privacy protocol

Modern SNMP devices may require AES.

Unfortunately, older ones may refuse AES.

For compatibility, continue to default to DES, but
allow AES to be indicated in attributes.
This commit is contained in:
Jarrod Johnson
2025-11-10 10:21:01 -05:00
parent 20292cdfd0
commit 6ebb6de107
5 changed files with 40 additions and 21 deletions

View File

@@ -605,6 +605,10 @@ node = {
'description': ('SNMPv1 community string, it is highly recommended to'
'step up to SNMPv3'),
},
'snmp.privacyprotocol': {
'description': 'The privacy protocol to use for SNMPv3',
'valid_values': ('aes', 'des'),
},
# 'secret.snmplocalizedkey': {
# 'description': ("SNMPv3 key localized to this node's SNMP Engine id"
# 'This can be used in lieu of snmppassphrase to avoid'

View File

@@ -255,7 +255,13 @@ def _extract_neighbor_data_b(args):
args are carried as a tuple, because of eventlet convenience
"""
switch, password, user, cfm, force = args[:5]
# Safely unpack args with defaults to avoid IndexError
switch = args[0] if len(args) > 0 else None
password = args[1] if len(args) > 1 else None
user = args[2] if len(args) > 2 else None
cfm = args[3] if len(args) > 3 else None
privproto = args[4] if len(args) > 4 else None
force = args[5] if len(args) > 5 else False
vintage = _neighdata.get(switch, {}).get('!!vintage', 0)
now = util.monotonic_time()
if vintage > (now - 60) and not force:
@@ -265,7 +271,7 @@ def _extract_neighbor_data_b(args):
return _extract_neighbor_data_https(switch, user, password, cfm, lldpdata)
except Exception as e:
pass
conn = snmp.Session(switch, password, user)
conn = snmp.Session(switch, password, user, privacy_protocol=privproto)
sid = None
for sysid in conn.walk('1.3.6.1.2.1.1.2'):
sid = str(sysid[1][6:])
@@ -364,8 +370,8 @@ def _extract_neighbor_data(args):
return _extract_neighbor_data_b(args)
except Exception as e:
yieldexc = False
if len(args) >= 6:
yieldexc = args[5]
if len(args) >= 7:
yieldexc = args[6]
if yieldexc:
return e
else:

View File

@@ -213,14 +213,14 @@ def _fast_backend_fixup(macs, switch):
else:
_nodesbymac[mac] = (nodename, nummacs)
def _offload_map_switch(switch, password, user):
def _offload_map_switch(switch, password, user, privprotocol=None):
if _offloader is None:
_start_offloader()
evtid = random.randint(0, 4294967295)
while evtid in _offloadevts:
evtid = random.randint(0, 4294967295)
_offloadevts[evtid] = eventlet.Event()
_offloader.stdin.write(msgpack.packb((evtid, switch, password, user),
_offloader.stdin.write(msgpack.packb((evtid, switch, password, user, privprotocol),
use_bin_type=True))
_offloader.stdin.flush()
result = _offloadevts[evtid].wait()
@@ -280,12 +280,11 @@ def _map_switch_backend(args):
# fallback if ifName is empty
#
global _macmap
if len(args) == 4:
switch, password, user, _ = args # 4th arg is for affluent only
if not user:
user = None
else:
switch, password = args
switch = args[0] if len(args) > 0 else None
password = args[1] if len(args) > 1 else None
user = args[2] if len(args) > 2 else None
privprotocol = args[4] if len(args) > 4 else None
if not user: # make '' be treated as None
user = None
if switch not in noaffluent:
try:
@@ -298,7 +297,7 @@ def _map_switch_backend(args):
except Exception as e:
pass
mactobridge, ifnamemap, bridgetoifmap = _offload_map_switch(
switch, password, user)
switch, password, user, privprotocol)
maccounts = {}
bridgetoifvalid = False
for mac in mactobridge:
@@ -367,9 +366,9 @@ def _map_switch_backend(args):
_nodesbymac[mac] = (nodename, maccounts[ifname])
_macsbyswitch[switch] = newmacs
def _snmp_map_switch_relay(rqid, switch, password, user):
def _snmp_map_switch_relay(rqid, switch, password, user, privprotocol=None):
try:
res = _snmp_map_switch(switch, password, user)
res = _snmp_map_switch(switch, password, user, privprotocol)
payload = msgpack.packb((rqid,) + res, use_bin_type=True)
try:
sys.stdout.buffer.write(payload)
@@ -391,10 +390,10 @@ def _snmp_map_switch_relay(rqid, switch, password, user):
finally:
sys.stdout.flush()
def _snmp_map_switch(switch, password, user):
def _snmp_map_switch(switch, password, user, privprotocol=None):
haveqbridge = False
mactobridge = {}
conn = snmp.Session(switch, password, user)
conn = snmp.Session(switch, password, user, privacy_protocol=privprotocol)
ifnamemap = get_portnamemap(conn)
for vb in conn.walk('1.3.6.1.2.1.17.7.1.2.2.1.2'):
haveqbridge = True

View File

@@ -21,7 +21,7 @@ import confluent.collective.manager as collective
def get_switchcreds(configmanager, switches):
switchcfg = configmanager.get_node_attributes(
switches, ('secret.hardwaremanagementuser', 'secret.snmpcommunity',
'secret.hardwaremanagementpassword',
'secret.hardwaremanagementpassword', 'snmp.privacyprotocol',
'collective.managercandidates'), decrypt=True)
switchauth = []
for switch in switches:
@@ -47,7 +47,9 @@ def get_switchcreds(configmanager, switches):
'secret.hardwaremanagementuser', {}).get('value', None)
if not user:
user = None
switchauth.append((switch, password, user, configmanager))
privacy_protocol = switchparms.get(
'snmp.privacyprotocol', {}).get('value', None)
switchauth.append((switch, password, user, configmanager, privacy_protocol))
return switchauth

View File

@@ -78,7 +78,7 @@ def _get_transport(name):
class Session(object):
def __init__(self, server, secret, username=None, context=None):
def __init__(self, server, secret, username=None, context=None, privacy_protocol=None):
"""Create a new session to interrogate a switch
If username is not given, it is assumed that
@@ -97,9 +97,17 @@ class Session(object):
# SNMP v2c
self.authdata = snmp.CommunityData(secret, mpModel=1)
else:
if privacy_protocol == 'aes':
privproto = snmp.usmAesCfb128Protocol
elif privacy_protocol in ('des', None):
privproto = snmp.usmDESPrivProtocol
else:
raise exc.ConfluentException('Unsupported SNMPv3 privacy protocol '
'{0}'.format(privacy_protocol))
self.authdata = snmp.UsmUserData(
username, authKey=secret, privKey=secret,
authProtocol=snmp.usmHMACSHAAuthProtocol)
authProtocol=snmp.usmHMACSHAAuthProtocol,
privProtocol=privproto)
self.eng = snmp.SnmpEngine()
def walk(self, oid):