Ansible - SW Cisco / Junip
Sommaire
Administrer switch Cisco / Junip avec Ansible[modifier]
Cette page traite de l'installation et la configuration d'Ansible pour gérer la conf des switch Cisco et Juniper.
Prérequis[modifier]
Afin de mieux gérer le versionning de chacun des paquets, nous utiliserons uniquement des paquets Python en lieu et place des paquets fournis par notre distribution.
Environnement Python[modifier]
Installer un environnement Python sur sa machine :
Gestionnaire de paquets Python[modifier]
Installer un package manager pour Python :
Paquets Python[modifier]
Installation des paquets[modifier]
Installer Ansible et la librairie paramiko permettant de gérer la connexion SSH avec Ansible :
$ pip install --user ansible $ pip install --user paramiko==2.8.1
Bugs identifiés[modifier]
- À compter de la version 2.8.2 de paramiko, ce dernier ne fonctionne pas si l'on possède plusieurs clés SSH sur sa machine
- La librairie pylibssh censée remplacer paramiko ne fonctionne pas avec la collection cisco.ios
Version des paquets Python[modifier]
Les versions suivantes ont été testées et sont fonctionnelles :
- ansible - 6.7.0
- ansible-compat - 2.2.7
- ansible-core - 2.13.7
- paramiko - 2.8.1
Switchs Cisco[modifier]
Ajouter sa clé SSH sur le switch[modifier]
Affichage de la clé publique :
$ cat /path/to/ssh/public/key
La clé s'affiche sur une seule ligne. Or, Cisco IOS supporte seulement 254 caractères. Afin de rendre la clé lisible auprès de Cisco IOS, utiliser la commande ci-après pour découper l'affichage en lignes de 100 caractères et enlever le premier champ "ssh-rsa" et le commentaire de fin de fichier.
$ cat /path/to/ssh/public/key | cut -d " " -f 2 | fold -b -w 100 ... ... ... ...
Ajouter la clé publique sur le switch (les ... correspondent au résultat de la commande effectuée précédemment) :
(config)#ip ssh pubkey-chain (conf-ssh-pubkey)#username user.name (conf-ssh-pubkey-user)#key-string (conf-ssh-pubkey-data)#... (conf-ssh-pubkey-data)#... (conf-ssh-pubkey-data)#... (conf-ssh-pubkey-data)#... (conf-ssh-pubkey-data)#exit
Collections Ansible[modifier]
Installer la collection cisco.ios :
$ ansible-galaxy collection install cisco.ios
Vérifier que l'on dispose bien des collections suivantes :
$ ansible-galaxy collection list Collection Version --------------------- ------- ansible.netcommon 4.1.0 ansible.utils 2.6.1 cisco.ios 3.2.0 community.network 5.0.0
Inventaire d'hôtes[modifier]
Créer un fichier hosts contenant l'inventaire de l'ensemble des switchs ainsi que la définition de groupes de switchs et de variables.
[all:vars] host_key_checking=false ansible_ssh_user='user.name' ansible_python_interpreter='/path/to/python/interpreter' ansible_ssh_private_key_file='/path/to/ssh/private/key' timeout=300 ansible_command_timeout=300 [DR13_cisco:vars] ansible_connection=ansible.netcommon.network_cli ansible_network_os=cisco.ios.ios [DR13_cisco] sw-DR-BatA-1 ansible_host=10.13.105.101 sw-DR-BatA-2 ansible_host=10.13.105.102 sw-DR-BatA-3 ansible_host=10.13.105.103 sw-DR-BatA-4 ansible_host=10.13.105.104 sw-DR-BatA-5 ansible_host=10.13.105.105 sw-DR-BatA-6 ansible_host=10.13.105.106 sw-DR-BatA-7 ansible_host=10.13.105.107 sw-DR-BatB-1 ansible_host=10.13.105.151 sw-DR-BatD-1 ansible_host=10.13.105.171 sw-DR-BatD-2 ansible_host=10.13.105.172 sw-DR-BatD-3 ansible_host=10.13.105.173 sw-DR-BatD-4 ansible_host=10.13.105.174 [DR13_BAT_A] sw-DR-BatA-1 sw-DR-BatA-2 sw-DR-BatA-3 sw-DR-BatA-4 sw-DR-BatA-5 sw-DR-BatA-6 sw-DR-BatA-7 [DR13_BAT_B] sw-DR-BatB-1 [DR13_BAT_D] sw-DR-BatD-1 sw-DR-BatD-2 sw-DR-BatD-3 sw-DR-BatD-4 [DR13_24_ports] sw-DR-BatA-7 sw-DR-BatB-1 sw-DR-BatD-4 [DR13_48_ports] sw-DR-BatA-1 sw-DR-BatA-2 sw-DR-BatA-3 sw-DR-BatA-4 sw-DR-BatA-5 sw-DR-BatA-6 sw-DR-BatD-1 sw-DR-BatD-2 sw-DR-BatD-3
Modules Cisco.Ios[modifier]
Il existe un total de 36 modules pour configurer des appareils Cisco utilisant IOS qui se décomposent en 3 catégories :
- 1 module exécutant des commandes IOS en dehors du mode configuration (utilise la syntaxe IOS)
- 1 module exécutant des commandes IOS en mode configuration (utilise la syntaxe IOS)
- 34 modules configurant de manière ciblée des éléments spécifiques du switch (VLANs, interfaces, utilisateurs). Ils n'utilisent pas la syntaxe IOS.
Exemples de modules pour chaque catégorie :
- cisco.ios.ios_command
- cisco.ios.ios_config
- cisco.ios.ios_l2_interfaces
Exemples de playbook[modifier]
Avec syntaxe IOS[modifier]
Le playbook ci-dessous configure le NAC sur les ports d'un switch Cisco. De plus, certains switchs possèdent 24 ports et non 48. Les commandes seront donc appliquées selon le nombre de ports dont dispose un switch.
Dans ce cas de figure, l'utilisation de commandes Cisco IOS est plus adaptée, car le module cisco.ios.ios_l2_interfaces spécifique à la configuration d'interfaces ne prend, par exemple, pas en charge les commandes "authentication".
- name: "Configuration"
hosts: DR13_cisco
gather_facts: false
tasks:
- name: Configure NAC interfaces on 48 ports switches
cisco.ios.ios_config:
lines:
- no shut
- description NAC
- switchport mode access
- authentication host-mode single-host
- authentication order dot1x
- authentication port-control auto
- authentication periodic
- authentication timer reauthenticate 1200
- authentication event server dead action authorize vlan 115
- no mab
- dot1x pae authenticator
- dot1x timeout tx-period 10
- no switchport access vlan
- spanning-tree portfast
- device-tracking attach-policy TRACKING-DR13
- authentication event no-response action authorize vlan 115
parents:
- interface range GigabitEthernet1/0/1-48
when:
- inventory_hostname in groups.DR13_48_ports
- name: Configure NAC interfaces on 24 ports switches
cisco.ios.ios_config:
lines:
- no shut
- description NAC
- switchport mode access
- authentication host-mode single-host
- authentication order dot1x
- authentication port-control auto
- authentication periodic
- authentication timer reauthenticate 1200
- authentication event server dead action authorize vlan 115
- no mab
- dot1x pae authenticator
- dot1x timeout tx-period 10
- no switchport access vlan
- spanning-tree portfast
- device-tracking attach-policy TRACKING-DR13
- authentication event no-response action authorize vlan 115
parents:
- interface range GigabitEthernet1/0/1-24
when:
- inventory_hostname in groups.DR13_24_ports
- name: Save running to startup when modified
cisco.ios.ios_config:
save_when: modified
Sans syntaxe IOS[modifier]
Le playbook ci-dessous ajoute les VLANs 10 et 11 sur tous les switch et les ajoute aux interfaces disposant d'un trunk. De plus, les switchs du bâtiment A ayant 2 vlans supplémentaires déclarés au niveau des ports trunk, la directive allowed_vlans sera donc appliquée différemment en fonction du bâtiment où se situe le switch.
Dans ce cas de figure, l'utilisation de modules pour configurer uniquement des VLANs et interfaces est possible mais le module cisco.ios.ios_config aurait tout à fait pu lui aussi être utilisé.
- name: "Configuration VLAN"
hosts: DR13_cisco
gather_facts: false
tasks:
- name: Add VLANs 10 and 11
cisco.ios.ios_vlans:
config:
- name: DR-toto
vlan_id: 10
state: active
shutdown: disabled
- name: DR-tata
vlan_id: 11
state: active
shutdown: disabled
- name: Add VLANs to BAT_A's 10G interfaces trunks
cisco.ios.ios_l2_interfaces:
config:
- name: TenGigabitEthernet1/1/3
mode: trunk
trunk:
allowed_vlans: X,10,11,X-X,X,Y,Z
- name: TenGigabitEthernet1/1/4
mode: trunk
trunk:
allowed_vlans: X,10,11,X-X,X,Y,Z
when:
- inventory_hostname in groups.DR13_BAT_A
- name: Add VLANs to BAT_B and BAT_D's 10G interfaces trunks
cisco.ios.ios_l2_interfaces:
config:
- name: TenGigabitEthernet1/1/3
mode: trunk
trunk:
allowed_vlans: X,10,11,X-X,X
- name: TenGigabitEthernet1/1/4
mode: trunk
trunk:
allowed_vlans: X,10,11,X-X,X
when:
- inventory_hostname in lookup('inventory_hostnames','DR13_BAT_B:DR13_BAT_D')
Switchs Juniper[modifier]
Paquets Python additionnels[modifier]
Les switchs Juniper nécessitent l'installation des paquets Python ci-après :
- jxmlease - 1.0.3
- lxml - 4.9.2
- ncclient - 0.6.13
$ pip install --user jxmlease lxml ncclient
Ajouter sa clé SSH sur le switch[modifier]
Afficher le contenu de sa clé publique :
$ cat /path/to/ssh/public/key ssh-rsa ... user.name@myhost
Associer la clé à un utilisateur sur le switch :
[edit system login user user.name authentication] user.name@host# set ssh-rsa "ssh-rsa ... user.name@myhost" user.name@host# commit
Collections Ansible[modifier]
Installer la collection junipernetworks.junos :
$ ansible-galaxy collection install junipernetworks.junos
Vérifier que l'on dispose bien des collections suivantes :
$ ansible-galaxy collection list Collection Version --------------------- ------- ansible.netcommon 4.1.0 ansible.utils 2.6.1 community.network 5.0.0 junipernetworks.junos 4.1.0
Inventaire d'hôtes[modifier]
Créer le fichier hosts contenant l'inventaire de l'ensemble des switchs ainsi que la définition de groupes de switchs et de variables.
[all:vars] host_key_checking=false ansible_ssh_user='user.name' ansible_python_interpreter='/path/to/python/interpreter' ansible_ssh_private_key_file='/path/to/ssh/private/key' timeout=300 ansible_command_timeout=300 [DR13_Junip:vars] ansible_connection=ansible.netcommon.netconf ansible_network_os=junipernetworks.junos.junos ansible_port=22 [DR13_Junip] sw-DR-BatB-1er ansible_host=10.13.105.152
Activation de NETCONF[modifier]
La configuration des switchs Juniper s'effectue via NETCONF pour tous les modules Ansible excepté junos_netconf. Il s'agit donc du mode à privilégier.
Activation de NETCONF sur le switch Juniper :
[edit system services] user@host# set netconf ssh user@host# set ssh user@host# commit
Vérifier le bon fonctionnement de NETCONF en initiant une connexion SSH (OK si le mot de passe de la clé SSH est demandé) :
$ ssh user.name@host -p 22 -s netconf
Modules Junipernetworks.Junos[modifier]
Il existe un total de 40 modules pour configurer des appareils Juniper sous JUNOS qui se décomposent en 3 catégories :
- 1 module exécutant des commandes JUNOS en dehors du mode configuration (utilise la syntaxe JUNOS)
- 1 module exécutant des commandes JUNOS en mode configuration (utilise la syntaxe JUNOS)
- 38 modules configurant de manière ciblée des éléments spécifiques du switch (VLANs, interfaces, utilisateurs). Ils n'utilisent pas la syntaxe JUNOS.
Exemples de modules pour chaque catégorie :
- junipernetworks.junos.junos_command
- junipernetworks.junos.junos_config
- junipernetworks.junos.junos_l2_interfaces
Exemples de playbook[modifier]
Avec syntaxe JUNOS[modifier]
Le playbook ci-dessous configure le NAC sur les ports d'un switch Juniper. Dans ce cas de figure, l'utilisation de commandes JUNOS est plus adaptée, car il n'existe pas de module spécifique permettant de configurer le protocole 802.1X.
- name: "Configuration NAC"
hosts: DR13_Junip
gather_facts: false
tasks:
- name: Configure NAC on all interfaces
junipernetworks.junos.junos_config:
lines:
- set protocols dot1x authenticator interface all supplicant single
- set protocols dot1x authenticator interface all reauthentication interval 1200
- set protocols dot1x authenticator interface all server-timeout 5
- set protocols dot1x authenticator interface all transmit-period 5
- set protocols dot1x authenticator interface all maximum-requests 3
- set protocols dot1x authenticator interface all retries 3
- set protocols dot1x authenticator interface all quiet-period 300
- set protocols dot1x authenticator interface all server-fail vlan-name dr-pc
- name: Edit description on all interfaces
junipernetworks.junos.junos_config:
lines:
- set interfaces ge-0/0/0 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/1 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/2 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/3 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/4 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/5 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/6 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/7 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/8 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/9 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/10 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/11 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/12 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/13 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/14 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/15 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/16 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/17 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/18 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/19 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/20 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/21 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/22 description "NAC: NAC DR13" unit 0 family ethernet-switching
- set interfaces ge-0/0/23 description "NAC: NAC DR13" unit 0 family ethernet-switching
- name: Check correctness of commit
junipernetworks.junos.junos_config:
check_commit: true
- name: Confirm a previous commit
junipernetworks.junos.junos_config:
confirm_commit: true
Sans syntaxe JUNOS[modifier]
Le playbook ci-dessous ajoute les VLANs 10 et 11 sur tous les switchs et les ajoute aux interfaces disposant d'un trunk. Dans ce cas de figure, l'utilisation de modules pour configurer uniquement des VLANs et interfaces est possible mais le module junipernetworks.junos.junos_config aurait tout à fait pu lui aussi être utilisé.
- name: "Configuration VLAN"
hosts: DR13_Junip
gather_facts: false
tasks:
- name: Gather running vlans configuration
junipernetworks.junos.junos_vlans:
state: gathered
- name: Add VLANs 10 and 11
junipernetworks.junos.junos_vlans:
config:
- name: dr-toto
vlan_id: 10
- name: dr-tata
vlan_id: 11
state: replaced
- name: Add VLANs to interface with trunk
junipernetworks.junos.junos_l2_interfaces:
config:
- name: ge-0/1/3
trunk:
allowed_vlans:
- dr-toto
- dr-tata
state: merged
Utiliser un playbook[modifier]
Faciliter l'écriture d'un playbook[modifier]
Installation d'Ansible-Lint[modifier]
Il est possible d'utiliser le linter Ansible-Lint qui permet de vérifier la validité d'un playbook en identifiant d'éventuelles erreurs dans l'écriture de ce dernier.
$ pip install --user ansible-lint
Version testée et fonctionnelle :
- ansible-lint - 6.11.0
Utilisation d'Ansible-Lint[modifier]
L'exemple ci-dessous montre qu'Ansible-Lint a identifié une erreur d'indentation ainsi qu'une erreur de syntaxe au sein du playbook.
$ ansible-lint -p /path/to/playbook_name.yaml
playbook_name.yaml:45: yaml[indentation]: Wrong indentation: expected 8 but found 7
playbook_name.yaml:48:7: syntax-check[specific]: couldn't resolve module/action 'junipernetorks.junos.junos_config'. This often indicates a misspelling, missing collection, or incorrect module path.
Rule Violation Summary
count tag profile rule associated tags
1 yaml[indentation] basic formatting, yaml
1 syntax-check[specific] min core
Failed after min profile: 2 failure(s), 0 warning(s) on 1 files.
Intégration d'Ansible avec un IDE[modifier]
Possibilité d'aller plus loin en installant une extension pour un IDE facilitant l'écriture de playbooks (auto-complétion, coloration syntaxique, ...)
Exemple pour vscode/vscodium : extension Ansible
Exécution d'un playbook[modifier]
Entrer la commande suivante afin d'exécuter un playbook (l'option -k permet d'avoir un prompt pour renseigner le mot de passe de notre clé SSH).
$ ansible-playbook -i /path/to/hosts/file /path/to/playbook_name.yaml -k