Automated SNMPv3 Configuration

Automated SNMPv3 Configuration

This blog post is about SNMPv3. As many Engineers familiar with SNMP will know, SNMP is a powerful protocol that can divulge a lot of information if not properly locked down. The security mechanisms built into SNMP versions 1 and 2c are severely lacking, and the plain-text community authentication introduces critical vulnerabilities into the little amount of security that is supported.

Enter SNMP v3, which is certainly a good improvement. Using a credential-oriented framework instead of global communities, v3 can be fine tuned from a range of authentication and encryption standards per-user. The approch is also much more modular, where read and write permission are defined in classes, and applied to user account, and grouping under SNMP groups definitely helps tie all the configuration together. Naturally I like to avoid the use of MD5 where possible and so support for SHA hashing is nice to see, though it is unfortunate that this only seems to include the definitively broken SHA-1.

Cisco's configuration syntax for SNMPv3 is not overly complex, and they have done a good job of keeping things very orderly. The below summarises the entirety of the base configuration options available:

SNMP Groups:

snmp-server group [groupname {v1 | v2c | v3{auth | noauth | priv}}] [read readview] [write writeview] [notify notifyview] [access access-list]

SNMP Users:

snmp-server user username [groupname remote ip-address [udp-port port] {v1 | v2c | v3 [encrypted] [auth {md5 | sha} auth-password [priv des56 priv password]] [access access-list]

SNMP Views:

snmp-server view view-name oid-tree {included | excluded}

Cisco IOS base SNMPv3 configuration:

 snmp-server view <Write View> iso included 
 snmp-server view <Read View> iso included 
 snmp-server group <Group> v3 [auth/noauth] read <Read View> write <Write View> 
 snmp-server user <User> <Group> v3 auth [sha/md5] <password> 
 snmp-server host <IP Address> version 3 auth <User> 
 snmp-server enable traps 

In this post I am going to be automating the SNMPv3 configuration for several network devices...


My Configuration Workflow

Now, a management-driven security requirement was for us to use a unique SNMP community for each device. Since SNMPv3 uses a Username/Password combination instead of a global community, I chose to randomise the passwords instead.

There is no such thing as true randomness, but Python's Secrets Library does a good enough job:

PasswordGenerator.py

#!/bin/python3.6

import secrets
import string
alphabet = string.ascii_letters + string.digits

for i in range(8):
    # where 8 is the number of passwords tp generate
    password = ''.join(secrets.choice(alphabet) for i in range(32))
    # where 32 is the character length of each password
    print(password)

I can now run the script and generate as many random password combinations as I want:

[skylar@SkyeNetDev Passwords]$ ./PasswordGenerator.py 
xftckyaGXjybzIz1C2TsUTE6gVHUzVFx
0qyurbTApwTHtx5MsA1AuXMlZPM6e8gU
5ttRXSMuHkBOLdJE9qU6NChkeLQvRiDR
GgiRn5ihgZeaYrA1r1WudSmpsDTfeMBQ
YtvfaPZwweyoSyEn7KJ7Ov9KhErB781X
AoffBtlArD7jBxmV3eBjAssBTCQE2M06
emkZNtNRmUXlAhPXUnd888qOlJW8oJVU
t5zfr82kO75Vze1Dc6JVtWqfBoUUPkmQ

Now let's rerun this and output to a file to refer to later on:

python3.6 PasswordGenerator.py > passwords.txt

Next up, I needed a list of hostnames to iterate over:

devices.txt

switch-1
switch-2
switch-3
switch-4
switch-5
switch-6
switch-7
switch-8

By now I still didn't know how I was planning on handling the configuration, but I was leaning towards Jjinja2 which takes variables in JSON format. Either way I need to format these values somehow.

First I merged the Devices and Passwords files into CSV format:

echo "hostname, password" > SNMPv3.csv
paste -d',' devices.txt passwords.txt > SNMPv3.csv

Which worked as intended:

switch-1,xftckyaGXjybzIz1C2TsUTE6gVHUzVFx
switch-2,0qyurbTApwTHtx5MsA1AuXMlZPM6e8gU
switch-3,5ttRXSMuHkBOLdJE9qU6NChkeLQvRiDR
switch-4,GgiRn5ihgZeaYrA1r1WudSmpsDTfeMBQ
switch-5,YtvfaPZwweyoSyEn7KJ7Ov9KhErB781X
switch-6,AoffBtlArD7jBxmV3eBjAssBTCQE2M06
switch-7,emkZNtNRmUXlAhPXUnd888qOlJW8oJVU
switch-8,t5zfr82kO75Vze1Dc6JVtWqfBoUUPkmQ

Now I used this site here to convert the CSV to JSON format. There are a ton of other methods in Python and Bash (and every other language, I'm sure), but this was quick and easy.

I now have a JSON inventory:

Parameters.json

[
  {
    "hostname": "switch-1",
    "password": "xftckyaGXjybzIz1C2TsUTE6gVHUzVFx"
  },
  {
    "hostname": "switch-2",
    "password": "0qyurbTApwTHtx5MsA1AuXMlZPM6e8gU"
  },
  {
    "hostname": "switch-3",
    "password": "5ttRXSMuHkBOLdJE9qU6NChkeLQvRiDR"
  },
  {
    "hostname": "switch-4",
    "password": "GgiRn5ihgZeaYrA1r1WudSmpsDTfeMBQ"
  },
  {
    "hostname": "switch-5",
    "password": "YtvfaPZwweyoSyEn7KJ7Ov9KhErB781X"
  },
  {
    "hostname": "switch-6",
    "password": "AoffBtlArD7jBxmV3eBjAssBTCQE2M06"
  },
  {
    "hostname": "switch-7",
    "password": "emkZNtNRmUXlAhPXUnd888qOlJW8oJVU"
  },
  {
    "hostname": "switch-8",
    "password": "t5zfr82kO75Vze1Dc6JVtWqfBoUUPkmQ"
  }
]

Save this output as a JSON file, and we can move on to generating our config.

Jinja2 Installation

We will now install Jinja2 to automate our config generation. Make a new Python virtual environment, and install Jjinja2:

python3.6 -m venv PythonEnvironment
source PythonEnvironment/bin/active
cd PythonEnvironment
pip3.6 install jinja2

We will now create our Jinja template file. Jinja is extremely flexible, and you can consider anything in a .j2 file to be plain text. The only sections of the file that Jinja really cares about are the variables put between double curly brackets, such as {{ variable }}.

Now for this simple SNMPv3 configuration, the only variable is the devices password. In fact, if I were to configure each device with the same password then I would never have had this problem in the first place! Nevertheless, write your config and put the password in curl brackets:

Config.j2

conf t
ip access-list standard SNMPv3-ACL
 permit x.x.x.x
 ! single SNMP collector address
 permit y.y.y.y 0.0.0.31
 ! a SNMP collector subnet

snmp-server group SNMP-Group v3 auth read SNMP-Read write SNMP-Write access SNMPv3-ACL
snmp-server user <username> SNMP-Group v3 auth sha {{ password }} access SNMPv3-ACL
snmp-server view SNMP-Read iso included
snmp-server view SNMP-Write iso included
snmp-server enable traps

Now that we have our JSON parameters and our Jinja2 template to fill with the parameters, all we need to do is marry the two together. I created the following Python script to do just that:

Generator.py

#!/usr/bin/python

import jinja2
import json
import os

configtemplate = "Config.j2"
parameters = "Parameters.json"
output = "Output"

config_parameters = json.load(open(parameters))

env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath="."),
                         trim_blocks=True,
                         lstrip_blocks=True)
template = env.get_template(configtemplate)

if not os.path.exists(output):
    os.mkdir(output)

for parameter in parameters:
    result = configtemplate.render(parameter)
    f = open(os.path.join(output, parameter['hostname']), "w")
    f.write(result)
    f.close()

This script will open our JSON parameter file and Jinja2 config template from the working directory, and will begin generating configs. For each hostname, it will fill in the associated password and save the full configuration in a file named after the hostname in a new directory "Output":

[skylar@SkyeNetDev Output]$ ls 
switch-1    switch-2    switch-3    switch-4
switch-5    switch-6    switch-7    switch-8

At this point, you can go ahead and paste each config onto the respective device. I actually had far more than 8 devices in this little exercise, and so I made a little Bash script using the RANCID library to automate the configuration push (alternatively this could be loaded into an Ansible playbook, or the ExScript Python library is also fantastic. I used RANCID for it's simplicity and ease of use).

ConfigPush.sh

#!/bin/bash

while read hostname; do
clogin -x $hostname $hostname
done < devices.txt

Boom! Super easy, CLogin will iterate through our list of hostnames and read commands from the respective config file to push to each device.

We are now SNMPv3! Set up each device on your SNMP collector. We use LibreNMS, and this can be done manually through the GUI:

Luckily there is also a PHP script included in the software to automate this:

./addhost.php -g SNMP-Group <device> any v3 username <password> sha

This command will require a new password for each device, in my use case. I used Jinja2 to help ;)


So this been a quick look at a rather messy workflow to accomplish a task that could otherwise take a lot longer. Nevertheless, thanks for reading along!

Related Article