SNMP
Sources:
https://makina-corpus.com/blog/metier/2016/initiation-a-snmp-avec-python-pysnmp
Introduction à SNMP (Simple Network Management Protocol)
Protocole de gestion de réseau, il permet de superviser l'état des équipements d'un parc, qu'ils soient réseaux, des serveurs, etc. Par supervision on entend : obtenir des informations sur l'état de ces machines. Cependant SNMP permet aussi d'effectuer des actions de maintenance comme redémarrer un service, une machine, nettoyer les têtes de lecture d'une imprimante, etc.
Noeuds
SNMP se base sur l'idée qu'un système de supervision se base sur:
- Des noeuds à administrer (Managed Nodes) contenant chacun un agent
- Au moins une station d'administration, le manager (Network Management Station)
- D'un protocole permettant d'échanger entre les agent et le NMS : SNMP
Une présentation très complète : https://en.wikipedia.org/wiki/Simple_Network_Management_Protocol
Fonctionnement
Comment ça marche?
- Le manager envoie des requêtes GET aux agents pour récupérer une information
- Il peut aussi envoyer des SET pour modifier l'état de services et/ou de machines gérés par l'agent
- Les agents peuvent envoyer des requêtes de type TRAP au manager pour signaler un problème
- Les agents peuvent être regroupés autour d'un agent maître
- Les informations accessibles sont fournies au travers d'une base "virtuelle" appellée MIB (Management Info Base) qui est normalisée.
Pourquoi l'utiliser ?
- Parce que ça marche...
- ... sur à peu près tout et n'importe quoi
- Sécurisé depuis la version 2
- Il est simple et robuste, s'appuie sur UDP ou TCP
- Les MIBs sont normalisées...
Défauts?
- ... mais pas toujours très lisibles
- L'implémentation peut être complexe
- Certains concurrents voient le jour comme WMI
Les MIBs
C'est une base de données "virtuelle" qui organise de manière hiérarchique les informations fournies par les agents sur les équipements. Ce n'est pas une BDD au sens de base de données relationelle, elle n'existe que au travers de l'agent qui peut l'implémenter comme il le souhaite. Une MIB décrit comment numéroter/accéder à l'information.
C'est l'agent qui gère cette information, et elle souvent volatile.
Il faut bien comprendre que d'une part, c'est l'agent qui fait tout le boulot, et d'autre part, que l'information qu'on lui demande est récupérée quand on lui demande. La MIB ne contient pas toutes les infos à l'avance (ça n'aurait pas de sens). Si j'installe un agent, dans un premier temps, il n'aura aucune information. Si à ce moment-là je lui demande son uptime, c'est lui qui le demandera à l'OS, avant de me le renvoyer. Il ne stocke pas cet uptime, c'est une information volatile, la stocker n'as pas d'intérêt. La plupart des informations issues des MIBs sont de cette nature.
Il existe plein de MIBs, selon le contructeur, le matériel, etc. Certains constructeurs ont donc leur propre MIB définissant une branche dans l'arbre des OID avec un numéro officiel défini auprès de l'IANA.
MIB-II
Une des MIB les plus connues est la MIB-II, décrite par la RFC 1213.
Elle est mise en œuvre dans quasiment tous les équipements TCP/IP.
Elle compte dix groupes :
- system
- interfaces
- Address Translation
- IP
- ICMP
- TCP
- UDP
- EGP
- transmission
- et SNMP
C'est à dire que toute information que vous pourriez demander concernant la MIB-II sera classée dans l'un de ces groupes.
Des exemples d'informations que vous pourriez retrouver dans ces ensembles, sont:
- la liste des processus (avec informations mémoire et CPU) ;
- les débits réseaux ;
- le load average ;
- l'utilisation mémoire ;
- le contact du système, l'uptime ;
- l'utilisation de l'espace disque ;
- les tables de routages.
Exemples de description d'une MIB
Les MIBs sont écrites dans le langage "ASN.1". Exemple d'une description d'interface réseau en ASN.1 :
ifDescr OBJECT-TYPE SYNTAX DisplayString (SIZE (0..255)) ACCESS read-only STATUS mandatory DESCRIPTION "A textual string containing information about the interface. This string should include the name of the manufacturer, the product name and the version of the hardware interface." ::= { ifEntry 2 } #Son emplacement dans le noeud parent ifEntry : 2e élement du noeud
Organisation de l'info dans les MIBs
Les MIBs sont organisée de façon arborescente. Graphe représentant la hiérarchie principale de toutes les MIBs:
https://makina-corpus.com/blog/metier/2016/pysnmp/snmp_mib.png
Le graphe ci-dessus présente la structure hiérarchique des MIBs.
Sous le nœud racine, noté « . » se trouvent 3 informations numérotées :
CCITT avec le numéro 0
ISO avec le numéro 1
ISO – CCITT avec le numéro 2
Sous le nœud ISO se trouvent 4 informations, elles aussi numérotées :
standard, avec le numéro 0
registration authority, avec le numéro 1
member body, avec le numéro 2
organization, avec le numéro 3
Le chemin d'accès à l'information « organization » située sous « ISO », elle-même située sous la racine sera « .1.3 » ou encore « .ISO.organization »
Et ainsi de suite.
Par exemple l'accès à l'information « MIB-2 » se fera via le chemin « .1.3.6.1.2.1 » ou encore « .ISO.organization.DoD.Internet.management.MIB-2 »
Et voilà, vous savez tout sur les MIBs : elles définissent des informations en ASN.1, lesquelles sont accessibles via un chemin exprimé avec des numéros ou les noms des informations parentes.
Les OID
Les informations sont donc numérotées selon leur place dans la MIB, ces numéros sont appellé OID (Object IDentifier). Quleques exemples :
- .1.3.6.1.2.1.1 system : uptime, contact, nom
- .1.3.6.1.2.1.2 interfaces: interfaces réseau
- .1.3.6.1.2.1.4 ip: ip routing, etc
- .1.3.6.1.2.1.5 icmp: icmp errors, discards... (icmp c'est par exemple le ping)
- .1.3.6.1.2.1.6/7 tcp ou udp: états des connexions tcp ou udp
Parfois écrites dans le style :
- sysUpTime.0
- Le 0 est comme le . dans le système de fichier UNIX
Comment les trouver, du coup?
- Avec les normes et standards : https://tools.ietf.org/html/rfc1213
- Avec des requêtes SNMP qui permettent de parcourir l'arborescence
- En les parcourant via des sites internet qui les représentent facilement
Types de requêtes
Les types de requêtes que le protocole propose :
- Get: Demande de la valeur d'un OID par le manager à l'agent
- L'agent retourne l'information via un message de type Response
- GetNext : Demande de l'information suivante dans la MIB
- Permet de parcourir une MIB sans connaître ce qu'elle contient
- Set : Modifie la valeur associée à un OID
- Permet de changer la configuration ou modifier l'état d'un hôte
- GetBulk : Demande tout un ensemble d'infos en une seule requête.
- Evite de multiplier les Get et GetNext
- Response : message envoyé par l'agent au manager contenant l'information demandée
- Trap : Message envoyé par l'agent au manager pour signaler un problème
- Inform : Message envoyé par le manager pour confirmer la réception du trap
Mise en place et utilisation en ligne de commandes
Nous allons ici utiliser SNMP en ligne de commandes pour bien voir son fonctionnement. L'agent sera une VM Debian 9 (192.168.1.19) et le manager un PC sous Ubuntu 18.10.
Install
Sur le manager :
sudo apt-get update sudo apt-get install snmp snmp-mibs-downloader #Contient des infos propriétaires sur des MIBs standard
Sur l'agent:
sudo apt-get update sudo apt-get install snmpd
Config du manager
Pas grand-chose à faire ici, vu que le gros du travail se passe sur l'agent. On va juste accéder à /etc/snmp/snmp.conf et commenter la ligne:
#mibs:
Cela permettra au manager d'importer les MIBs.
Notre manager est configuré, mais on se servira quand même de lui pour configurer notre agent.
Config de l'agent
En tant que partie d'un vrai système client-serveur, notre agent n'as lui-même pas tellement d'outils pour se configurer. On peut toucher à quelques fichiers, mais la plupart des changements passeront par le manager.
Sur l'agent, on va ouvrir le fichier de config du daemon /etc/snmp/snmpd.conf.
D'abord, on va changer agentAddress; en l'état il n'accepte que les connexions venant de 127.0.0.1. On va commenter agentAddres 127.0..0.1 et décommenter la ligne agentAddress du dessous :
# Listen for connections from the local system only #agentAddress udp:127.0.0.1:161 # Listen for connections on all interfaces (both IPv4 *and* IPv6) agentAddress udp:161,udp6:[::1]:161
Ensuite, on va insérer une ligne createUser temporaire. Ces directives ne vont pas là normalement, mais on va l'enlever après.
L'utilisateur s'appellera bootstrap et servira de base pour créer notre premier vrai utilisateur. En effet, les packages SNMP font cela en clonant les utilisateurs. On doit définir son type d'auth (MD5 ou SHA) et une passphrase de 8 caractères. Si on utilise comme ici du chiffrement, on donne aussi le protocole (DES ou AES) et éventuellement un password de chiffrement (si on ne le donne pas, ce sera le même que pour l'auth).
Ajoutons donc la ligne suivante :
createUser bootstrap MD5 temp_password DES
Nous allons ensuite définir le niveau d'accès de bootstrap, ainsi que celui de demo (qui sera notre utilisateur définitif). On va leur donner un accès r/w avec rwuser (on utiliserait rouser pour du Read-Only). On ajoutera aussi priv pour forcer l'utilisateur à chiffrer. SI on voulait restreindre l'utilisateur, on pourrait donner l'OID de plus haut niveau qu'il puisse utiliser. Ici, nos deux lignes ressemblent à ça :
rwuser bootstrap priv rwuser demo priv
Il n'y a plus qu'à redémarrer le daemon :
sudo systemctl restart snmpd
On peut désormais se connecter depuis le manager pour créer notre utilisateur. Pour cela on utilisera l'outil snmpusm (user management). Il ne nous faudra que l'iP de l'agent.
Mon fichier /etc/snmp/snmpd.conf à ce stade:
# Listen for connections from the local system only #agentAddress udp:127.0.0.1:161 # Listen for connections on all interfaces (both IPv4 *and* IPv6) agentAddress udp:161,udp6:[::1]:161 #Creating temporary agent createUser bootstrap MD5 Prevert77 DES rwuser bootstrap priv rwuser demo priv
Structure des commandes SNMP
Toutes les commandes du package snmp (la suite net-snmp) suivent à peu près le même pattern.
La première chose à faire sera de s'authentifier auprès du damon avec qui on veut communiquer, ce qui demande quelques informations, voici les plus communes :
- v VERSION: This flag is used to specify the version of the SNMP protocol that you would like to use. We will be using v3 in this guide.
- -c COMMUNITY: This flag is used if you are using SNMP v1 or v2-style community strings for authentication. Since we are using v3-style user-based authentication, we will not be needing this.
- -u USER-NAME: This parameter is used to specify the username that you wish to authenticate as. To read or modify anything using SNMP, you must authenticate with a known username.
- -l LEVEL: This is used to specify the security level that you are connecting with. The possible values are noAuthNoPriv for no authentication and no encryption, authNoPriv for authentication but no encryption, and authPriv for authentication and encryption. The username that you are using must be configured to operate at the security level you specify, or else the authentication will not succeed.
- -a PROTOCOL: This parameter is used to specify the authentication protocol that is used. The possible values are MD5 or SHA. This must match the information that was specified when the user was created.
- -x PROTOCOL: This parameter is used to specify the encryption protocol that is used. The possible values are DES or AES. This must match the information that was specified when the user was created. This is necessary whenever the user's privilege specification has priv after it, making encryption mandatory.
- -A PASSPHRASE: This is used to give the authentication passphrase that was specified when the user was created.
- -X PASSPHRASE: This is the encryption passphrase that was specified when the user was created. If none was specified but an encryption algorithm was given, the authentication passphrase will be used. This is required when the -x parameter is given or whenever a user's privilege specification has a priv after it, requiring encryption.
Connexion au daemon et création de l'utilisateur
À partir d'ici, sauf indication contraire, j'agis depuis le manager.
Vu notre utilisateur bootstrap, nos commandes utilisées via son compte ressembleront à ça :
snmp_command -u bootstrap -l authPriv -a MD5 -x DES -A temp_password -X temp_password IPofremote_host snmp_sub_command_or_options
Je peux vérifier que mon utilisateur bootstrap est dispo, avec cette commande :
snmpget -u bootstrap -l authpriv -a MD5 -x DES -A Prevert77 -X Prevert77 192.168.1.19 1.3.6.1.2.1.1.1.0
l'OID est celui de sysDescr, et doit renvoyer l'équivalent d'un uname -a.
Création de l'utilisateur demo
On va utiliser snmpusm, voici sa syntaxe globale:
snmpusm authentication_info remote_host create new_user existing_user
Pour créer mon utilisateur demo à partir de bootstrap :
snmpusm -u bootstrap -l authpriv -a MD5 -x DES -A Prevert77 -X Prevert77 192.168.1.19 create demo bootstrap
Qui doit renvoyer :
User successfully created.
On a notre utilisateur demo. On va pouvoir changer son mot de passe (8 caractères ou +!). Cette fois-ci, on se connecte avec demo.
snmpusm -u demo -l authPriv -a MD5 -x DES -A Prevert77 -X Prevert77 192.168.1.19 passwd Prevert77 MonNouveauMdp
Qui renvoie :
SNMPv3 Key(s) successfully changed.
On peut tester notre nouveau mdp en demandant à l'agent son l'uptime du daemon snmp :
snmpget -u demo -l authPriv -a MD5 -x DES -A Prevert77 -X Prevert77 sysUpTime.0
Qui doit renvoyer un uptime.
Création d'un fichier de configuration client
Pour éviter de se taper le mot de passe et les méthode d'auth à chaque commande, on peut les spécifier dans une fichier de configuration du manager. Il y'a deux méthodes : soit on peut partager ces informations avec quiconque utilise la machine manager, soit on veut les garder rien que pour notre utilisateur UNIX.
- Première méthode : Placer les détails dans le fichier /etc/snmp/snmp.conf
- Seconde méthode : créer un dossier .snmp dans notre ~ et placer ça ici.
mkdir ~/.snmp
cd ~/.snmp
nano snmp.conf
Quoi qu'il en soit on écrira la même chose. Le tableau suivant donne les détails, avec à gauche la commande snmp, et à droite la directive à mettre dans le fichier de conf correspondante.
-u USERNAME | The SNMPv3 username to authenticate as. | defSecurityName USERNAME |
-l authPriv | The security level to authenticate with. | defSecurityLevel authPriv |
-a MD5 | The authentication protocol to use. | defAuthType MD5 |
-x DES | The privacy (encryption) protocol to use. | defPrivType DES |
-A PASSPHRASE | The authentication passphrase for the supplied username. | defAuthPassphrase PASSPHRASE |
-X PASSPHRASE | The privacy passphrase fro the supplied username. |
defPrivPassphrase PASSPHRASE |
Dans mon fichier, ça donne :
#User called "demo" defSecurityName demo defSecurityLevel authPriv defAuthType MD5 defPrivType DES defAuthPassphrase MonNouveauMdp defPrivPassphrase MonNouveauMdp
Grâce à ça, je pourrais taper directement mes commandes snmp :
snmpget 192.168.1.19 sysUpTime.0
Enlever le compte bootstrap :
Sur l'agent, on peut déjà commenter / enlever ce que l'on avait mis sur l'agent bootstrap :
# Listen for connections from the local system only #agentAddress udp:127.0.0.1:161 # Listen for connections on all interfaces (both IPv4 *and* IPv6) agentAddress udp:161,udp6:[::1]:161 #Creating temporary agent #createUser bootstrap MD5 Prevert77 DES #rwuser bootstrap priv rwuser demo priv
Pour le supprimer définitivement :
snmpusm 192.168.1.19 delete bootstrap
Traps
En dehors de la façon traditionnelle dont fonctionne SNMP (j'interroge un serveur via SNMP pour savoir comment il va) appellée requêtes actives, il existe les traps, qui sont des messages spontanément envoyés par le serveur sur des adresses préconfigurées, sur le port UDP 162. Les traps ont des inconvénients par rapport au requêtes actives :
- Ils ne sont pas fiable. UDP peut perdre des paquets. Et on ne saura jamais alors qu'il y'a eu un problème.
- L'envoi se fait surtout sur les erreurs, et par les récupérations d'erreurs. Le statut de la machine n'est alors pas clair.
- Si des milliers de switches se mettent à envoyer des traps, c'est le bazar et on est surchargée.
- Il faut reconfigurer tout les serveurs émetteurs de trap si le serveur de monitoring change d'IP.
- Les traps sont durs à tester.