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.
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.
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
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.
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.
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
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é.
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 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.
À 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
Votre clé ssh doit être configurée pour l’utilisateur root du contrôleur et l’utilisateur root des clients.
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>
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 }
On remarque que pc1 est actif mais pas pc2.
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.
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
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.
Installez puis désinstallez fail2ban
à l’aide de Ansible.
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 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 :
—
et finir par …
---
Contenu
...
-
suivi d’un
(espace) :---
- Pomme
- Poire
- Cerise
...
:
:Prof:
nom: Axel
age: 28
---
- Prof:
nom: Axel
age: 28
- Etudiant1:
nom: Bob
age: 18
- Etudiant2:
nom: Alice
age: 19
...
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
...
Pour exécuter un playbook :
$ ansible-playbook <chemin_vers_playbook.yaml>
Créez le playbook suivant:
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 }}
.
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'
...
Créez les playbooks suivants :
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.
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.
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.
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
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
...
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
.
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
.
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.
Reprenez le formatif 4 mais la page web sera créée à l’aide de template.