Tâches Ansible Ad-Hoc Linux

Ce chapitre envisage la prise de connaissance et la mise en oeuvre de tâches Ansible Ad-Hoc pour administrer un système Linux avec Ansible. Le mode ad-hoc permet d’exécuter des tâches ad-hoc. Rappelons qu’une tâche n’est rien d’autre que l’appel à un module.

Il y a beaucoup de raisons qui invitent à apprendre le mode ad hoc :

  • Comment intégrer le scripting bash avec ansible,
  • Evaluer l’opportunité de passer aux livres de jeu et à la conception de rôles,
  • Envisager la conception par convention, le code réutilisable, la gestion des sources (SCM), la tenue de la documentation, …
  • Prendre connaissance des modules de base et les expérimenter.

Objectifs de certification

RHCE EX294 (RHEL8)

Si vous poursuivez des objectifs de certification voici ceux qui sont suggérés ici :

  • 2. Maîtrise des composants de base d’Ansible
    • 2.1. Inventaires
    • 2.2. Modules
    • 2.3. Variables
    • 2.7. Fichiers de configuration
    • 2.8. Utiliser la documentation fournie pour trouver des informations spécifiques aux modules et commandes Ansible
  • 3. Installation et configuration d’un nœud de contrôle Ansible
    • 3.1. Installer les paquets requis
    • 3.2. Créer un fichier d’inventaire statique des hôtes
    • 3.3. Créer un fichier de configuration
    • 3.4. Créer et utiliser des inventaires statiques pour définir des groupes d’hôtes
    • 3.5. Gérer les parallélismes
  • 4. Configuration des noeuds gérés par Ansible
    • 4.1. Créer et distribuer des clés SSH aux noeuds gérés
    • 4.2. Configurer la réaffectation des privilèges sur les noeuds gérés
    • 4.3. Valider une configuration fonctionnelle à l’aide des commandes Ansible ad hoc
  • 5. Écriture de scripts pour les tâches d’administration
    • 5.1. Créer des scripts shell simples
    • 5.2. Créer des scripts shell simples qui exécutent les commandes Ansible ad hoc
  • 6. Création des jeux et des playbooks Ansible
    • 6.1. Utiliser des modules Ansible courants
  • 7. Utilisation des modules Ansible
    • 7.1. Paquets logiciels et repos
    • 7.2. Services
    • 7.3. Règles de pare-feu
    • 7.4. Systèmes de fichiers
    • 7.5. Périphériques de stockage
    • 7.6. Contenus de fichiers
    • 7.7. Archives
    • 7.8. Tâches planifiées
    • 7.9. Sécurité
    • 7.10. Utilisateurs et groupes

1. Mise en place de la topologie de lab

1.1. Topologie de lab

Topologie du lab Ansible

2. Configuration du Contrôleur

La configuration du projet sur le contrôleur a toute son importance. Dans cette étape on propose de créer le projet avec Ansible lui-même, tant que faire se peut … On comprendra peut-être mieux que la solution Ansible joue le rôle d’interface intelligible entre l’infrastructure et ses administrateurs. Cette étape n’est pas nécessaire si le contrôleur est déjà approvisionné.

2.1. Installation de Ansible sur controller

Sur “controller”, nous allons travailler dans le dossier ~/lab, c’est une manière d’initier notre projet Ansible :

mkdir ~/lab
cd ~/lab

Il est nécessaire d’identifier la distribution CentOS 8 :

. /etc/os-release ; echo $PRETTY_NAME
CentOS Linux 8 (Core)

Si le “controller” est bien une machine Centos 8, on peut passer à l’installation de dépôt “Extra” nommés epel-release :

dnf -y install epel-release

Enfin, Ansible s’installe avec le gestionnaire de paquets :

dnf -y install ansible

Vérifions la version du moment :

ansible --version | head -1
ansible 2.9.15

2.2. Création d’un fichier d’inventaire

Avant toute chose, un projet de gestion avec ansible a besoin d’un inventaire.

Dans cette opération, on utilise le module ansible.builtin.copy avec l’argument content qui crée le fichier en ajoutant du contenu. La séquence \n réalise des retour charriot dans le texte.

ansible -m ansible.builtin.copy -a "dest=~/lab/inventory content='localhost ansible_connection=local\n\n[nodes]\nnode1\nnode2\n\n[all:vars]\nansible_connection=ssh\nansible_user=ansible\nansible_ssh_pass=testtest\nansible_become=yes\nansible_become_user=root\nansible_become_method=sudo\n'" localhost

Veuillez vérifier vous-même :

cat ~/lab/inventory
[nodes]
node0
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_ssh_pass=testtest
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo

Cet inventaire désigne trois hôtes :

  • node0 (le contrôleur lui-même)
  • node1
  • node2

Il désigne aussi deux groupes :

  • nodes qui comprend les hôtes node1 et node2
  • all avec des variables qui portent sur tous les hôtes de l’inventaire

On sera attentif aux variables d’inventaire du groupe all :

  • Le type de connexion en SSH : ansible_connection=ssh
  • Le nom d’utilisateur pour se connecter ansible_user=ansible
  • Le mot de passe de l’utilisateur de connexion : ansible_ssh_pass=testtest
  • L’élévation de privilège activé : ansible_become=yes
  • L’utilisateur privilégié à utiliser : ansible_become_user=root
  • La méthode d’élévation de privilèges : ansible_become_method=sudo

2.3. Création d’un fichier de configuration minimal

De manière semblable avec le module ansible.builtin.copy, on peut créer un fichier de configuration par défaut ansible.cfg

ansible -m ansible.builtin.copy -a "dest=~/lab/ansible.cfg content='[defaults]\ninventory = ./inventory\nhost_key_checking = False\n#private_key_file = ~/nodes.key\ndeprecation_warnings=False\n'" localhost

Veuillez vérifier vous-même :

cat ~/lab/ansible.cfg
[defaults]
inventory = ./inventory
host_key_checking = False
#private_key_file = ~/nodes.key
deprecation_warnings=False

Tant que la clé publique de “controller” n’est pas placée sur le compte l’utilisateur de gestion “ansible” des deux noeuds, le paramètre de clé privée est désactivé dans le fichier de configuration. Nous créerons cette clé privée dans les étapes suivantes.

3. Tâches Ad Hoc d’administration de base Linux

Toutes les actions qui suivent se déroulent désormais sur le “controller” dans le dossier de travail ~/lab qui dispose d’un fichier de configuration et d’un inventaire.

cd ~/lab

3.1. Test de connectivité

Le module ping teste la connexion de gestion par Ansible vers la cible. Attention, le résultat dépend du plugin de connexion utilisé. On sera attentif aux paramètres de connexion utilisateur, de ses privilèges et de l’élévation de prvilèges, mot de passe ou clé privée de la configuration, etc. La réponse attendue est “pong”. Attention, il ne s’agit pas d’un test ICMP Echo Request !

En toute logique, si le lab est bien monté, un résulat positif “pong” doit vous parvenir :

ansible all -m ping
node0 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node2 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
node1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

Notez que le binaire “ansible” demande un hôte ou un groupe d’inventaire, ici all :

ansible -m ping all

3.2. Commandes brutes

Si on ne précise pas le nom du module, c’est le module raw par défaut qui est utilisé avec les commandes brutes comme option d’argument.

Le module raw exécute les arguments directement en brut avec le plugin de connexion, ici dans une session SSH.

Quand on ne précise pas le module avec -m module et que l’option -a est utilisée, ansible suppose le module raw :

ansible nodes -a "hostname"
ansible nodes -a "df -h"

Installer Python3-pip comme pré-requis à une gestion ansible :

ansible nodes -a "dnf -y install python3-pip"
ansible all -a "uptime"
ansible all -a "date"
ansible all -a "cat /etc/redhat-release"
ansible all -a "mount"
ansible all -a "getenforce"

3.3. Redémarrage en mode brut

Pour redémarrer tous les serveurs dans le groupe [nodes] :

ansible nodes -a "/sbin/reboot"

Il est logique que cette opération brutale de redémarrage rompe la connexion entre le contrôlleur et ses noeuds en cours gestion et rende un retour “UNREACHABLE” !

node1 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Shared connection to node1 closed.",
    "unreachable": true
}
node2 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Shared connection to node2 closed.",
    "unreachable": true
}

Nous verrons dans la suite comment mieux gérer le redémarrage des systèmes sans générer une erreur de connexion.

3.4. Faire une pause

Le module pause permet de faire une pause dans le livre de jeu.

ansible nodes -a "/sbin/reboot"
ansible -m pause -a "minutes=2" nodes

3.5. Attendre une connection

wait_for_connection est un module qui attend qu’un système distant soit joignable/utilisable.

ansible nodes -a "/sbin/reboot"
ansible nodes -m wait_for_connection

Plus fin, attendre la bannière du serveur SSH

wait_for est un module qui attend une condition avant de continuer.

ansible nodes -a "/sbin/reboot"
ansible nodes -m wait_for -a "port=22 host='{{ ansible_host }}' search_regex=OpenSSH"

3.6. Redémarrer un hôte avec le module approprié

Le module reboot redémarre de manière appropriée le système distant :

ansible nodes -m reboot

3.7. Processus simultanés

Par défaut, Ansible (/usr/bin/ansible) utilise 5 processus simultanés (forks).

ansible-config dump | grep FORKS
DEFAULT_FORKS(default) = 5

On peut manipuler ce paramètre comme variable d’environnement, dans le fichier de configuration ou directement sur la ligne de commande avec -f ou --forks. Ici pour redémarrer les serveurs du groupe nodes avec 10 forks :

ansible nodes -m reboot -f 10

Le nombre de communication simultanées peut être contrôlé dans le fichier de configuration dans la section [default] :

[defaults]
forks = 10

3.8. Elévation de privilèges

Par défaut, Ansible (/usr/bin/ansible) utilise le compte courant comme utilisateur distant. Notre inventaire utilise un autre compte (le compte distant “ansible”).

On peut manipuler le type de connexion, le nom d’utilisateur, l’usage de l’élévation de privilège, le mot de passe de l’utilisateur et/ou la clé publique dans des variables d’inventaire ou de configuration comme nous l’avons configuré dans ce lab.

Si l’on désire contrôller l’élévation de privilèges sur la ligne de commande, on peut utiliser les options suivantes :

  • -u REMOTE_USER, --user REMOTE_USER
  • -k, --ask-pas
  • -b, --become
  • -K, --ask-become-pass
  • --become-user BECOME_USER
  • --become-method BECOME_METHOD

Pour se connecter avec l’utilisateur “ansible” :

ansible nodes -m reboot -u ansible

Redémarrer un serveur exigerait probablement une élévation de privilèges

ansible nodes -m reboot -u ansible --become

Si vous ajoutez --ask-become-pass ou -K, Ansible demande le mot de passe pour l’élévation de privilège (sudo/su/pfexec/doas/etc).

ansible nodes -m reboot -u ansible --become --ask-become-pass

4. Gestion des utilisateurs

4.1. Gérer les utilisateurs et les groupes

Les modules user et group gèrent les utilisateurs et les groupes.

Ici on crée un utilisateur “louise” dans le groupe “team” :

ansible nodes -m group -a "name=team state=present"
ansible nodes -m user -a "name=louise groups=team createhome=yes"

Supprimer un utilisateur et son groupe :

ansible nodes -m user -a "name=louise state=absent remove=yes"
ansible nodes -m group -a "name=team state=absent"

4.2. Configurer un utilisateur de gestion

ansible nodes -m user -a "name=ansible"
ansible nodes -m dnf -a "name=sudo"

Tentons de trouver de manière récursive une entrée “ansible ALL=\(ALL\) NOPASSWD\:ALL” dans un fichier situé dans le chemin /etc avec le module Ansible find qui identifie le rôle sudo ici de l’utilisateur Ansible :

ansible nodes -m find -a "paths=/etc contains='ansible ALL=\(ALL\).*$' recurse=yes"

Cette recherche nous donne un résultat sur le fichier /etc/sudoers.d/90-cloud-init-users. Comment est-il écrit, d’où vient-il ?

ansible node1 -a "cat /etc/sudoers.d/90-cloud-init-users"
node1 | CHANGED | rc=0 >>
# Created by cloud-init v. 19.4 on Sat, 21 Nov 2020 11:36:16 +0000

# User rules for ansible
ansible ALL=(ALL) NOPASSWD:ALL

Si nous désirons créer une utilisatrice de gestion avec des privilèges sans mot de passe nommée “Louise”, quelle est la procédure en mode Ansible Ad hoc ?

ansible nodes -m user -a "name=louise createhome=yes"
ansible nodes -m dnf -a "name=sudo"
ansible -m ansible.builtin.copy -a "dest=/etc/sudoers.d/ansible-louise content='louise  ALL=(ALL) NOPASSWD:ALL'" nodes

Ne serait-ce pas mieux de supprimer entretemps cet utilisateur et ce fichier ?

ansible nodes -m user -a "name=louise state=absent"
ansible -m file -a "dest=/etc/sudoers.d/ansible-louise state=absent" nodes

Créons plutôt un utilisateur “ansible” pour une gestion privilégiée :

ansible nodes -m user -a "name=ansible"
ansible nodes -m dnf -a "name=sudo"
ansible -m ansible.builtin.copy -a "dest=/etc/sudoers.d/ansible-louise content='louise  ALL=(ALL) NOPASSWD:ALL'" nodes
ansible -m file -a "path=/etc/sudoers.d/90-cloud-init-users state=absent" nodes
ansible -m ansible.builtin.copy -a "dest=/etc/sudoers.d/ansible content='ansible  ALL=(ALL) NOPASSWD:ALL'" nodes

5. Configurer l’accès par clé SSH

5.1. Création des clés SSH

Controller doit disposer d’une clé privée associée à une clé publique placée dans le fichier ~/.ssh/authorized_keys d’un utilisateur de gestion sur les cibles, ici root bien que cela ne soit pas recommandé.

La création d’une paire de clés SSH peut se réaliser avec le module openssh_keypair en utilisant les arguments path et size. Le module file permet de manipuler les propriétés des fichiers et des dossiers.

ansible -m openssh_keypair -a "path=~/nodes.key size=2048" localhost
ansible -m file -a "path=~/nodes.key mode=0600" localhost

5.2. Copie des clés SSH

Le module authorized_key se charge de placer la clé publique dans /home/ansible/.ssh/authorized_keys sur chaque noeud :

ansible -m authorized_key -a "user=ansible key='{{ lookup('file', '~/nodes.key.pub') }}'" nodes

On remarque l’usage d’une variable Jinja2 avec un filtre lookup qui lit un fichier local : la clé publique qui a été générée précédemment sur le système de fichier local.

5.3. Modification du fichier d’inventaire

Dans cette opération, on utilise le module ansible.builtin.lineinfile avec les arguments regexp et state pour effacer la ligne qui indique le mot de passe :

ansible localhost -m lineinfile -a "path=~/lab/inventory regexp='^ansible_ssh_pass=.*$' state=absent"

Veuillez vérifier vous-même :

cat ~/lab/inventory
[nodes]
node1
node2

[all:vars]
ansible_connection=ssh
ansible_user=ansible
ansible_become=yes
ansible_become_user=root
ansible_become_method=sudo

5.4. Modification du fichier de configuration minimal

De manière semblable avec le module ansible.builtin.replace, on peut modifier le fichier de configuration par défaut ansible.cfg en décommentant la ligne private_key_file :

ansible localhost -m replace -a "path=~/lab/ansible.cfg regexp='#private_key_file.*$' replace='private_key_file= ~/nodes.key'"

Veuillez vérifier vous-même :

cat ~/lab/ansible.cfg
[defaults]
inventory = ./inventory
host_key_checking = False
private_key_file = ~/nodes.key
deprecation_warnings=False

Vérifions la connectivité :

ansible -m ping all

6. Récupérer des Facts

6.1. Facts sur les hôtes

Par défaut, le module gather_facts est exécuté implicitement dans les livres de jeu. Ce comportement peut se contrôler dans un jeu ou dans la configuration.

Mais avec le binaire ansible, c’est le module setup qui affiche les variables collectées.

L’argument filter est utile sur la ligne de commande dans un usage Ad Hoc.

ansible node1 -m setup
ansible node1 -m setup -a 'filter=ansible_mounts'
ansible node1 -m setup -a "filter=*ipv4*"
ansible node1 -m setup -a "filter=ansible_swapfree_mb"

6.2. Module Debug

Le module debug permet d’afficher des messages. Il prend comme argument soit var qui affiche les variables ou msg qui affiche un message avec des variables.

Voyez la différence :

ansible nodes -m debug -a "var='inventory_hostname'"
ansible nodes -m debug -a "msg='Hello {{ inventory_hostname }}'"

7. Installer et gérer un service

7.1. Installation et gestion du service Chrony

Comme premier exercice de gestion simultanée, nous allons configurer le service de temps Chrony sur Centos8.

  • dnf – Manages packages with the dnf package manager
  • package – Generic OS package manager

  • service – Manage services
  • service_facts – Return service state information as fact data
  • systemd – Manage services

Installation de Chronyd sur RHEL8, avec les modules dnf ou package :

ansible all -m dnf -a "name=chrony state=present"
ansible all -m package -a "name=chrony state=present"

Démarrer et activer le service avec les modules service ou systemd :

ansible all -m service -a "name=chronyd state=started enabled=yes"
ansible all -m systemd -a "name=chronyd state=started enabled=yes"

Pour synchroniser les hôtes, on fera appel aux modules service et raw :

ansible all -m service -a "name=chronyd state=stopped"
ansible all -a "chronyd -q 'server 0.fr.pool.ntp.org iburst'"
ansible all -m service -a "name=chronyd state=started"

Ou encore :

ansible all -a "chronyc -a makestep"

Si nous étions invité à réaliser ces opérations sur des cibles Debian/Ubuntu ou en Centos 7, on proposerait ceci :

Installation de NTPD sur Debian/Ubuntu :

ansible all -m apt -a "name=openntpd state=present"
ansible all -m service -a "name=openntpd state=started enabled=yes"

Installation de NTPD sur RHEL7 :

ansible all -m dnf -a "name=ntp state=present"
ansible all -m service -a "name=ntpd state=started enabled=yes"

Et puis les tentatives de synchronisation :

ansible all -m service -a "name=ntpd state=stopped"
ansible all -a "ntpdate -q 0.eu.pool.ntp.org"
ansible all -m service -a "name=ntpd state=started"

8. Limites et tâches en arrière-plan

8.1. Limiter une tâche à un hôte

Dans cette démarche on exécute l’inventaire contre un groupe d’hôte (ici “all”) en le limitant uniquement à “node1” :

ansible all -m service -a "name=chronyd state=restarted" --limit node1

L’option --limit accepte des expressions rationnelles (regexp) :

ansible all -m service -a "name=chronyd state=restarted" --limit "node*"

L’option --limit accepte une liste :

ansible all -m service -a "name=chronyd state=restarted" --limit "node1,node2"

8.2. Tâches en arrière-plan

L’option -B permet d’activer la tâche en arrière-plan avec un délai maximum.

ansible nodes -B 3600 -m dnf -a "name="*" state=latest"

9. Fichiers

9.1. Prendre des informations sur un fichier

ansible nodes -m stat -a "path=/etc/environment"

9.2. Copier un fichier sur l’hôte

ansible nodes -m ansible.builtin.copy -a "src=/etc/hosts dest=/tmp/hosts"
ansible nodes -m stat -a "path=/tmp/hosts"

9.3. Rapatrier un fichier sur le contrôleur

ansible node1 -m fetch -a "src=/etc/hosts dest=/tmp/{{inventory_hostname}}"
ansible localhost -m stat -a "path=/tmp/node1"

9.4. Créer des dossiers et des fichiers

ansible nodes -m file -a "dest=/tmp/test mode=644 state=directory"
ansible nodes -m file -a "src=/tmp/test dest=/opt/tmp owner=root group=root state=link"
ansible nodes -m stat -a "path=/opt/tmp"

9.5. Effacer des dossiers et des fichiers

ansible nodes -m file -a "dest=/opt/tmp state=absent"
ansible nodes -m stat -a "path=/opt/tmp"

9.6. Autres modules fichiers

Files modules :

  • acl - Sets and retrieves file ACL information.
  • archive - Creates a compressed archive of one or more files or trees
  • assemble - Assembles a configuration file from fragments
  • blockinfile - Insert/update/remove a text block surrounded by marker lines
  • ansible.builtin.copy - Copies files to remote locations
  • fetch - Fetches a file from remote nodes
  • file - Sets attributes of files
  • find - Return a list of files based on specific criteria
  • ini_file - Tweak settings in INI files
  • iso_extract - Extract files from an ISO image
  • lineinfile - Manage lines in text files
  • patch - Apply patch files using the GNU patch tool
  • replace - Replace all instances of a particular string in a file using a back-referenced regular expression.
  • stat - Retrieve file or file system status
  • synchronize - A wrapper around rsync to make common tasks in your playbooks quick and easy.
  • tempfile - Creates temporary files and directories.
  • template - Templates a file out to a remote server
  • unarchive - Unpacks an archive after (optionally) copying it from the local machine.
  • xattr - Manage user defined extended attributes
  • xml - Manage bits and pieces of XML files or strings

10. Services applicatifs

10.1. Installation du service Apache HTTPd

Sous Centos :

ansible nodes -m yum -a "name=httpd state=present"

10.2. Lancer le service Apache

Sous Centos :

ansible nodes -m service -a "name=httpd state=started"

10.3. Tester le service Apache

ansible nodes -m uri -a "url=http://{{ansible_host}} return_content=yes status_code=200,403"

10.x. Arrêter le service

Sous Centos :

ansible nodes -m service -a "name=httpd state=stopped"

10.x. Retirer Apache

Sous Centos :

ansible nodes -m yum -a "name=httpd state=absent"

10.x. Installation et gestion du service MariaDB

ansible db -m yum -a "name=mariadb-server state=present"
ansible db -m service -a "name=mariadb state=started enabled=yes"

10.x. Configurer les serveurs d’application

!!!

ansible nodes -m dnf -a "name=python2-mysql state=present"
ansible nodes -m dnf -a "name=python2-setuptools state=present"
ansible nodes -m easy_install -a "name=virtualenv executable=easy_install-2"
ansible nodes -m easy_install -a "name=django state=present virtualenv=/opt"

10.x.. Pare-feu Firewalld

10.x. Pare-feu Iptables

!!!

ansible db -a "iptables -F"
ansible db -a "iptables -A INPUT -s 192.168.60.0/24 -p tcp -m tcp --dport 3306 -j ACCEPT"

Comment traduire les commandes iptables en module Ansible ?

ansible db -m iptables -a "flush=yes"
ansible db -m iptables -a "chain=INPUT protocol=tcp source=192.168.60.0/24 destination_port=3306 jump=ACCEPT"
ansible db -m yum -a "name=MySQL-python state=present"
ansible db -m mysql_user -a "name=django host=% password=12345 priv=*.*:ALL state=present"

Laisser un commentaire