Ansible
Présentation
Ansible est une plateforme logicielle libre pour la configuration et la gestion des ordinateurs. Elle combine le déploiement de logiciels multi-nœuds, l'exécution des tâches ad-hoc, et la gestion de configuration. Elle gère les différents nœuds à travers SSH et ne nécessite l'installation d'aucun logiciel supplémentaire sur ceux-ci. Les modules communiquent via la sortie standard en notation JSON et peuvent être écrits dans n'importe quel langage de programmation. Le système utilise YAML pour exprimer des descriptions réutilisables de systèmes, appelés playbook. Elle permet :
- D'automatiser : déployer des applications, gérer des systèmes
- De gagner du temps : grâce aux playbooks, on peut résoudre les problèmes une fois et partager facilement la solution
- De collaborer : en créant une "culture de l'automatisation"
- D'intégrer plus facilement en automatisant les technologies que l'on utilise déjà.
Mise en place
Source :
https://www.tartarefr.eu/ansible-par-la-pratique-premiere-partie-les-bases/
J'utilise deux machines :
- Mon PC sous Ubuntu 18.10, sur lequel j'installe Ansible et sshpass
- Une VM Debian basique, qui sera ma cible (192.168.1.49)
Avant toute chose, il faut que notre VM soit dans les known_hosts de la machine Ansible. Il suffit de s'y connecter une fois en ssh pour ajouter la clef !
Je commence par installer Ansible sur mon PC. Ensuite, je peux commencer à lancer une commande via Ansible :
justine@Justine-pc:/etc/ansible$ ansible 192.168.1.49 -m ping
Mais ça ne fonctionne pas : Ansible ne connait pas la cible, et on a pas d'utilisateur sur celle-ci !
Dans mon dossier home, je créée deux répertoires : prod et preprod, et dans chacun d'entre eux je met un fichier hosts.ini vierge. Ce fichier contiendra notre inventaire de machines.
Ensuite, dans preprod/hosts.ini, je mets :
[server] 192.168.1.49 [ansible] localhost
Je peux ensuite rééessayer en précisant le répertoire de notre environnement et un utilisateur et ça fonctionne :
justine@Justine-pc:~/Ansible$ ansible -i ~/Ansible/preprod/hosts.ini 192.168.1.49 --user user --ask-pass -m ping SSH password: 192.168.1.49 | SUCCESS => { "changed": false, "ping": "pong" }
Je peux tenter une commande :
justine@Justine-pc:~/Ansible$ ansible -i ~/Ansible/preprod/hosts.ini 192.168.1.49 --user user --ask-pass -a whoami SSH password: 192.168.1.49 | CHANGED | rc=0 >> user
Par défaut, Ansible utilise le même utilisateur que l'utilisateur local sur l'hôte distant. C'est pour cela qu'il faut bien gérer les utilisateurs. De plus, il faut penser que la connexion root en ssh est désactivée par défaut sur Debian !
Lancer une série de commandes
Le fichier permettant de lister les commande à lancer s’appelle un playbook. Il peut être monolithique ou divisé en plusieurs parties. Ansible recommande la séparation par rôle, ce qui permet de réutiliser les commandes d’un playbook et de les partager facilement avec la communauté.
Les rôles
Il faut bien réfléchir à la séparation des rôles qu'aura notre serveur, afin de garder une bonne modularité. Si on prend l'exemple d'un serveur web personnel qui contiendrait un blog Wordpress, un outil d'analyse de trafic web Piwik et du monitoring, on a déjà 3 rôles. Mais chacun de ces rôles en nécéssite d'autres (bdd, serveur web...), et que la machine doit rester à jour, on arrive a un grand nombre de rôles :
- system: concernera la post-installation du système
- Mettre le système à jour
- Définition des pools ntp (fr)
- Définition du nom d’hôte
- Définition basique du firewall: SSH (22)
- Redirection des mails locaux vers une adresse externe
- etc…
- pki: Concernera l’obtention et la mise à jour des certificats SSL
- Copie de nos clés privées
- Copies de nos fichiers et scripts gérant la pki (systemd)
- webserver: Concernera l’installation et la configuration d’apache
- Installation et configuration d’apache
- Mise à jour des extensions (ex modsecurity), hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
- Ajout des ports HTTP (80) et HTTPS (443) au firewall
- database: Concernera l’installation et la configuration de mariadb
- Installation et configuration de mariadb
- blog: Concernera l’installation et la configuration de wordpress
- Installation et configuration du blog
- définition de l’alias ou du vhost pour le webserver
- import de la sauvegarde si elle existe
- Mise à jour des extensions, hors RPM (les paquets RPMs seront mis à jour avec le rôle system)
- webstat: Concernera l’installation et la configuration de piwik
- Installation et configuration de piwik
- définition de l’alias ou du vhost pour le webserver
- import de la sauvegarde si elle existe
- monitoring: Concernera l’installation et la configuration du monitoring
- Installation et configuration des outils
- ids: Concernera l’installation et la configuration des outils de détection et de vérification d’intrusion
- Installation et configuration des outils
Du coup, le changement d'un rôle impactera pas ou peu les autres, et on pourra facilement changer certaines choses. Changer de blog n'impactera pas le reste par exemple, mais changer de serveur web impactera les autres rôles.
Les Playbooks
Il s'agit de fichiers au format YAML, qui contient une succession de tâche à accomplir. Par défaut, une erreur sur l'une d'entre elles entraîne l'arrêt du processus. Il existe beaucoup de modules différents et la doc les présente tous :
https://docs.ansible.com/ansible/latest/modules/modules_by_category.html
À propos du YAML :
https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
À propos des playbooks :
https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html#playbook-language-example
J'ai créé un playbook tout simple qui se contente d'installer apache2 :
#site.yml --- - hosts: server #Je choisis mon hôte tasks: - name: update installation de serveur web #Je nomme ma tâche apt: #Différentes commandes apt name: apache2 force_apt_get: yes #Sinon, il utilise aptitude update_cache: yes become: true #Sert à utiliser sudo
Que je peux ensuite lancer :
justine@Justine-pc:~/Ansible/preprod$ ansible-playbook -i ~/Ansible/preprod/hosts.ini --user user --ask-pass --ask-become-pass site.yml SSH password: SUDO password[defaults to SSH password]: PLAY [server] ****************************************************************************************************************************** TASK [Gathering Facts] ********************************************************************************************************************* ok: [192.168.1.49] TASK [update installation de serveur web] ************************************************************************************************** changed: [192.168.1.49] PLAY RECAP ********************************************************************************************************************************* 192.168.1.49 : ok=2 changed=1 unreachable=0 failed=0
Ici :
- ansible-playbook -i ~/Ansible/preprod/hosts.ini : J'utilise un playbook en précisant l'environnement
- --user user --ask-pass : utilisateur et demande de mot de passe pour ssh
- --ask-become-pass : mot de passe pour sudo
- site.yml : mon playbook
Si je relance le playbook, les tâches déjà effectuées seront ingorées. Cela évite les effets de bord : Les playbooks sont idempotents, c'est-à-dire qu'on peut bien les réutiliser si on veut ! Ici, je pourrais rajouter des fonctions à mon playbook et le relancer si j'en ai envie.
Chiffrer les variables des playbooks
Les playbooks peuvent s'avérer dangereux au niveau sécurité : si je dois donner un mot de passe à un utilisateur via un playbook, je ne vais pas par la suite le commit tel quel sur un dépôt public par la suite ! Pour ça, Ansible-vault propose deux façon de chiffrer les fichiers de variables :
- Chiffrer tout le fichier de variables et utiliser les commandes ad-hoc pour éditer le fichier
- Chiffrer seulement la valeur de la variable dans un fichier texte en clair
Chiffrer tout le fichier
On créée notre fichier chiffré et on inscrit notre variable une fois le fichier ouvert par l'éditeur par défaut (variable EDITOR) avec la commande :
justine@Justine-pc:~/Ansible/preprod$ ansible-vault create undossier/unfichierdevariables #Je peux ensuite l'éditer : justine@Justine-pc:~/Ansible/preprod$ ansible-vault edit undossier/unfichierdevariables
On peut aussi créer un fichier en clair et le chiffrer après :
echo 'mysql_root_password = "monsupermotdepasse"' > host_vars/server ansible-vault encrypt host_vars/server
Chiffrer la valeur de la variable
On chiffre la valeur :
ansible-vault encrypt_string --vault-id @prompt 'monsupermotdepasse' --name 'mysql_root_password' >> host_vars/server New vault password (default): Confirm vew vault password (default): Encryption successful
Mais je n'ai pas trop compris ce point-là...
Un playbook plus abouti
Le playbook suivant donne une idée un peu complète des possibilités offertes par l'outil, et devrait parler de lui-même.
Fichier de variables
--- name: johndoe #pubkey est un lien vers un git de type https://mongit.fr/user.keys #Il faut bien sûr avoir au préalable injecté une clef dans le git en question pubkey: https://monsuperlien.com/johndoe.keys file: /home/justine/Nextcloud/Ansible/resultat.txt inventoryfile: /home/justine/Nextcloud/Ansible/inventaire.txt
Fichier hosts
[serv] serv.innertech.ovh
Playbook
--- - hosts: nest tasks: - name: Dis bonjour debug: msg: "Hi, I will be adding a user named {{ name }} and inject his key from {{ pubkey }}" msg: "After that, the user {{ name }} shall be able to sudo without a passwd (as he won't have any)" - name: Creation de l'utilisateur user: name: "{{ name }}" shell: /bin/bash groups: sudo append: yes become: yes - name: Ajout de sudo NOPASSWD copy: content: '%sudo ALL=(ALL:ALL) NOPASSWD:ALL' dest: /etc/sudoers.d/sudo_nopasswd mode: 0440 become: yes - name: Injection de la clef publique authorized_key: user: "{{ name }}" state: present key: "{{ pubkey }}" become: yes - name: Informations sur Lynis debug: msg: "Next, we will try to install Lynis and run it" - name: Installation de Lynis apt: name: lynis force_apt_get: yes update_cache: yes become: yes - name: Lancement de Lynis et renvoi du resultat shell: lynis --no-colors audit system register: resultat args: executable: /bin/bash become: yes - name: Enregistrement du resultat de Lynis local_action: module: copy content: "{{ resultat }}" dest: "{{ file }}" - name: Recuperation d'informations setup: filter: ansible_default_ipv4.address - name: ajout de la machine à un fichier d'inventaire local_action: module: copy content: "{{ ansible_default_ipv4.address }} User: {{ name }} Pub_Key: {{ pubkey }}" dest: "{{ inventoryfile }}"