Using Salt Grains to Output System Interface Names to suricata.yaml

Kia Matthews
3 min readSep 27, 2019

I am a SOC Engineer in a shop that deploys the exact same server loaded with Suricata to each of our company’s locations, and we use Salt to deploy the exact same configuration files to each of these servers. We buy the servers 5 or 10 at a time and recently I realized that, while we are ordering the same server build from our vendor, some things change ever so slightly, like the model of the NICs. As such, the names of the interfaces (enp24s0f0, enp24s0f1 and so on) that are used to monitor the network traffic have changed. This means using a simple “file.managed” for suricata.yaml wasn’t going to cut it any more. Luckily, Salt Grains and jinja exist. I figured I could just call that Salt grain for interfaces names and dump that into suricata.yaml.

Saltstack’s Grains interface gives us a great deal of information about a Salt minion. Unfortunately, none of the standard grains output the names of the interfaces as values. The interface names do, however, exist as keys — example:

admin@saltmaster:~$ sudo salt "ids-server" grains.get ip_interfaces
ids-server:
----------
eno1:
- 192.168.xx.xx
eno2:
- 192.168.xx.xx
enp24s0f0:
enp24s0f1:
enp24s0f2:
enp24s0f3:
lo:
- 127.0.0.1

Luckily, someone smarter than me already tackled this issue. Thomas created a simple custom grain that looks for a system’s interface names, checks if the interface has an IP assigned to it, and if so, outputs the interface name. I modified this slightly, since I wanted the grain to list all interfaces, IP assignment notwithstanding.

Python 3 version (added 11.25.20)

When I originally wrote this article in 2019, I was using Python 2 with salt. Once I updated my Salt minions to use Python 3, the custom grain was no longer able to be created and I saw NameError: name '__utils__' is not defined, errors in my salt minion log. Turns out that when using Python 3, dunders are no longer available using salt.modules.network, so the script needs to be modified to use salt.utils.network.

#!/usr/bin/python3import salt.utils.networkdef internal_interfaces():grains = {}interfaces = salt.utils.network.interfaces()grains['internal_interfaces'] = []for interface, data in interfaces.items():
if 'enp' in interface:
grains['internal_interfaces'].append(interface)
return grains

Python 2 version

#!/usr/bin/env pythonimport salt.modules.networkdef internal_interfaces():grains = {}interfaces = salt.modules.network.interfaces()grains['internal_interfaces'] = []for interface, data in interfaces.iteritems():
if 'enp' in interface:
grains['internal_interfaces'].append(interface)
return grains

I created a custom grains directory under my file_roots and added internal_interfaces.py.

To distribute the custom grain to my minions:

sudo salt \* saltutil.sync_grains

Now I can call my new grain, “internal_interfaces” and get the following:

admin@saltmaster:~$ sudo salt "ids-server" grains.get internal_interfaces
ids-server:
- enp24s0f3
- enp24s0f2
- enp24s0f1
- enp24s0f0

In suricata.yaml, I can now use jinja templating to fill in the names of my interfaces:

# Linux high speed capture support
af-packet:
- interface: {{ grains['internal_interfaces'][0] }}
threads: 1
defrag: yes
cluster-type: cluster_flow
cluster-id: 98
buffer-size: 64535
use-mmap: yes
- interface: {{ grains['internal_interfaces'][1] }}
threads: 1
defrag: yes
cluster-type: cluster_flow
cluster-id: 97
buffer-size: 64535
use-mmap: yes
- interface: {{ grains['internal_interfaces'][2] }}
threads: 1
defrag: yes
cluster-type: cluster_flow
cluster-id: 96
buffer-size: 64535
use-mmap: yes
- interface: {{ grains['internal_interfaces'][3] }}
threads: 1
cluster-id: 95
defrag: yes
cluster-type: cluster_flow
buffer-size: 64535
use-mmap: yes

Escaping Jinja Errors

I was originally getting the following error: SaltRenderError: Jinja syntax error: Encountered unknown tag ‘D’, caused by the following line in suricata.yaml:

#customformat: "%{%D-%H:%M:%S}t.%z %{X-Forwarded-For}i %H %m %h %u %s %B %a:%p -> %A:%P"

Even though this line is commented out, jinja still threw a fit. To avoid such errors, you can escape any jinja or jinja-like syntax that you want Salt to ignore.

--

--

Kia Matthews

InfoSec practitioner. I don't know anything but I'm trying!