14 KiB
\newpage
Automatiser la configuration de son SI
Comme tout bon paresseux sys. admin qui se respecte, sans plus attendre,
vous allez vouloir automatiser toutes ces actions rébarbatives (configurer des
services). Comme de très nombreuses personnes sont passées par là avant vous,
il existe un grand nombre de solutions pour gérer les configurations d'un parc
de machines. Parmi les plus connues, citons : Puppet,
Chef, SaltStack ou encore
Ansible.
Introduction à Ansible
Ansible est une solution de gestion de configuration. Basé sur Python et YAML, sa principale particularité est de ne pas nécessiter de daemon sur les machines qu'il va gérer : tout se fait exclusivement via SSH, à partir de la machine d'un administrateur, possédant Ansible (ou bien d'un système de gestion de configuration tel qu'Ansible Tower ou AWX).
Son installation est très simple, car les dépendances sont minimes et l'outil n'a pas besoin de base de données pour fonctionner : tout va se faire à partir d'une arborescence de fichiers, qui sera gérée par Git.
Commencez par installer Ansible, sur une machine distincte de la machine virtuelle démarrée : la machine hôte sera parfaitement adaptée à cette tâche, d'autant plus que l'installation de la plate-forme est propre et légère : vous ne risquez pas de vous retrouver avec une usine à gaz impossible à retirer.
Consultez la procédure d'installation pour votre distribution ici.
Résultat attendu
À la fin de cette partie, vous devez être en mesure de déployer une nouvelle machine, identique à celle que vous venez de configurer, à partir d'une ISO et d'un nouveau disque.
Le fichier à rendre est un playbook login-x-TP2/basis.yml
, accompagné de
toutes ses dépendances : celui-ci doit faire les configurations basiques du
système et des utilisateurs.
Un deuxième playbook est à rendre : login-x-TP2/vitrine.yml
, celui-ci doit
permettre de déployer, une page vitrine typique d'une entreprise (cf. la 4e
question de cours ;)). Cette page doit être accessible depuis votre domaine
https://login-x.adlin2023.example.tld/.\
::::: {.warning}
Il est attendu que la vitrine soit générée avec Hugo, sur la machine exécutant Ansible (et non pas sur la machine où elle sera hébergée).
Pour réaliser cela, vous êtes autorisé à faire usage du module
ansible.builtin.command
.
:::::
Ma première commande
Inventaire
Comme il sera bientôt question de maintenir plusieurs machines, la première chose à faire consiste à les inventorier.
Commençons par créer un répertoire, qui contiendra l'ensemble de nos
configurations à destination d'Ansible. Dans ce répertoire, créons un fichier
d'inventaires hosts
, contenant l'IP de notre machine :
::::: {.more}
Vous pouvez lancer une autre machine virtuelle à ce stade et ajouter son IP sur
une nouvelle ligne du fichier hosts
.
:::::
Plus tard, c'est dans ce fichier que vous pourrez créer des groupes de machines (pour, par exemple, regrouper les serveurs web, les bases de données, etc.) ou donner des caractéristiques spécifiques à vos machines.
ping
Lancez ensuite la commande suivante :
Vous devriez avoir un retour similaire à celui-ci, indiquant simplement que la connexion a bien été effectuée et que le nécessaire est bien installé sur la machine distance.\
::::: {.question}
Si votre clef SSH n'a pas été récupérée depuis le CRI, vous pouvez rajouter
l'option --ask-pass
, afin de pouvoir indiquer le mot de passe de connexion au
compte, sinon vous obtiendrez une erreur plutôt incompréhensible.
:::::
Confort
Une des premières choses que nous devrions faire, est d'installer notre clef SSH sur les machines, pour éviter d'avoir à taper le mot de passe à chaque test.
Si vous avez bien suivi jusqu'ici, vous savez qu'il ne faut pas utiliser le
compte root
directement ! Pas d'inquiétude, tout est prévu dans Ansible :
retirer l'option --user root
si votre nom d'utilisateur local est identique
que celui dans la machine virtuelle, ou adaptez l'option en conséquence.
Et ajoutez les options --become
et --ask-become-pass
(utilisez --sudo
et
--ask-sudo-pass
pour les vieilles versions) :
Les modules
Les modules Ansible ont généralement deux missions distinctes :
- récupérer des informations en allant les extraire sur chaque machine ;
- pousser des nouvelles informations pour atteindre un état précis.
Lorsque les informations récupérées montrent que l'état est déjà atteint, aucune modification n'est faite sur la machine, on parle d'idempotence.
À noter cependant que lorsque l'on retire un état de notre recette, il est
conservé tel quel sur la machine. Par exemple, si un utilisateur toto
est
créé suite à l'application d'une recette décrivant l'utilisateur toto
; si
l'on supprime la description, l'utilisateur ne sera pas supprimé des machines
sur lequel la recette aura été appliquée. Pour qu'il soit supprimé, il faut
modifier la description pour signaler que cet utilisateur ne doit pas
exister. À l'application de cette nouvelle recette, si un tel utilisateur est
trouvé, il sera donc supprimé.
Collecte du lundi d'informations
Parmi les modules de base, le module setup
permet de récupérer un grand
nombre de caractéristiques de la machine distance, voyez plutôt :
Les informations récupérées (quel que soit le module), sont ensuite accessibles
dans des variables afin de permettre de compléter des modèles ou pour faire de
l'exécution conditionnelle d'état (par exemple on pourra utiliser la variable
ansible_facts.ansible_distribution
pour distinguer les gestionnaires de
paquets).
Ma première recette
Un livre de recettes (playbook dans le vocabulaire d'Ansible), regroupe les descriptions des états que l'on souhaite obtenir sur un groupe de machines données.
Par exemple, voici à quoi pourrait ressembler un tel recueil :
tasks:
-
name: ensure nginx is at the latest version apt: name=nginx state=latest
-
name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf
-
hosts: databases remote_user: root
tasks:
- name: ensure postgresql is at the latest version yum: name=postgresql state=latest
- name: ensure that mysql is started service: name=mysqld state=started
</div>
On filtre d'abord sur les hôtes concernés par la configuration, on donne
ensuite des paramètres qui seront utilisées pour chaque tâche et enfin on
décrit les tâches.
Le guide de référence pour connaître toutes les syntaxes possibles est
disponible dans [la documentation
d'Ansible](http://docs.ansible.com/ansible/latest/playbooks.html).
### Exécution d'un *Playbook*
Placer le contenu dans un fichier YAML, par exemple `playbook.yml`, non loin du
fichier `hosts` créé à la section précédente, puis lancez :
<div lang="en-US">
```bash
ansible-playbook -i hosts playbook.yml
Coup de pouce
Voici à quoi ressemblerait votre premier playbook créant l'utilisateur
adeline
:
tasks:
- name: ensure adeline as an account user: name: adeline state: present shell: /bin/fish groups: sudo append: yes
</div>
La création de l'utilisateur se fait à l'aide du module
[`user`](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html).
À vous maintenant, à l'aide [des collections de modules à votre
disposition](https://docs.ansible.com/ansible/latest/collections/index.html)
de copier vos fichiers de configuration à leur place et avec les bons droits
(`authorized_keys`, `.bashrc`, ...). Installez également les paquets que vous
aviez installés à la main (client DHCP, `htop`, ...).
Les notifications
-----------------
Au sein d'un *playbook*, il est possible de définir des tâches particulières
(nommées *handlers* dans le vocabulaire Ansible), qui ne seront exécutées que
si un changement a eu lieu. Par exemple, nous pourrions déclarer une tâche de
redémarrage du serveur web, seulement lorsque sa configuration est mise à
jour.
Pour se faire, il faut ajouter un élément `notify` à sa tâche :
<div lang="en-US">
```yaml
- name: template configuration file
template: src=template.j2 dest=/etc/httpd.conf
notify:
- restart cherokee
Puis, au même niveau que les tasks, on déclare nos handlers :
Il est attendu que le nom de la notification corresponde à l'attribut name
d'un handler.
Voir la documentation pour davantage de détails et d'exemples.
La configuration de votre serveur SSH laisse à désirer. Corriger les problèmes énoncés par ces deux articles :
- https://linuxhandbook.com/ssh-hardening-tips/ ;
- https://www.ssi.gouv.fr/guide/recommandations-pour-un-usage-securise-dopenssh/ ;
- https://stribika.github.io/2015/01/04/secure-secure-shell.html.
Mettez en place un handler pour relancer votre serveur SSH en cas de modification de sa configuration.
Les variables
Les variables peuvent provenir de très nombreux emplacements : définies
spécifiquement pour l'hôte, chargées depuis des fichiers lié au groupe auquel
appartient la machine, liées à la recette ou à la tâche en cours d'exécution,
récupérée par un module (comme le module setup
).
Définition
Dans l'inventaire
Cet inventaire définit une variable ntp_server
qui pourra ensuite être
utilisée par une recette installant un serveur NTP. Le cadre d'utilisation de
ce genre de variable est adapté lorsqu'un hôte ou un groupe d'hôte seulement
partagent des caractéristiques, comme par exemple leur localisation ou
datacenter de rattachement, etc.
Dans un playbook
De la même manière que dans l'inventaire, il est possible de définir des variables dans une recette :
Ces variables peuvent être placées dans un fichier, pour plus de lisibilité :
Le format correspond au sous arbre que l'on pourrait trouver sous vars
:
Utilisation
Modèles
Ces variables peuvent être utilisées pour remplir un emplacement de texte dans un modèle. Ansible utilise Jinja2 comme moteur de template, un format très courant dans le milieu du développement Python.
Ce format est relativement générique. Ainsi, pour accéder aux éléments d'une
table de hash/dictionnaire, on utilise le caractère .
; les crochets [x]
sont utilisés principalement pour accéder aux éléments des tableaux :
ou d'un dictionnaire lorsque la clef doit être récupérée d'une variable :
Notez dans ce dernier exemple l'utilisation d'un filtre join
permettant
d'aplatir la liste retournée.
Dans les recettes
Au sein même de votre description YAML, vous pouvez utiliser la puissance de Jinja2 :
Dans cet exemple, les variables des fichiers /vars/webservers_common.yml
,
puis celles de /vars/webservers_Debian.yml
seront chargées dans le contexte.
Conditions
Une autre utilisation possible des variables est d'appliquer ou de passer des tâches. Par exemple :
Vous trouverez bien d'autres cas d'utilisation dans la documentation.