Auditer la configuration d'un switch Cisco (python+regexp) 🛠

Bon Dieu! Si vous connaissiez le nombre d'entreprises qui n'ont absolument rien à foutre de leur configuration d'équipement réseau (exemple: switch Cisco)... 😢

Pourtant il est plus que nécessaire de prendre le temps, de temps en temps de les auditer. Faut avouer que ça peut être très chiant, pour palier à ça, on peut très bien le scripter.

Cet article ne va pas expliquer comment auditer de A à Z une configuration avec Python mais plutôt vous donner des petits tips.

Je vais ainsi me concentrer sur les expressions rationnelles (REGEX) pour analyser la configuration des interfaces.

En effet, le plus courrament, nous allons chercher à auditer ces interfaces. C'est quelque chose qui peut être assez compliqué à réaliser, les expressions rationnelles sont là pour nous aider (enfin vite fait). 😒

Un fichier d'exemple

Et oui, je ne vais pas vous donner d'astuce sans vous présenter au moins un exemple de configuration à analyser, bien sûr, celle-ci est totalement factice.

interface Vlan1
 no ip address
 shutdown
!
interface Vlan10
 ip address 10.0.0.1 255.2555.255.0
 no ip route-cache
!
interface Vlan20
 no ip address
!
interface GigabitEthernet1/0/5
 description PC-05
 switchport access vlan 20
 switchport mode access
 switchport nonegotiate
 shutdown
 spanning-tree portfast
 spanning-tree bpduguard enable
!
interface GigabitEthernet1/0/6
 description PC-06
 switchport access vlan 10
 switchport mode access
 switchport nonegotiate
 switchport port-security maximum 2
 switchport port-security aging time 2
 switchport port-security violation restrict
 switchport port-security mac-address aaaa.aaaa.aaaa vlan access
 switchport port-security mac-address bbbb.bbbb.bbbb vlan access
 spanning-tree portfast
 spanning-tree bpduguard enable
!
end
no ip http server

Effectivement, j'en vois certains venir, la configuration ne va pas casser 3 pattes à un canard mais c'est que pour l'exemple!

Vérifier qu'une interface contient/ne contient pas une configuration donnée

Pour l'exemple, on va vérifier le nom des interfaces contenant port-security.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re

def main():
    f = open("config.txt")
    file = f.read()
    
    # Stockage de la REGEX dans une variable
    pattern = '(?sm)interface (\S*)(?:(?!^!$).)*port-security.*?!$'
    result = re.findall(pattern, file)

    for r in result:
        print(r)

if __name__ == "__main__":
    main()

Le résultat retourné est donc GigabitEthernet1/0/6.

Pratique, n'est-ce pas! Vous pouvez obtenir une information détaillée en cliquant sur le lien suivant: explication.

Maintenant on va faire l'inverse, c'est-à-dire, lister toutes les interfaces qui ne contiennent pas la configuration port-security.

Le principe reste donc le même, seule la REGEXP change.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re

def main():
    f = open("config.txt")
    file = f.read()
    
    # Stockage de la REGEX dans une variable
    pattern = '(?sm)interface (\S*)(?:(?!port-security).)*?!$'
    result = re.findall(pattern, file)

    for r in result:
    	# Bypass des interfaces type Vlan
        pattern = 'Vlan.*'
        if re.match(pattern, r):
            pass
        else:
            print(r)

if __name__ == "__main__":
    main()

J'ai ajouté un condition pour éviter que les interfaces type Vlan ne ressortent au test. Ainsi j'obtiens le retour GigabitEthernet1/0/5 qui est effectivement, l'interface qui ne détient par de switchport port-security.

Alors bien sûr on pourrait aller encore plus loin, ce n'est pas le but de cet article.

Vérifier que notre switch contient telle ou telle configuration

Comme vous le savez sûrement, par exemple, il n'est pas très utile d'avoir l'interface web d'active sur celui-ci, ce qui d'ailleurs consitue facilement un vulnérabilité sur notre équipement (de l'exposer).

Ce pour quoi il est conseillé de la désactiver. On peut donc ainsi le vérifier de la manière suivante, vous verrez que la REGEXP est bien plus simple cette fois-ci!

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import re

def main():
    f = open("config.txt")
    file = f.read()

    pattern = '^ip http server'
    if re.match(pattern, file):
        print("match")
    else:
        print("not match")

if __name__ == "__main__":
    main()

Ça nous retourne bien not match puisque ip http server est précédé de no dans notre configuration d'exemple.

A savoir que ces méthodes sont adaptables sur des fichiers déjà sauvegardés par Rancid, Oxidized ou autre! 😁
Maintenant, vous n'avez plus d'excuse pour auditer vos configurations réseau.