10 - Automatisation

Introduction

L’automatisation recouvre plusieurs concepts:

Des outils de surveillance peuvent tenter de redémarrer des services ou applications ayant cessé de fonctionner.

Un outil d’automatisation peut installer et configurer les applications et s’assurer que les configurations restent conformes à ce qui a été défini.

Nous verrons la deuxième possibilité.

Dans ce cas l’administrateur définit de façon centralisée ce que doit être la configuration des applications puis pousse la configuration vers tous les serveurs ayant cette application.

De cette façon, vous êtes assurés d’avoir un parc de serveurs avec des configurations homogènes en tout temps.

Vous pourrez donc déployer et gérer votre infrastructure de façon automatique.

Il existe plusieurs applications pour l’automatisation: Chef, Master, Puppet, Ansible…

Administrer plusieurs serveurs peut rapidement devenir très compliqué sans stratégie d’automatisation.

On doit passer sur chaque serveur pour apporter des modifications.

Les commandes d’administration peuvent changer d’une distribution à l’autre (ex: dnf vs APT).

Aucune vision globale de l’état des changements apportés (succès ou échec).

Une solution d’automatisation comme Ansible permet d’exprimer à haut niveau la configuration souhaitée.

Présentation de Ansible

Ansible est un logiciel d’automatisation moderne qui permet de configurer à partir d’un contrôleur plusieurs serveurs simultanément

Ansible est dit agentless . C’est à dire qu’il ne nécessite aucun agent supplémentaire sur les serveurs. Seuls Python et SSH sont requis sur les noeuds qui seront configurés par Ansible.

Ansible est compatible avec les principales distributions Linux, Windows et même certains équipements comme des switchs et des routeurs et d’autres équipements. Ansible contient plus d’une centaine de modules.

Installation

On installe le paquet Ansible uniquement sur le contrôleur.

Ansible est disponible sur le dépôt Epel que vous devez d’abord installer :

$ dnf install -y epel-release

Vous pouvez maintenant installer Ansible sur votre contrôleur :

$ dnf install ansible

Fichier d’inventaire

Suite à son installation, Ansible est déjà prêt à administrer des noeuds. Pour ce faire, il faut les déclarer dans le fichier d’inventaire.

L’inventaire contient la liste des noeuds configurés par Ansible. Les noeuds peuvent être regroupés en catégories (par exemple: Serveurs

On peut identifier un noeud par son adresse IP ou son FQDN.

Exemple de fichier d’inventaire

Par défaut, le fichier d’inventaire se trouve dans /etc/ansible/hosts.

# Liste des machines du B3350
[B3350]
pc1
pc2

# Liste des machines des profs
[Profs]
prof1
prof2

À noter que les noms ou les adresses IP fournies dans l’inventaire doivent être accessible par le contrôleur.

Vérifiez avec un ping que la résolution de nom fonctionne et que arrivez à joindre vos machines.

Configuration des noeuds

Ansible est agentless, il n’y a pas besoin d’installer un agent sur les noeuds.

Seuls ssh et python sont nécessaires.

ssh est toujours installé. Si python n’est pas installé, la commande suivante règlera le problème :

$ dnf install -y python36

Rappel sur les clés de chiffrement

Chiffrement asymétrique:

  • Les deux parties utilisent des clés différentes. La clé publique permet de crypter et seule la personne possédant la clé privée sera en mesure de décrypter le message.

  • Ce qui est crypté avec une clé peut uniquement être décrypté avec l’autre clé.

Cle2

Chiffrement asymétrique:

  • La clé publique peut être distribuée de façon illimitée à tout le monde.

  • La clé privée doit être conserver précieusement de façon confidentielle.

  • Problème 1: la clé publique doit être fournie de façon sécurisée sinon un pirate peut fournir à vos clients sa clé publique et pourra réaliser une attaque de l’homme du milieu.

  • Problème 2: le chiffrement par clés asymétriques est 1000 fois plus lent que le chiffrement par clés symétriques.

Problème 1: la clé publique est signée au sein d’un certificat par une autorité de certification de confiance pour garantir que c’est la bonne clé publique qui est transmise.

Problème 2: le chiffrement asymétrique n’est utilisé qu’au début pour permettre l’échange sécurisé d’une clé symétrique qui ne sera valide que pour la durée de la session.

Les principaux protocoles de chiffrement sont:

  • RSA (chiffrement et signature)

  • DSA (signature seulement)

Pour créer un couple de clés:

$ ssh-keygen -t rsa

Generating public/private rsa key pair.
Enter file in which to save the key (/home/axel/.ssh/id_rsa): 
Created directory '/home/axel/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/axel/.ssh/id_rsa.
Your public key has been saved in /home/axel/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:iCjIFYCiTFXPTjLvVDqsWjQYKNMIR0OH+xWwF1v2Z3U axel@m1.dom.local
The key's randomart image is:
+---[RSA 2048]----+
|o+B++o. o     . E|
|+=oo..+= .   . . |
|O..+.oo= .. o    |
|=oo.ooO.o  o     |
|o..o.+.BS        |
| .  o = .        |
|     o .         |
|    o            |
|   .             |
+----[SHA256]-----+

Par défaut, les clés sont créées dans ~/.ssh/ et se nomment id_rsa et id_rsa.pub.

ATTENTION: la clé privée id_rsa doit être protégée: permission 400 et ne doit jamais être distribuée.

Crypter ou signer

Crypter avec la clé publique:

  • Si je crypte avec la clé publique, seule la personne ayant la clé privée pourra décrypté le message (la clé publique ne peut pas décrypter ce qui a été crypté avec la clé publique).

  • Dans ce cas, la confidentialité est assurée, il s’agit bien de cryptage.

Signer avec la clé privée:

  • Si je crypte avec la clé privée, toutes les personnes possédant la clé publique pourront décrypter le message, il n’y a donc aucune confidentialité. Par contre, si elles parviennent à décrypter le message à l’aide de la clé publique, cela signifie qu’il a été crypté avec la clé privée.

  • Comme une seule personne possède la clé privée, cela signifie obligatoirement que le message vient de cette personne: on parle de signature.

On crypte avec la clé publique, on signe avec la clé privée.

Connexion ssh sans mot de passe

À l’aide d’un couple de clés on pourra se connecter à une machine distante en utilisant ssh sans avoir besoin de mot de passe.

Pour cela, il faudra signer le message à l’aide de la clé privée. Ceci permet de prouver notre identité.

On signe un message à l’aide de la clé privée, ce message est envoyé à la machine distante. Si la machine distante peut décrypter le message à l’aide de la clé publique correspondant à la clé privée, cela signifie que l’identité de l’utilisateur a été vérifiée et qu’il peut donc se connecter.

Pour cela, il faudra préalablement copier la clé publique sur la machine distante.

Puis il faut la placer dans le fichier ~/.ssh/authorized_keys de l’utilisateur qui sera autorisé à se connecter sans mot de passe.

Pour copier la clé dans le fichier:

$ cat id_rsa.pub > ~user/.ssh/authorized_keys

Toutes les autres clés qui seront ajoutées ensuite devront l’être avec:

$ cat id_rsa.pub >> ~user/.ssh/authorized_keys

Clé ssh pour Ansible

Votre clé ssh doit être configurée pour l’utilisateur root du contrôleur et l’utilisateur root des clients.

Formatif 1

Vous aurez besoin de 3 machines :

Le contrôleur

Deux noeuds qui seront gérés par le contrôleur.

Sur le contrôleur, connectez-vous en root puis créez vos clés ssh:

# ssh-keygen -t rsa

Copiez les commandes ci-dessous dans un fichier texte nommé script.sh dans le répertoire /root sur le contrôleur :

scp .ssh/id_rsa.pub root@$1:
ssh root@$1 "mkdir .ssh 2>/dev/null"
ssh root@$1 "cat id_rsa.pub >> .ssh/authorized_keys"
ssh root@$1 "chmod 700 .ssh"
ssh root@$1 "chmod 600 .ssh/authorized_keys"
scp .ssh/id_rsa.pub root@$2:
ssh root@$2 "mkdir .ssh 2>/dev/null"
ssh root@$2 "cat id_rsa.pub >> .ssh/authorized_keys"
ssh root@$2 "chmod 700 .ssh"
ssh root@$2 "chmod 600 .ssh/authorized_keys"

Exécutez ensuite la commande suivante :

# bash script.sh <ip noeud1> <ip noeud2>

Lancer des commandes ad-hoc

Une commande ad hoc est une commande sporadique qu’on souhaite exécuter une seule fois sur un ou plusieurs hôtes de notre inventaire.

Pour lancer une commande ad hoc on utilisera par exemple la syntaxe suivante:

$ ansible <noeuds> -m <nom du module> -a <arguments>

Dans ce premier exemple, on souhaite vérifier la connectivité des différents noeuds du groupe B3350.

Pour ce faire, on utilisera le module ping. Sur le contrôleur, on lance la commande :

$ ansible B3350 -m ping

pc1 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}
pc2 | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname pc2: Name or service not known",
    "unreachable": true
}
  • notez que le module ping ne prend aucun argument

On remarque que pc1 est actif mais pas pc2.

Formatif 2

Créez le fichier d’inventaire. Vous y placerez trois groupes:

  • ntp: contient les deux machines

  • web: contient une des deux machines

  • dns: contient l’autre machine

Testez que votre fichier d’inventaire est correct avec les commandes suivantes:

$ ansible all --list-hosts
$ ansible web --list-hosts
$ ansible dns --list-hosts
$ ansible ntp --list-hosts

Testez le module ping sur toutes vos machines pour vous assurer qu’elles fonctionnent et sont joignables.

Utiliser différents modules

Ansible compte plus d’une centaine de modules différents organisés en différentes catégories.

Par exemple: Gestionnaires de paquets, fichiers, système,…

Leur documentation est accessible ici: https://docs.ansible.com/ansible/2.9/modules/list_of_all_modules.html

Utiliser différents modules

Ansible compte plus d’une centaine de modules différents organisés en différentes catégories.

Par exemple: Gestionnaires de paquets, fichiers, système,…

Leur documentation est accessible ici: https://docs.ansible.com/ansible/latest/modules/modules_by_category.html

Pour voir la liste des modules:

$ ansible-doc -l

Et pour les compter:

$ ansible-doc -l | wc -l

Pour installer une application, nous pouvons utiliser le module dnf sur Centos.

Pour installer le paquet fail2ban, la commande sera:

$ ansible all -m dnf -a "name=fail2ban state=latest update_cache=true"

Cette commande installe la dernière version de fail2ban, le met à jour si nécessaire.

Formatif 3

Installez puis désinstallez fail2ban à l’aide de Ansible.

Les playbooks

Comparativement aux commandes ad hoc, les playbooks sont des recettes comprenant plusieurs tâches à exécuter successivement.

Ils sont écrits en langage YAML , un langage de balisage (à la manière de XML, HTML ou JSON) qui est facile à lire pour un humain.

YAML

YAML est un langage utilisant des balises. Il signifie : Yet Another Markup Language.

Le site Ansible fournit une explication de son fonctionnement: https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax

Voici des bases de YAML :

  • Un fichier YAML peut commencer par et finir par
---

Contenu

...
  • Avec Ansible, une recette commence presque toujours par une liste (souvent une liste de tâches). Les élémets d’une liste doivent avoir la même indentation et commencent par un - suivi d’un (espace) :
---

- Pomme
- Poire
- Cerise

...
  • Il existe aussi les dictionnaires ou tableaux associatifs qui sont des paires de clé/valeur séparées par : :
Prof:
	nom: Axel
	age: 28
  • On peut avoir une liste de dictionnaires :
---

- Prof:
	nom: Axel
	age: 28

- Etudiant1:
	nom: Bob
	age: 18

- Etudiant2:
	nom: Alice
	age: 19

...

Création de playbooks

Une bonne pratique pour la gestion des playbooks est de créer une arborescence. On crée un répertoire playbooks qui contiendra tous les playbooks. En dessous, on crée un répertoire pour chacun des playbooks. Ces réperoires devront avoir un nom significatif par rapport à leur rôle.

Exemple de la création d’un playbook permettant de désactiver selinux et firewalld sur des machines.

$ mkdir security
$ cd security
$ vim desactiver_securite.yml

Le fichier de recette pourra contenir:

---
- hosts: all
  tasks:
    - name: Désactivation de selinux
      # le module utilisé
      lineinfile:
        # spécifie le chemin du fichier à modifier
        path: /etc/selinux/config
        # Modifiera la ligne correspondant à l'expression régulière suivante
        regexp: '^SELINUX='
        # Comment sera modifié la ligne
        line: SELINUX=disabled
    - name: Désactivation de firewalld
      # Nom du module utilisé
      service:
        name: firewalld
        state: stopped
        enabled: no
...

Exécution des playbooks

Pour exécuter un playbook :

$ ansible-playbook <chemin_vers_playbook.yaml>

HTTP

Formatif 4

Créez le playbook suivant:

  • Pour les serveurs web, le serveur Apache devra être installé et démarré et le démarrage automatique configuré.

De plus, la page web par défaut devra être modifié pour afficher l’adresse IP du serveur web.

Les modules suivants doivent être utilisés:

  • dnf pour l’installation du serveur.

  • service pour le démarrage du service.

  • copy pour créer le fichier index.html. Son contenu peut utiliser les variables {{ ansible_fqdn }} et/ou {{ ansible_default_ipv4.address }}.

Itération

Lorsque plusieurs opérations similaires doivent être effectuées, il est possible de factoriser les opérations grâce à des itérations.

Exemple: vous souhaitez ajouter plusieurs lignes à un fichier, plutôt que d’appeler plusieurs fois le module lineinfile, il est possible de l’appeler une fois et d’effectuer des itérations pour chacune des lignes.

Si vous souhaitez ajouter les deux lignes suivantes au fichier /etc/resolv.conf:

nameserver 8.8.8.8
nameserver 8.8.4.4

Vous pourrez utiliser un playbook de la forme:

---
- hosts: all
  tasks:
    - name: Configuration des DNS
      # le module utilisé
      lineinfile:
        # spécifie le chemin du fichier à modifier
        path: /etc/resolv.conf
        # On spécifie que les lignes à ajouter le seront au sein d'une indentation
        line: "{{ item }}"
        # Les lignes devront être présentes dans le fichier (ajoutées le cas échéant)
        state: present
      with_items:
        - 'nameserver 8.8.8.8'
        - 'nameserver 8.8.4.4'
...

Formatif 5

Créez les playbooks suivants :

  1. Un pour la configuration de NTP, vos serveurs devront interroger les serveurs suivants grâce aux lignes de configuration suivantes :
Server 0.ca.pool.ntp.org
Server 1.ca.pool.ntp.org
Server 2.ca.pool.ntp.org
Server 3.ca.pool.ntp.org

Le service devra démarrer automatiquement et être démarré

Les modules suivants seront utilisés:

  • dnf pour l’installation de chrony

  • lineinfile avec itération pour l’ajout des serveurs.

  • service pour le démarrage.

  1. Un pour les serveurs DNS : installation, démarrage et démarrage automatique de Bind 9.

Dans la configurtion, assurez-vous que le serveur soit un DNS récursif (ceci DOIT se trouver dans votre playbook) et il doit écouter sur toutes les interfaces et accepter les requêtes de toute la planète.

Voici, pas dans l’ordre, ni même groupées correctement, les instructions à trouver :

recursion yes;
allow-query { any; };
listen-on port 53 { any; };

Les modules utilisés seront les suivants:

  • dnf pour l’installation

  • lineinfile pour les 3 lignes à ajouter.

  • service pour le démarrage.

Suppléments

Gestion des playbooks

Les playbooks devront être sauvegardés et idéalement gérés à l’aide d’un gestionnaire de version. Comme il s’agit de fichiers texte, git est tout désigné pour cette tâche.

Répétition des commandes

Beaucoup d’instructions sont dites idempotent, c’est à dire qu’elles donneront le même résultat quel que soit le nombre de fois où elles seront exécutées. Ainsi, on peut demander l’installation d’une application 20 fois, elle ne sera installé qu’une seule fois, même chose pour le démarrage d’un service.

Pour cette raison, chacun des playbooks peut être exécuté toutes les heures pour s’assurer que même en cas d’intervention manuelle, la bonne configuration sera rétablie rapidement.

La méthode à utiliser est de configurer l’exécution des playbooks dans la crontab du contrôleur.

Exemple de ligne à ajouter dans la crontab :

0 * * * * ansible-playbook /root/playbooks/ntp/ntp.yaml

Les handlers

Il est possible de redémarrer les services lorsqu’un changement est effectué pour que celui-ci soit pris en compte. Pour ceci il faudra ajouter une section notify dans la tache et un handler qui sera le destinatire du notify.

Les handlers sont appelés une seule fois pour chaque service même si plusieurs taches nécessitent un redémarrage du service. Ils sont appelés uniquement si un changement a été effectué donc un service ne sera pas redémarré inutilement.

Exemple: modification du fichier rsyslog.conf et redémarrage du service rsyslogd.

---
- hosts: all
  tasks:
    - name: Modification rsyslog
      # le module utilisé
      lineinfile:
        # spécifie le chemin du fichier à modifier
        path: /etc/rsyslog.conf
        # On spécifie que les lignes à ajouter le seront au sein d'une infentation
        line: "{{ item }}"
        # Les lignes devront être présentes dans le fichier (ajoutées le cas échéant)
        state: present
      with_items:
        - '$ModLoad imudp'
        - '$UDPServerRun 514'
      notify:
        - restart rsyslogd

  handlers:
    - name: restart rsyslogd
      service:
        name: rsyslog
        state: restarted
...

Formatif 6

Ajoutez l’exécution de vos playbooks à la crontab, toutes les 4 minutes.

Modifiez le fichier de configuration du client NTP et validez que la configuration revient à la normale rapidement.

ATTENTION : dans certains cas, le services doit être redémarré pour que la modification soit à nouveau prise en compte.

Vous devrez utiliser les handlers avec la fonction notify.

Formatif facultatif

Serez-vous capables de trouver la solution pour corriger le fichier chrony.conf si quelqu’un ajoute une ou plusieurs lignes d’instruction Server ….

Le fichier devrait toujours contenir uniquement les 4 lignes que vous avez configurées.

Astuce: regardez le module replace.

Les templates

Il est possible de créer des fichiers contenant des variables Ansible et de les copier sur les serveurs distants. Pour ceci on utilise le module template.

Exemple : le fichier hosts de la machine cible doit contenir l’adresse IP et le nom de la machine.

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.230.133		machine.domaine.com

Pour ceci, on peut créer un fichier modèle stocké avec le playbook :

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

{{ ansible_default_ipv4.address }}	{{ ansible_fqdn }}

Dans le playbook on utilisera le module template :

---
- hosts: all
  tasks:
          - name: Fichier hosts
            template:
                    src: /root/playbooks/hosts/hosts
                    dest: /etc/hosts
...

Lors de l’exécution, le nom des variables sera remplacé par leur valeur.

Formatif 7

Reprenez le formatif 4 mais la page web sera créée à l’aide de template.