# Projet LXC-Docker-Ansible Groupe : xxx / xxx # 0 - Définiton : ### LXC LXC ou encore Linux Containers, est un système de virtualisation. Il est utilisé pour faire fonctionner des environnements Linux isolés les uns des autres dans des conteneurs partageant le même noyau. ### Docker Docker est un système d'exploitation pour l'exécution de conteneurs. Il fournit un ensemble de commandes simples pour concevoir, démarrer ou arrêter des conteneurs. ### Ansible Ansible est une application logicielle d’automatisation informatique open source en ligne de commande écrite en Python. Il peut configurer des systèmes, déployer des logiciels et orchestrer des flux de travail avancés pour prendre en charge le déploiement d’applications, les mises à jour système, etc. - **Inventaire** : Ansible fonctionne simultanément sur plusieurs nœuds gérés ou « hôtes » de votre infrastructure, en utilisant une liste ou un groupe de listes appelée inventaire. Une fois votre inventaire défini, vous utilisez des modèles pour sélectionner les hôtes ou les groupes sur lesquels Ansible doit s'exécuter. - **Playbook** : Un playbook Ansible est un modèle de tâches d'automatisation, qui sont des opérations informatiques complexes exécutées sans intervention humaine. Les playbooks Ansible sont écrits au format YAML lisible par l'homme et exécutés sur un ensemble, un groupe ou une classification d'hôtes, qui forment ensemble un inventaire. ### Jenkins Jenkins est un serveur d’automatisation open source autonome qui peut être utilisée pour automatiser toutes sortes de tâches liées à la création, au test et à la livraison ou au déploiement de logiciels. # I - Partie 1 : Automatisation avec Ansible Dans cette première partie nous allons découvrir comment déployer une infrastructure de plusieurs serveurs et réseaux avec Ansible. L'infrastructure réseau que nous essayerons de créer sera la suivante : ![](https://i.imgur.com/Ybp0NUN.jpg) Notre objectif sera d'utiliser les playbooks d'Ansible pour créer nos réseaux, nos conteneurs ainsi que l'installation de la configuration des différents services. ## A - Installation d'Ansible Pour commencer à utiliser Ansible, nous allons devoir installer le logiciel Ansible sur notre machine. Avant toute chose, nous allons exécuter les commandes suivantes pour mettre à jour notre environnement. ```bash apt update && apt full-upgrade ``` Nous allons désormais installer le package `sofware-properties` pour pouvoir ajouter le dépôt de projet d'ansible à notre machine. ```bash apt install software-properties-common -y ``` Depuis notre machine, exécutez la commande suivante pour inclure le PPA (archive personnelle de packages) du projet officiel dans la liste des sources de votre système ```bash add-apt-repository --yes --update ppa:ansible/ansible ``` Nous allons désormais pouvoir installer Ansible sur notre machine. ```bash apt install ansible ``` Maintenant que nous avons ansible nous allons pouvoir créer les Playbooks et les inventaires nécessaires au déploiement de notre infrastructure serveur. ## B - Inventaire Pour exécuter nos playbooks, nous aurons besoin d'un inventaire qui contiendra les différents groupes, noms et variables nécessaires pour exécuter les différentes commandes ansible du playbook. Nous allons donc modifier le fichier `/etc/ansible/hosts` avec la commande suivante : ```bash nano /etc/ansible/hosts ``` On ajoutera le contenu suivant à l'intérieur du fichier. ```ini [all:vars] networkNet1=10.1.0 networkName1=dmz1 networkNet2=10.2.0 networkName2=dmz2 [webservers] web1 inventory_host_ip=10 web2 inventory_host_ip=20 [haproxy] proxy inventory_host_ip=30 [backup] storage inventory_host_ip=40 [databases] mariadb inventory_host_ip=50 ``` Revenons maitenant sur le contenu du fichier : - `[all:vars]`: - `:vars` permet de créer des variables spécifique à un groupe, ici nous spécifions all pour créer des variables globales. - Nous créerons ici 4 variables spécifiant le nom et l'adresse des réseaux qui seront créés. - `[group]`: - Pour le reste du fichier nous créerons des groupes pour chaque type de services que nous installerons. Ces groupes contiendront la liste de toutes les machines qui auront ce service. - Nous ajouterons également une variable `inventory_host_ip` à chaque conteneur correspondant à la partie hôte de l'ip des machines sur les différents réseaux. ## C - Déploiement des machines virtuelles Maintenant que nous avons notre inventaire, nous allons pouvoir passer à la création de nos playbooks. Pour plus de flexibilité, nous allons créer 3 playbooks qui auront pour tâche de : - Créer nos réseaux et nos conteneurs - Installer et configurer les services sur les conteneurs - Executer les deux autres playbooks à la suite Nous commencerons donc par créer un premier playbook `playbook_deploy.yml` avec la commande suivante : ```bash nano playbook_deploy.yml ``` Comme le playbook est particulièrement long, nous diviserons la description de celui-ci en plusieurs parties. ### 1 - Configuration de l'hôte Nous commencerons par ajouter ce premier bloc à notre playbook. ```yaml - name: Host configuration hosts: localhost tasks: - name: Install LXC packages apt: name={{ item }} state=present loop: [ 'lxc', 'python3-lxc', 'snapd' ] - name: Install LXD snap: name: lxd - name: Generate SSH key openssh_keypair: path: "~/.ssh/id_rsa" type: rsa size: 4096 state: present force: no ``` Dans ce premier bloc, nous spécifierons `localhost` en tant qu'hosts pour exécuter directement des commandes sur notre machine hôte. Tâches : - *`Install LXC packages`* La première étape sera de s'assurer que LXC est bien installé pour créer nos conteneurs avec le module `apt`. - Nous installerons également le package `python3-lxc` pour que Ansible puisse utiliser LXC. - Pour simplifier la création de réseau nous allons utiliser LXD, nous installerons donc `snapd` pour pouvoir l'installer utlérieurment. - *`Install LXD`* Nous nous assurons ensuite également que LXD est bien installé avec le module `snap`. - *`Generate SSH key`* Enfin pour se connecter en SSH à nos différentes machines, nous aurons besoin d'une paire de clés, nous générerons donc une paire de clés si aucune n'est présente sur la machine. - Le paramètre `force` à la valeur `no` va permettre d'éviter de remplacer la clé si celle-ci existe. ### 2 - Création des réseaux Nous allons maintenant ajouter un nouveau bloc à notre playbook. ```yaml - name: Network configuration hosts: localhost tasks: - name: Create new interfaces shell: | if [ -z $(ip -o -4 a | awk '/{{ item.int }}/ {print $2}') ]; then lxc network create {{ item.int }} ipv4.address={{ item.net }}.1/24 ipv4.nat=true fi loop: - { 'int': '{{ networkName1 }}', 'net': '{{ networkNet1 }}' } - { 'int': '{{ networkName2 }}', 'net': '{{ networkNet2 }}' } ``` Nous spécifierons encore `localhost` en tant qu'hosts pour exécuter directement des commandes sur notre machine hôte. Tâches : - *`Create new interfaces`* Comme il n'existe aucun module LXD pour Ansible nous allons devoir exécuter directement un bloc de commande shell pour créer notre réseau. - Nous utiliserons la commande `lxc network create` pour créer notre réseau. - Le paramètre `ipv4.address` pemet de spécifier l'adresse du réseau. - Le paramètre `ipv4.nat=true` permet un internet NAT au réseau. - Pour éviter de créer un réseau si celui-ci existe déjà, nous plaçons notre commande dans un bloc `if`, chargé de vérifier qu'il n'existe pas. - Pour créer nos deux réseaux à partir de la même tâche nous allons utiliser la clause `loop`. - Nous spécifierons dans notre loop des dictionnaires avec le nom et l'adresse de notre interface que l'on récupéra directement depuis notre inventaire avec les variables `networkName1`, `networkNet1`,`networkName2` et `networkNet2`. ### 3 - Création des conteneurs Nous allons maintenant ajouter le bloc principale de notre playbook. ```yaml - name: Creation of containers hosts: all connection: local tasks: - name: Create webserver containers when: inventory_hostname in groups["webservers"] lxc_container: name: "{{ inventory_hostname }}" container_log: true template: download state: started template_options: --dist ubuntu --release jammy --arch amd64 container_config: - "lxc.net.0.link = {{ networkName1 }}" - "lxc.net.0.ipv4.address = {{ networkNet1 }}.{{ inventory_host_ip }}/24" - "lxc.net.0.ipv4.gateway = {{ networkNet1 }}.1" - "lxc.net.1.type = veth" - "lxc.net.1.flags = up" - "lxc.net.1.link = {{ networkName2 }}" - "lxc.net.1.ipv4.address = {{ networkNet2 }}.{{ inventory_host_ip }}/24" - name: Create haproxy containers when: inventory_hostname in groups["haproxy"] lxc_container: name: "{{ inventory_hostname }}" container_log: true template: download state: started template_options: --dist ubuntu --release jammy --arch amd64 container_config: - "lxc.net.0.ipv4.address = 10.0.3.{{ inventory_host_ip }}/24" - "lxc.net.0.ipv4.gateway = 10.0.3.1" - "lxc.net.1.type = veth" - "lxc.net.1.flags = up" - "lxc.net.1.link = {{ networkName1 }}" - "lxc.net.1.ipv4.address = {{ networkNet1 }}.{{ inventory_host_ip }}/24" - name: Create backup containers when: inventory_hostname in groups["backup"] lxc_container: name: "{{ inventory_hostname }}" container_log: true template: download state: started template_options: --dist ubuntu --release jammy --arch amd64 container_config: - "lxc.net.0.link = {{ networkName2 }}" - "lxc.net.0.ipv4.address = {{ networkNet2 }}.{{ inventory_host_ip }}/24" - "lxc.net.0.ipv4.gateway = {{ networkNet2 }}.1" - name: Create databases containers when: inventory_hostname in groups["databases"] lxc_container: name: "{{ inventory_hostname }}" container_log: true template: download state: started template_options: --dist ubuntu --release jammy --arch amd64 container_config: - "lxc.net.0.link = {{ networkName2 }}" - "lxc.net.0.ipv4.address = {{ networkNet2 }}.{{ inventory_host_ip }}/24" - "lxc.net.0.ipv4.gateway = {{ networkNet2 }}.1" - name: Remove old line in configuration file lineinfile: dest: /var/lib/lxc/{{ inventory_hostname }}/config state: absent regexp: '^lxc.net.0.link = lxcbr0$' when: inventory_hostname not in groups["haproxy"] - name: Add ip to /etc/hosts shell: | sed -i '/.*{{ inventory_hostname }}$/d' /etc/hosts echo $(grep -oP "(lxc.net.0.ipv4.address = )\K([0-9]{1,3}\.){3}[0-9]{1,3}" /var/lib/lxc/{{ inventory_hostname }}/config) {{ inventory_hostname }} >> /etc/hosts ``` Nous spécifierons cette fois-ci `all` en tant qu'hosts pour exécuter les commandes pour toutes les machines de notre inventaire. Néanmoins nous souhaitons exécuter les commandes non pas sur les machines mains sur notre machine hôte, nous allons spécifier `connection: local`. > PS: Il également possible d'arriver à ce résultat en spécifiant `delegate_to: localhost` sur chacunes des tâches au lieu d'utiliser `connection: local`. Tâches : - *`Create [group] containers`* Nos 5 premières tâches sont chargées de créers nos conteneurs LXC avec le module `lxc_container`, nous séléctionnerons chaque groupe indépendaments avec une close `when` en vérifiant si le nom de la machine appartient au groupe. - Avec `template` et `template_options` nous choisissons d'installer Ubuntu 22.04. - Avec `container_config` nous allons ajouter la configuration réseau appropriés. - Nous utiliserons les variables `networkName`, `networkNet`, `inventory_host_ip` pour construire l'ip de chacune des machines à partir de notre inventaire. - *`Remove old line in configuration file`* Comme le fichier de configuration des conteneurs LXC ne possède pas de format particuliers, il n'est pas possible de mettre à jour le fichier en fonction des clés. - Nous allons supprimer la ligne `lxc.net.0.link = lxcbr0` de la configuration d'origine des conteneurs avec le module `lineinfile`. - Nous exclurerons le groupe haproxy avec la clause `when` qui conserve l'interface lxcbr0. - *`Add ip to /etc/hosts`* Notre dernière tâche va se charger d'ajouter la résolution du nom de la machine à son ip via le fichier /etc/hosts. - Cette fois-ci le module `inlinefile` n'est pas suffisant car nous devons utiliser une commande shell pour récupérer l'ip de chacune des machines. - La commande `sed` pour supprimer une éventuelle ancienne ligne pour le même hôte. - La commande `echo` pour ajouter la nouvelle entrée dans le fichier. - Nous recuperons l'ip de la machine à partir du fichier de configuration du conteneur à l'aide de la commande `grep`. ### 4 - Configuration de SSH, de la résolution DNS, et désactivation du DHCP. Nous allons maintenant ajouter un autre bloc à notre playbook pour configurer nos conteneurs. ```yaml - name: SSH, DNS, DHCP Configuration hosts: all connection: local tasks: - name: Configure SSH, DNS and DHCP lxc_container: name: "{{ inventory_hostname }}" container_command: | if [ ! -d ~/.ssh ]; then mkdir ~/.ssh echo "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" | tee -a ~/.ssh/authorized_keys fi if [ -z $(grep "nameserver 1.1.1.1" /etc/resolv.conf) ]; then echo nameserver 1.1.1.1 >> /etc/resolv.conf apt install openssh-server resolvconf -y echo nameserver 1.1.1.1 >> /etc/resolvconf/resolv.conf.d/head fi sed -i 's/dhcp4: true/dhcp4: false/g' /etc/netplan/10-lxc.yaml - name: Restart container lxc_container: name: "{{ inventory_hostname }}" state: restarted ``` Nous allons utiliser le même `hosts` ainsi qu'une connection `local` pour ce bloc. > PS: Les tâches de ce bloc auraient pu être ajouté au bloc précédent mais pour plus de lisibilité, j'ai fait le choix de le séparer en deux bloc distincts. Tâches : - *`Configure SSH, DNS and DHCP`* Comme nous n'avons pas de connection SSH, nous allons utiliser le module `lxc_container` pour executer des commandes sur nos conteneurs. - Les deux premières commandes seront permettrent de recupérer la clé publique de notre machine avec la commande `lookip` et l'ajouter au fichier `authorized_keys`. - Nous ajouterons ensuite une entrée DNS dans le fichier `/etc/resolv.conf` pour s'assurer de pouvoir résoudre les noms de domaines - Nous allons ensuite télecharger `openssh-server` et `resolvconf`. - Nous ajouterons ensuite notre entrée DNS dans le fichier `/etc/resolvconf/resolv.conf.d/head` pour rendre l'entrée permanente. - Enfin on désactive le DHCP sur notre machine pour éviter d'ajouter une deuxième adresse ip à nos interfaces. - *`Restart container`* Une fois toutes les commandes executées, nous allons redémarrer le conteneur, avec le module `lxc_container`, pour appliquer tous les changements des différents services et retirer les adresses DHCP. ### 5 - Mise à jour des empreintes des clés des conteneurs Nous allons maintenant ajouter un autre bloc à notre playbook pour ajouter les empreintes des clés ```yaml - name: Update hosts fingerprints hosts: all connection: local serial: 1 tasks: - name: Wait for port 22 to become open wait_for: host: "{{ inventory_hostname }}" port: 22 - name: Container key is up-to-date locally shell: | ssh-keygen -R {{ inventory_hostname }} ssh-keyscan -H {{ inventory_hostname }} >> ~/.ssh/known_hosts ``` Nous allons utiliser le même `hosts` ainsi qu'une connection `local` pour ce bloc. Néanmoins nous allons également utiliser `serial: 1` pour indiquer de jouer la liste les tâches sur les hôtes chacun leurs tours. Cela nous permettra d'utiliser wait_for indépendament sur chacun des hôtes. Tâches: - *`Wait for port 22 to become open`* Nous allons indiquer à notre playbook d'attendre jusqu'à ce que le serveur SSH démarre correctement sur l'hôte, à l'aide de le module `wait_for`. - Le paramètre `port` permet d'indiquer le port à attendre, par défaut SSH utilise le port 22. - *`Container key is up-to-date locally`* Nous allons ensuite mettre à jour les signatures des hôtes dans le fichier `known_hosts` de notre machine locale. - La commande `ssh-keygen -R` va retirer la signature de l'hôte du fichier. - La commande `ssh-keyscan -H` va ajouter la nouvelle signature de l'hôte au fichier. ### 6 - Installation de python Nous allons maintenant ajouter un dernier bloc à notre playbook pour installer python sur nos conteneurs. ```yaml - name: Install python hosts: all tasks: - name: Install python apt: name=python3 update_cache=yes state=latest ``` Nous allons enfin pouvoir retirer le `connection: local` car nos hôtes sont désormais accessibles depuis une connexion SSH. Tâches: - *`Install python`* Nous allons installer `python3` sur nos hôtes à l'aide de le module `apt`. ### 7 - Lancement du premier playbook Notre premier playbook est désormais complet. Nous pouvons essayer de le lancer avec la commande suivante : ```bash ansible-playbook -i /etc/ansible/hosts ./playbook_deploy.yml ``` La sortie devrait normalement être la suivante: ![](https://i.imgur.com/vnfTyEV.png) Après l'execution de cette commande, notre infrastructure a été créé et prête à être utilisée par Ansible. ## D - Configuration des serveurs Maintenant que nous avons nos conteneurs opérationnels, nous allons pouvoir créer notre deuxième playbook `playbook_install.yml` avec la commande suivante : ```bash nano playbook_install.yml ``` Comme le premier playbook, nous diviserons la description de celui-ci en plusieurs parties. ### 1 - Installation des serveurs web Nous commencerons par ajouter un premier bloc à notre playbook pour configurer nos serveurs web ```yaml - name: Web servers configuration hosts: webservers tasks: - name: Install apache and php apt: name: "{{ item }}" update_cache: yes state: latest loop: [ 'apache2', 'libapache2-mod-php', 'php', 'rsync', 'perl', 'php-ldap', 'php-imap', 'php-apcu', 'php-xmlrpc', 'php-cas', 'php-mysqli', 'php-mbstring', 'php-curl', 'php-gd', 'php-simplexml', 'php-xml', 'php-intl', 'php-zip', 'php-bz2' ] - name: User creation user: name: btc shell: /usr/bin/bash ``` Nous spécifirons `webservers` en tant que hosts pour executer les commandes uniquement sur les machines du groupe webservers de notre inventaire. Tâches : - *`Install apache and php`* Nous allons installer avec le module `apt`, tous les packages nécessaire pour faire fonctionner un site web. - Le paramètre `update_cache` avec la valeur `yes` permet d'executer `apt update` avant d'installer les packages. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. - La clause `loop` nous permet de lister tous les packages qui seront installer avec apt. - *`User creation`* Nous allons créer un utilisateur `btc` avec le module `user`. - Le paramètre `name` permet de choisir le nom de l'utilisateur. - Le paramètre `shell` permet de choisir le shell attribué à l'utilisateur. ### 2 - Installation du serveur de base de données MariaDB Nous allons ajouter un nouveau bloc à notre playbook pour configurer nos serveurs de bases de données ```yaml - name: Database servers configuration hosts: databases tasks: - name: Install mariadb-server apt: name: mariadb-server update_cache: yes state: latest ``` Nous spécifirons `databases` en tant que hosts pour executer les commandes uniquement sur les machines du groupe databases de notre inventaire. Tâches : - *`Install mariadb-server`* Nous allons installer la package `mariadb-server` à l'aide module `apt` pour faire fonctionner nos serveurs de bases de données. - Le paramètre `update_cache` avec la valeur `yes` permet d'executer `apt update` avant d'installer les packages. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. ### 3 - Installation du serveur Haproxy Nous allons ajouter un nouveau bloc à notre playbook pour configurer nos serveurs de reverses proxy. ```yaml - name: Reverse proxy servers configuration hosts: haproxy tasks: - name: Install haproxy apt: name: haproxy state: present - name: Configure haproxy conf file template: src: haproxy.cfg dest: /etc/haproxy/haproxy.cfg - name: Restart haproxy service: name: haproxy state: restarted enabled: yes ``` Nous spécifirons `haproxy` en tant que hosts pour executer les commandes uniquement sur les machines du groupe haproxy de notre inventaire. Tâches : - *`Install haproxy`* Nous allons installer la package `haproxy` à l'aide module `apt` pour faire fonctionner nos serveurs de bases de données. - Le paramètre `update_cache` avec la valeur `yes` permet d'executer `apt update` avant d'installer les packages. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. - *`Configure haproxy conf file`* Nous allons utiliser le module template pour copier le fichier de configuration d'haproxy local dans notre hôte. - *`Restart haproxy`* Nous allons redémarrer le service `haproxy` à l'aide module `service` pour appliquer la configuration. - Le paramètre `enabled` avec la valeur `yes` permet d'activer le service au démarrage. ### 4 - Connection entre le serveur de backup et les serveurs webs Nous allons ajouter un nouveau bloc à notre playbook pour configurer la connection entre nos serveurs de backup et web. ```yaml - name: SSH connection bewteen backup and webservers hosts: backup, webservers tasks: - name: Generate SSH key id_rsa openssh_keypair: path: "~/.ssh/id_rsa" type: rsa size: 4096 state: present force: no when: inventory_hostname in groups["backup"] - name: Fetch the public key from the node to host fetch: src: "~/.ssh/id_rsa.pub" dest: "buffer/backup-id_rsa.pub" flat: yes when: inventory_hostname in groups["backup"] - name: Copy the key add to authorized_keys authorized_key: user: root state: present key: "{{ lookup('file','buffer/backup-id_rsa.pub')}}" when: inventory_hostname in groups["webservers"] - name: Container key is up-to-date locally shell: | ssh-keygen -R {{ item }}; ssh-keyscan -H {{ networkNet2 }}.{{ hostvars[item].inventory_host_ip }} >> ~/.ssh/known_hosts loop: "{{ query('inventory_hostnames', 'webservers') }}" when: inventory_hostname in groups["backup"] ``` Nous spécifirons `backup, webservers` en tant que hosts pour que l'on puisse executer des commandes sur les deux groupes. Tâches : - *`Generate SSH key id_rsa`* Nous allons générer une paire de clé sur le rsa pour mettre en place la connexion vers nos serveurs webs. - Le paramètre `force` à la valeur `no` va permettre d'éviter de remplacer la clé si celle-ci existe. - La clause `when` permet d'executer cette commande uniquement sur les hôtes appartenant au groupe backup. - *`Fetch the public key from the node to host`* Nous allons ensuite télecharger la clé publique vers notre machine local à l'aide du module `fetch`. - Le paramètre `flat` à la valeur `yes` permet de désactiver l'ajout du nom d'hôte à la fin du fichier source. - La clause `when` permet d'executer cette commande uniquement sur les hôtes appartenant au groupe backup. - *`Copy the key add to authorized_keys`* .Nous allons ajouter la clé publique copier localement au fichier `authorized_keys` des hôtes à l'aide du module `authorized_key`. - Le paramètre `user` permet de s'assurer que la clé est ajouter au fichier de l'utilisateur `root`. - La clause `when` permet d'executer cette commande uniquement sur les hôtes appartenant au groupe webservers. - *`Container key is up-to-date locally`* Nous allons ensuite mettre à jour les signatures des hôtes dans le fichier `known_hosts` des hôtes du group backup. - La commande `ssh-keygen -R` va retirer la signature de l'hôte du fichier. - La commande `ssh-keyscan -H` va ajouter la nouvelle signature de l'hôte au fichier. - La clause `when` permet d'executer cette commande uniquement sur les hôtes appartenant au groupe backup. ### 5 - Installation du backup Nous allons ajouter un dernier bloc à notre playbook pour configurer notre serveur de backup ```yaml - name: Backup servers configuration hosts: backup tasks: - name: Install packages apt: name: "{{ item }}" update_cache: yes state: latest loop: [ 'cron', 'rsync', 'iptables', 'fail2ban' ] - name: Mount html folder synchronize: src: html/ dest: /root/html recursive: true - name: Add cron scheduled cron: name: "Sync {{ item }}" state: present minute: "*/5" hour: "*" day: "*" month: "*" weekday: "*" job: 'rsync -Pqr /root/html/ root@{{ networkNet2 }}.{{ hostvars[item].inventory_host_ip }}:/var/www/html' loop: "{{ query('inventory_hostnames', 'webservers') }}" - name: Add fail2ban config copy: content: | [sshd] enabled = true port = ssh filter = sshd bantime = 604800 maxretry = 5 backend = systemd dest: /etc/fail2ban/jail.local owner: root group: root mode: 0644 - name: Restart fail2ban service: name: fail2ban state: restarted enabled: yes ``` Nous spécifirons `backup` en tant que hosts pour executer les commandes uniquement sur les machines du groupe backup de notre inventaire. Tâches : - *`Install packages`* Nous allons installer les différents packages nécessaire à la synchronisation des fichiers et au fonctionnement de fail2ban à l'aide module `apt`. - Le paramètre `update_cache` avec la valeur `yes` permet d'executer `apt update` avant d'installer les packages. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. - La clause `loop` nous permet de lister tous les packages qui seront installer avec apt. - *`Mount html folder`* Nous allons ensuite ajouter le dossier html local à l'intérieur de notre hôte à l'aide du module `synchronize` - Le paramètre `recursive` avec la valeur `true` permet de copier recursivement le contenu du dossier synchroniser. - *`Add cron scheduled`* Nous allons ajouter une tâche cron à notre machine à l'aide du module `cron`. - Les paramètres `minute`, `hour`, `day`, `month`, `weekday` indiquent la fréquence à laquelle executer la commande passer au paramètre `job`. - Le paramètre `job` indique la commande a executer par la tâche cron. - La clause `loop` nous permet d'ajouter une tâche pour chacun des hôtes du group webservers. - *`Mount fail2ban local config`* Nous allons ajouter le fichier de configuration de fail2ban à l'aide du module `copy`. - Le paramètre `content` permet de copier depuis un texte au lieu d'un fichier. - Les paramètres `owner`, `group`, `mode` nous permettent de spécifier le propriétaire et les droits au fichier de destination. - *`Restart fail2ban`* Nous allons redémarrer le service `fail2ban` à l'aide module `service` pour appliquer la configuration. - Le paramètre `enabled` avec la valeur `yes` permet d'activer le service au démarrage. ### 6 - Lancement du deuxième playbook Notre deuxième playbook est désormais complet. Nous pouvons essayer de le lancer avec la commande suivante : ```bash ansible-playbook -i /etc/ansible/hosts ./playbook_install.yml ``` La sortie devrait normalement être la suivante: ![](https://i.imgur.com/6TJRcpl.png) Après l'execution de cette commande, notre infrastructure a désormais été configuré et prête à être utilisée en production. ### 6 - Lancement du troisième playbook Nous allons créer un troisième playbook qui aura pour objectif de lancer nos deux autres playbooks. Nous commencerons donc par créer un troisième playbook `playbook_main.yml` avec la commande suivante : ```bash nano playbook_main.yml ``` Nous n'ajouterons que les 2 lignes suivantes pour importer nos deux playbooks. ```yaml - import_playbook: playbook_deploy.yml - import_playbook: playbook_install.yml ``` Nous pouvons désormais essayer de lancer ce dernier playbook avec la commande suivante : ```bash ansible-playbook -i /etc/ansible/hosts ./playbook_main.yml ``` La sortie devrait normalement être la suivante: ![](https://i.imgur.com/07siUTX.png) Nous en navons terminer avec Ansible et nous désormais en capacité de déployer toute notre infrastructure ainsi que sa configuration depuis une simple commande ansible. # II - Partie 2 : Orchestration ## A - Installation de Jenkins Pour installer Jenkins nous allons passer par docker, pour cela vous allez tout d'abord créer le dossier d'accueil pour les différents fichier de configuration Jenkins. Positionner vous d'abord a la racine de votre dossier utilisateur ```bash cd ~ ``` Puis créer le dossier d'acceuil pour jenkins. ```bash mkdir /var/jenkins_home ``` Attribuer lui ensuite des droits de lecture, d'écrituer et d'execution. ```bash= chmod 777 /var/jenkins_home/ -R ``` Créer ensuite le conteneur ```bash= docker run -p 8080:8080 -d -v /var/jenkins_home:/var/jenkins_home jenkins/jenkins ``` Vérifier ensuite la bonne création de votre conteneur. ```bash= docker ps ``` Déplacer vous ensuite dans le dossier "secrets" de Jenkins pour récupérer votre clé d'authentification pour l'interface graphique. ```bash= cd var/jenkins_home/secrets ``` Ici récupérer votre clé. ```bash= cat initialAdminPassword ``` Puis connectez vous a votre adresse ip sur votre navigateur en précisant le port attribuer durant l'installation de Jenkins. ![](https://i.imgur.com/xHWTvuB.png) Vous devriez arriver sur cette page la, vous devez désormais utiliser la clé administrateur pour démarrer Jenkins. Puis démarrer l'installation en selectionnant les packages les plus apprécier par la communauté. ![](https://i.imgur.com/gshw9uO.png) Une fois l'installation terminé créer votre premier utilisateur. ![](https://i.imgur.com/Nn8psOE.png) Valider l'URL de Jenkins. ![](https://i.imgur.com/sDPXHet.png) Puis votre Jenkins est prêt ! Vous pouvez commencer a l'utiliser. ![](https://i.imgur.com/e3cToI3.png) ## B - Création d'un agent Jenkins Désormais nous allons créer un agent Jenkins dans le but de faire fonctionner notre Job grâce a ce dernier. Tout d'abord rendez vous sur le tableau de bord Jenkins. Puis cliquez sur "Administrer Jenkins". ![](https://i.imgur.com/VNVKStd.png) Descendez dans la section "System Configuration" et cliquez sur "Nodes and Clouds". ![](https://i.imgur.com/SArzshj.png) Et cliquez ensuite sur "New Node" ![](https://i.imgur.com/kvsXKFS.png) Attribuer un nom a votre nouveau node puis attribuer lui le type "Permanent Agent". ![](https://i.imgur.com/GNZhWQB.png) Attribuer lui un répertoire de travail afin qu'il execute ces commandes sur ce dernier. ![](https://i.imgur.com/SsI9a4r.png) Laissez les utilisation par défaut et attribuer la méthode de lancement en "Launch agents via SSH". ![](https://i.imgur.com/GFtNpRW.png) Récuperer l'adresse ip de votre conteneur docker qui fais tourner Jenkins puis attribuer la en Host. ![](https://i.imgur.com/pb82L9K.png) Le but est désormais d'ajouter un "Credentials" cliquez sur ajouter puis "Jenkins". ![](https://i.imgur.com/PjFsQwL.png) Configurer le type de connexion en "SSH Username with private key", puis le Username "root". ![](https://i.imgur.com/n3SRF7o.png) Avant de continuer cette section nous allons d'abord configurer générer une clé SSH. Pour cela tapez cette commande. ```bash= ssh-keygen -t rsa -b 4096 -N "" -f /root/.ssh/id_rsa ``` Puis copiez la nouvelle clé dans le dossier authorized_keys. ```bash= cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys ``` Ensuite récupérer et copiez votre nouvelle clé SSH. ```bash= cat ~/.ssh/id_rsa ``` Dans la section "Private Key" selectionnez "Enter directly" et copiez votre clé publique du dosser ~/.ssh/id_rsa ![](https://i.imgur.com/ccN3tKe.png) Ajouter votre nouveau credentials puis paramétrez le "Host Key Verification Strategy" en "Non verifying verification strategy". ![](https://i.imgur.com/stauLJi.png) Puis cliquez sur "Ajouter". ![](https://i.imgur.com/3fsytEf.png) Vérfiez ensuite l'état de votre agent en accédant a la page de statut de ce dernier. Puis cliquez sur "Launch Agent". ![](https://i.imgur.com/c6GoAvl.png) Cliquez ensuite sur la section "Journal" et vérifier la bonne connection a l'agent. ![](https://i.imgur.com/nQoSpDz.png) Votre nouvel agent est désormais configurer et nous pouvons y rattacher nos futurs job. ## C - Création du premier job Jenkins #### 1 - Création d'un premier job Démo Rendez vous sur la page d'acceuil de Jenkins puis cliquez sur "Créer un job". ![](https://i.imgur.com/oaxAwmY.png) Attribuer lui ensuite le nom que l'on souhaite ici "Demo". ![](https://i.imgur.com/0Yumt9H.png) Et Cliquez sur " Construire un projet en free-style". Accédez ensuite a la section configuration du job que vous venez de créer. Attribuez lui une description. ![](https://i.imgur.com/V8GY3i5.png) Pour le moment nous n'utiliserons pas de gestion de code source avec git donc laissez le par défaut sur "Aucune". ![](https://i.imgur.com/7J4nbKj.png) Cliquez ensuite sur "Build step" et selectionner " Executer un script en shell ". ![](https://i.imgur.com/LwtOOdU.png) Ici nous effectuerons executerons une commande simple en sheel pour créer notre conteneur LXC. Puis nous lui installerons SSH. ```bash hostname=demo if [ ! -d /var/lib/lxc/demo ]; then lxc-create -t download -n $hostname -- -d ubuntu -r jammy -a amd64 lxc-start -n $hostname && lxc-wait -n $hostname -s RUNNING && sleep 2 lxc-attach -n $hostname -- apt install openssh-server -y fi ``` Voici les commandes nécéssaire, cliquez ensuite sur "Sauver". ![](https://i.imgur.com/TYjqw89.png) Désormais il faut s'assurer du bon fonctionnement du job, pour cela rendez vous dans la section "sortie de la console" de votre build, puis assurez vous du succes de l'opération. ![](https://i.imgur.com/y2Wae0l.png) Nous pouvons constater que notre premier job Demo a fonctionné nous pouvons désormais passer au second. ### 2 - Création du deuxième job Statique Répéter les étapes précédentes jusqu'a la création d'un nouveau job. Cependant avant la création de ce job, veuillez installer le plugin ansible. Pour cela rendez vous dans la section "administrer jenkins" puis "Plugins". ![](https://i.imgur.com/mwPvC6q.png) Puis cliquez sur "Available plugins". ![](https://i.imgur.com/cmmONgA.png) Tapez ansible dans la barre de recherche puis cochez "Ansible" et cliquez sur "installer whitout restart". ![](https://i.imgur.com/KD2Tbzl.png) Vous pouvez également installer le package "Ansicolor" pour permettre l'utilisation de couleur lors de l'exécution des tâches Ansible. ![](https://i.imgur.com/41X2s3P.png) Puis ajouter une première étape au build de type shell avec le contenu suivant. ```bash hostname=statique if [ ! -d /var/lib/lxc/$hostname ]; then lxc-create -t download -n $hostname -- -d ubuntu -r jammy -a amd64 echo " lxc.net.0.ipv4.address = 10.0.3.100/24 lxc.net.0.ipv4.gateway = auto " >> /var/lib/lxc/$hostname/config lxc-start -n $hostname && lxc-wait -n $hostname -s RUNNING && sleep 2 lxc-attach -n $hostname -- apt install openssh-server python3 -y lxc-attach -n $hostname -- mkdir ~/.ssh lxc-attach -n $hostname -- \ bash -c "echo $(cat ~/.ssh/id_rsa.pub) >> ~/.ssh/authorized_keys" ssh-keygen -R 10.0.3.100 ssh-keyscan -H 10.0.3.100 >> ~/.ssh/known_hosts fi ``` Puis ajouter une deuxième étape au build : Invoke Ansible Playbook ![](https://i.imgur.com/Z8ELdEp.png) Le contenu du playbook aura le contenu suivant : ```yml - name: Test hosts: 10.0.3.100 tasks: - ping: ``` Nous pouvons désormais lancer notre job. Si nous jetons un coup d'oeil au log, nous pouvons voir que notre job, c'est executé sans problème et affiche le status `SUCCESS`. ![](https://i.imgur.com/Ogw2tXy.png) ### 3 - Création du troisième job "parametred" Répéter les étapes précédentes jusqu'a la création d'un nouveau job. Nous commencerons par ajouter une première étape au build `Script Shell` avec le contenu suivant. ```bash hostname=parametred if [ ! -d /var/lib/lxc/$hostname ]; then lxc-create -t download -n $hostname -- -d ubuntu -r jammy -a amd64 echo " lxc.net.0.ipv4.address = 10.0.3.110/24 lxc.net.0.ipv4.gateway = auto " >> /var/lib/lxc/$hostname/config lxc-start -n $hostname && lxc-wait -n $hostname -s RUNNING && sleep 2 lxc-attach -n $hostname -- apt install openssh-server python3 -y lxc-attach -n $hostname -- mkdir ~/.ssh lxc-attach -n $hostname -- \ bash -c "echo $(cat ~/.ssh/id_rsa.pub) >> ~/.ssh/authorized_keys" ssh-keygen -R 10.0.3.110 ssh-keyscan -H 10.0.3.110 >> ~/.ssh/known_hosts echo 10.0.3.110 > /root/project/inventory_parametred.ini fi ``` Nous ajouterons ensuite une deuxième étape au build : `Invoke Ansible Playbook` - Playbook Path: /root/project/playbook_parametred.yml Le contenu du playbook aura le contenu ci-dessous, il se limitera à tester le bon fonctionnement du conteneur. ```yml - name: Test hosts: all tasks: - ping: ``` Nous sélectionnerons également `File or host` en tant qu'inventaire avec le chemin vers l'inventaire créer par le script à la première étape : `/root/project/inventory_parametred.ini` Nous pouvons désormais lancer notre job. Si nous jetons un coup d'oeil au log, nous pouvons voir que notre job, c'est executé sans problème et affiche le status `SUCCESS`. ![](https://i.imgur.com/UPdKQsR.png) ### 4 - Création du quatrième job "deploy_and_install" Pour ce dernier job, nous reprenderons le playbook et l'inventaire créer lors de la première partie. Nous commencerons par ajouter une étape `Invoke Ansible Playbook` au build. Nous ajouterons le contenu suivant pour le chemin du playbook et l'inventaire : (Pensez à changer les chemins suivants si-nécessaire) ![](https://i.imgur.com/nUEbYvu.png) Nous pouvons désormais lancer notre job. Si nous jetons un coup d'oeil au log, nous pouvons voir que notre job, c'est executé sans problème et affiche le status `SUCCESS`. ![](https://i.imgur.com/MKwj6uA.png) # Partie 3 - Gestion des évènements ## A - Création du projet Github Tout d'abord il nous faut créer un repo GitHub. Pour cela rendez vous sur le site de GitHub et connectez vous. ![](https://i.imgur.com/8wUi0UP.png) Une fois sur cette interface cliquez sur "Repository". ![](https://i.imgur.com/Jp8bpSp.png) Cliquez sur "New". ![](https://i.imgur.com/kOq3N01.png) Puis nommer votre projet et ajouter lui un fichier "Readme". ![](https://i.imgur.com/536HfUy.png) Puis vous devriez ensuite arrivé sur cette page là, signe que votre repo est bel et bien créer. ![](https://i.imgur.com/cPhR8iq.png) Nous allons ensuite pouvoir push notre projet web dans notre repository. Celui ne contiendra qu'un fichier `index.php` contenant notre application web ainsi qu'un fichier `test.php` contenant les tests unitaires de l'application. ## B - Création du job Maintenant il faut lier notre nous allons créer un job Jenkins que nous connecterons à notre compte Github. Rendez vous sur Jenkins puis cliquez sur "Nouveau Item". ![](https://i.imgur.com/njFZDCa.png) Nommer votre projet puis selectionner "Projet free-style". ![](https://i.imgur.com/wCTUhi3.png) La première chose à faire est de sélectionner dans les options générale l'option `GitHub project` puis ajouter l'URL web du repository GitHub. ![](https://i.imgur.com/b4zIgbj.png) Maintenant, dans la section `Gestion du code source`, sélectionner l'option `Git`. ![](https://i.imgur.com/lh7UlAb.png) Ajouter une nouvelle fois l'URL de votre repository GitHub. ![](https://i.imgur.com/HXPELVS.png) Changer également la branche sur laquel votre projet ce trouve votre projet (par défaut: \*/main or \*/master) ![](https://i.imgur.com/FINuIiK.png) Nous allons ensuite sélectionner `GitHub hook trigger for GITScm polling` dans la section `Ce qui déclanche le build`. ![](https://i.imgur.com/O4Gs5ZH.png) Enfin, nous allons ajouter une étape `Exécuter un script shell` à notre build avec le contenu ci-dessous. ```bash if [ -d /var/lib/lxc/webA ]; then new=webB; newIP=210; old=webA; oldIP=200; else new=webA; newIP=200; old=webB; oldIP=210; fi if [ ! -d /var/lib/lxc/$new ]; then lxc-create -t download -n $new -- -d ubuntu -r jammy -a amd64 echo " lxc.net.0.ipv4.address = 10.0.3.$newIP/24 lxc.net.0.ipv4.gateway = auto " >> /var/lib/lxc/$new/config lxc-start -n $new && lxc-wait -n $new -s RUNNING && sleep 2 lxc-attach -n $new -- apt install openssh-server python3 -y lxc-attach -n $new -- mkdir ~/.ssh lxc-attach -n $new -- bash -c "echo $(cat ~/.ssh/id_rsa.pub) >> ~/.ssh/authorized_keys" ssh-keygen -R 10.0.3.$newIP ssh-keyscan -H 10.0.3.$newIP >> ~/.ssh/known_hosts echo 10.0.3.$newIP > inventory.ini ansible-playbook -i inventory.ini /root/project/playbook_web.yml set +e lxc-attach -n $new -- /root/vendor/bin/phpunit /var/www/html/test.php valid=$? set -e if [ $valid -eq 0 ]; then if [ -d /var/lib/lxc/$old ]; then lxc-destroy --force $old fi else if [ -d /var/lib/lxc/$new ]; then lxc-destroy --force $new fi fi fi ``` Ce script aura pour objectif de créer un nouveau conteneur et d'installer l'ensemble des services nécessaires à l'intérieur ainsi qu'une copie de notre repository Github à partir d'un playbook Ansible. Il exécutera ensuite les tests unitaires de notre application web. En cas de réussite ceux-ci, il supprimera l'ancien conteneur, sinon il abandonnera les changements en supprimant le nouveau conteneur. Nous pouvons ensuite sauvegarder notre job en appuyant sur `Sauver`. ![](https://i.imgur.com/TYjqw89.png) ## C - Création du playbook Dans notre script nous appelons un playbook pour configurer notre conteneur, nous allons devoir créer celui-ci. Nous allons créé notre playbook avec la commande suivante : ```bash nano /root/project/playbook_web.yml ``` Le ajouterons le contenu suivant dans le playbook. ```yaml - name: Web servers configuration hosts: all vars: - mysql_root_password: password tasks: - name: Update apt: update_cache: yes - name: Install Python Mysql apt: name: python3-mysqldb state: latest - name: Install LAMP apt: name: "{{ item }}" state: latest loop: [ 'apache2', 'libapache2-mod-php', 'php', 'mariadb-server', 'composer', 'perl', 'php-ldap', 'php-imap', 'php-apcu', 'php-xmlrpc', 'php-cas', 'php-mysqli', 'php-mbstring', 'php-curl', 'php-gd', 'php-simplexml', 'php-xml', 'php-intl', 'php-zip', 'php-bz2' ] - name: Ensure mysql is enabled to run on startup service: name=mysql state=started enabled=true - name: Reset root PW shell: mysql -u root --execute="ALTER USER 'root'@'localhost' IDENTIFIED BY '{{ mysql_root_password }}';" - name: Create /root/.my.cnf with password credentials blockinfile: path: /root/.my.cnf block: | [client] user=root password={{ mysql_root_password }} [mysql] user=root password={{ mysql_root_password }} [mysqldump] user=root password={{ mysql_root_password }} [mysqldiff] user=root password={{ mysql_root_password }} create: yes - name: Delete anonymous MySQL user mysql_user: name="" host={{item}} state=absent with_items: - localhost - "{{ansible_nodename}}" - name: Delete Hostname based MySQL user mysql_user: name=root host="{{ansible_nodename}}" state=absent - name: Remove MySQL test database mysql_db: name=test state=absent - name: Create database mysql_db: name: bdd state: present - name: Add sample data to database copy: dest: /tmp/dump.sql content: | CREATE TABLE IF NOT EXISTS `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(250) NOT NULL, `password` varchar(250) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ; INSERT IGNORE INTO `users` (`id`, `username`, `password`) VALUES (1, 'ProfDeMath', '$2y$10$09wBgbjS8s7D0rWXGzoeW.D1V5QQjUIzfvdQklaHZmrWKXhkBkAhq'); - name: Insert sample data into database mysql_db: name: bdd state: import target: /tmp/dump.sql login_user: root login_password: "{{ mysql_root_password }}" - name: Install PHPUnit composer: command: require arguments: phpunit/phpunit working_dir: /root - name: Remove default index.html file: state: absent path: /var/www/html/index.html - name: Clone a github repository git: repo: https://github.com/Denhetsu/Repo-Ansible-Jenkins dest: /var/www/html clone: yes update: yes ``` Revenons sur les différents parties de ce playbook. Nous spécifions `all` en tant que hosts pour executer les commandes sur toutes les machines de notre inventaire. > PS: L'inventaire contiendra uniquement l'adresse ip du nouveau conteneur. Tâches : #### Installation - *`Update`* Nous allons en premier lieu mettre à jour notre conteneur avec le module `apt`. - Le paramètre `update_cache` avec la valeur `yes` permet d'executer `apt update`. - *`Install Python Mysql`* Nous allons installer avec le module `python-mysql` nécessaire pour que Ansible communique avec notre base de données. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. - *`Install LAMP`* Nous allons installer avec le module `apt`, tous les packages nécessaire pour faire fonctionner notre site web et notre serveur de base de données. - Le paramètre `state`avec la valeur `latest` permet de toujours installer la dernière version du packages. - La clause `loop` nous permet de lister tous les packages qui seront installer avec apt. - *`Ensure mysql is enabled to run on startup`* Nous allons démarrer le service mariadb-server et l'activer au démarrage du conteneur à l'aide du module `service`. - Le paramètre `enabled` avec la valeur `true` permet d'activer le service au démarrage. #### Configuration Mysql - *`Reset root PW`* Nous allons changer le mot de passe de l'utilisateur root de mysql par celui spécifié dans notre variable au début de notre playbook. - *` Create /root/.my.cnf with password credentials`* Nous allons créer un fichier de configuration pour mysql avec le module `blockinfile`. Celui permetra de se connecter à notre base de données sans qu'il nous demande d'identifiants. - *`Delete anonymous MySQL user`* Nous allons ensuite supprimer les utilisateurs anonymes - *`Delete Hostname based MySQL user`* Nous allons ensuite supprimer l'utilisateur utilisant le nom d'hôte - *`Remove MySQL test database`* Enfin nous allons supprimer la base de données `test` présent par défaut dans Mysql. #### Mise en place des services - *`Create database`* Nous allons créer la base de données utilisée par notre site web. - *`Add sample data to database`* Nous allons créer un fichier qui contiendra les commandes pour créer notre table `users` et ajouter l'utilisateur `ProfDeMath` à l'intérieur avec le hash du mot de passe `12345`. - *`Insert sample data into database`* Nous allons enfin exécuter les commandes du fichier dans notre base de données. - *`Install PHPUnit`* Pour exécuter les tests unitaires de notre application web, nous allons installer `phpunit` à l'aide du module `composer`. - *`Remove default index.html`* Nous allons maintenant supprimer le fichier index.html générer par apache2 pour laisser la place à notre application web. - *`Clone a github repository`* Nous allons enfin pour cloner le repository qui contient notre projet dans le dossier `/var/www/html` de notre conteneur. ## D - Automatiser le lancement du job lors d'un push Maintenant que notre job est complété, nous devoir finit de configurer l'automatisation du lancement du job avec GitHub. Pour faire cela, il est nécessaire d'ajouter un webhook à notre repository Github vers notre plateforme jenkins. Cependant notre serveur jenkins n'est disponible qu'en localhost sur notre machine. Pour résourdre cela nous allons utiliser une solution de contournement en utilisant le service proposé par `serveo.net`. Comme nous avons également besoin d'un nom de domaine statique, nous allons utiliser l'outil `openssl` pour générer un nom aléatoire. ```bash openssl rand -hex 8 # Out: d79aca26d8b43ce9 ``` > PS : La taille du nom du sous domaine ne peut exéceder 63 caractères. Pour rendre accessible notre localhost sur internet, nous utiliserons la commande suivante avec le résultat de la commande précédante à la place de `$URL`. ```txt ssh -R d79aca26d8b43ce9:80:localhost:8080 serveo.net ``` Vous désormais tester la connection depuis l'url qui est affiché. > Exemple: ><https://d79aca26d8b43ce9.serveo.net/> Maintenant que notre jenkins est accesible depuis internet, nous allons pouvoir ajouter un webhook sur notre repository Github. Aller sur la page de votre projet et rendez-vous dans l'onglet `Settings` puis la section `Webhooks`. ![](https://i.imgur.com/9XAyBk7.png) Appuyer sur `Add webhook`. Dans l'onglet qui s'affiche, ajouter dans le champ `Payload URL` l'url généré précédemment. ![](https://i.imgur.com/O1gff2N.png) Vous pouvez ensuite terminer la création du webhook. Pour tester le bon fonctionnement du webhook, nous pouvons réaliser un nouveau push vers notre repository. Nous pouvons alors apercevoir au début du fichier de log que notre build à bien été lancé par notre push. ![](https://i.imgur.com/kimeKsf.png) # Partie 4 - Docker / Dockerfiles ![](https://i.imgur.com/tcD7Fou.jpg) ## A - Dockerfile Nous allons désormais répéter toute les étapes précédentes du projet sauf que nous allons désormais utiliser des Dockerfile pour automatiser les déploiements des conteneurs. Nous suiverons l'arborescence de fichier suivante : ![](https://i.imgur.com/2OjLcde.png) La première chose que nous ferons sera de généré une pair de clé rsa avec la commande suivante : ```bash ssh-keygen -t rsa -b 4096 -f ./id_rsa -N "" -q ``` Nous déplacerons ensuite notre clé publique dans le dossier web et notre clé privé dans le dossier backup. ```bash mkdir web && mkdir mysql && mkdir proxy && mkdir backup mv id_rsa backup/ mv id_rsa.pub web/ ``` ### 1 - Serveur web Dans notre dossier web, nous créérons le fichier `Dockerfile` avec le contenu suivant : ```dockerfile FROM ubuntu EXPOSE 80 EXPOSE 22 ENV TZ=Europe/Paris RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN apt-get update RUN apt-get install -y openssh-server rsync nginx \ php perl php-ldap php-imap php-apcu php-xmlrpc \ php-cas php-mysqli php-curl php-gd php-simplexml \ php-xml php-intl php-zip php-bz2 RUN useradd btc -s /bin/bash RUN mkdir -m700 /root/.ssh COPY id_rsa.pub /root/.ssh/authorized_keys RUN chmod 600 /root/.ssh/authorized_keys RUN service ssh start CMD /usr/sbin/sshd -D & nginx -g "daemon off;" ``` - *`FROM`* : Permet de sélectionner la dernière image Ubuntu sur laquelle build notre conteneur. - *`EXPOSE`* : Ouvre le port 80 - *`ENV`*: Déclare une variable d'environnement "TZ" pour déclarer la timezone qui nous sera demander durant l'installation des différents packages. - *`RUN`*: - **1ère ligne** : Permet de copier notre timezone dans les fichiers localtime et timezone. - **2ème ligne** : Effectue la mise a jour du conteneur - **3ème ligne** : Installe nginx ainsi que tout les packages php. - **4ème ligne** : Créer un nouveau user btc. - *`CMD`*: Fais tourner Nginx en arrière plan et le tiens actif. ### 2 - Loadbalancer Dans notre dossier proxy, nous créérons un fichier `nginx.conf` avec le contenu suivant qui contiendra la configuration du loadbalancer : ```nginx worker_processes auto; events { worker_connections 1024; } http { upstream backend { least_conn; server web1; server web2; } server { listen 80; location / { proxy_pass http://backend; } } } ``` Nous créérons égelement le fichier `Dockerfile` avec le contenu suivant : ```dockerfile FROM ubuntu:latest RUN apt-get update RUN apt-get install -y nginx COPY nginx.conf etc/nginx/nginx.conf EXPOSE 80 EXPOSE 443 CMD ["nginx", "-g", "daemon off;"] ``` - *`FROM`*: Permet de sélectionner la dernière image Ubuntu sur laquelle build notre conteneur. - *`RUN`*: Effectuer la mise a jour du conteneur et installe nginx. - *`COPY`*: Copie le nouveau fichier de configuration - *`EXPOSE`*: Ouvre les ports 80 et 443. - *`CMD`*: Fais tourner Nginx en arrière plan et le tiens actif. ### 3 - Serveur de stockage Dans notre dossier proxy, nous créérons un fichier `index.html` avec le contenu de notre site web. ```html Hello world ``` Nous créérons ensuite un fichier `jail.local` avec le contenu suivant qui contiendra la configuration de fail2ban pour ssh : ```bash [sshd] enabled = true filter = sshd banaction = iptables-allports bantime = 1800 findtime = 120 maxretry = 3 ``` Nous créérons égelement le fichier `Dockerfile` avec le contenu suivant : ```dockerfile FROM ubuntu:22.04 RUN apt-get update && apt-get install -y fail2ban cron rsync openssh-server iptables rsync COPY index.html /usr/share/nginx/html/ RUN mkdir -m700 /root/.ssh COPY id_rsa /root/.ssh/ RUN \ echo "*/5 * * * * rsync -Pqr --delete /usr/share/nginx/html/ root@web1:/usr/share/nginx/html" > /etc/cron.d/backup-cron && \ echo "*/5 * * * * rsync -Pqr --delete /usr/share/nginx/html/ root@web2:/usr/share/nginx/html" >> /etc/cron.d/backup-cron && \ chmod 0644 /etc/cron.d/backup-cron && \ crontab /etc/cron.d/backup-cron RUN touch /var/log/auth.log COPY jail.local /etc/fail2ban/ EXPOSE 22 CMD \ ssh-keyscan -H web1 >> ~/.ssh/known_hosts && \ ssh-keyscan -H web2 >> ~/.ssh/known_hosts && \ service ssh start && \ service fail2ban start && \ cron && \ tail -f /var/log/fail2ban.log ``` - *`FROM`*: Permet de sélectionner la dernière image Ubuntu sur laquelle build notre conteneur. - *`RUN`*: Effectuer la mise a jour du conteneur et installe fail2ban, crontab, rsync et openssh. - *`COPY`*: Copie le nouveau fichier index.html dans le dossier html de Nginx et remplace l'ancien fichier existant. - *`RUN`* ### 4 - Serveur de base de données Enfin, dans notre dossier web, nous créérons le fichier `Dockerfile` avec le contenu suivant : ```dockerfile FROM ubuntu:22.04 RUN groupadd -r mysql && useradd -r -g mysql mysql RUN mkdir /run/mysqld && chown mysql:mysql /run/mysqld RUN apt-get update RUN \ apt-get install mariadb-server -y && \ sed -i 's/^bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf && \ service mariadb restart && \ mysql -u root -e "CREATE DATABASE toto; CREATE USER 'toto'@'%' IDENTIFIED BY 'toto'; GRANT ALL ON toto.* TO 'toto'@'%'; FLUSH PRIVILEGES;" EXPOSE 3306 USER mysql CMD ["mysqld"] ``` - *`FROM`*: Permet de sélectionner la dernière image Ubuntu sur laquelle build notre conteneur. - *`RUN`*: - **1ère ligne** : Créer un nouvel utilisateur appelé mysql ainsi qu'un nouveau groupe portant le même nom avec des privilèges réduit. - **2ème ligne** : Créer un nouveau dossier mysqld et permet de changer le propriétaire du dossier pour "mysql". - **3ème ligne** : Effectue la mise a jour du conteneur. - **4ème ligne** : Installe le serveur mariadb. - **5ème ligne** : Permet au serveur MariaDB d’écouter les connexions sur toutes les interfaces réseau disponibles. La commande sed est utilisée pour effectuer l’opération de recherche et de remplacement, et l’option -i est utilisée pour modifier le fichier sur place. - **6ème ligne** : Permet de redémarrer le service Mariadb. - **7ème ligne** : Créer une nouvelle base de données nommée toto, crée un nouvel utilisateur nommé toto avec des privilèges complets sur la base de données toto et vide les privilèges pour s’assurer que les modifications prennent effet immédiatement. ### 5 - Déploiement Nous créerons enfin le fichier `deploy.sh` qui crééra toute notre infrastructure. ```css /* Delete old */ docker rm --force mysql docker rm --force web1 docker rm --force web2 docker rm --force proxy docker rm --force backup docker network rm dmz1 docker network rm dmz2 /* Create network */ docker network create dmz1 --driver=bridge --subnet 172.24.1.0/24 --gateway 172.24.1.1 docker network create dmz2 --driver=bridge --subnet 172.24.2.0/24 --gateway 172.24.2.1 /* Create image */ docker build -t mysql mysql/ docker build -t web web/ docker build -t proxy proxy/ docker build -t backup backup/ /* Create container */ docker run -dti --net dmz2 --ip 172.24.2.50 --name mysql mysql docker run -dti --net dmz1 --ip 172.24.1.10 --name web1 web docker network connect dmz2 web1 --ip 172.24.2.10 docker run -dti --net dmz1 --ip 172.24.1.20 --name web2 web docker network connect dmz2 web2 --ip 172.24.2.20 docker run -dti -p 80:80 --net dmz1 --ip 172.24.1.30 --name proxy proxy docker run -dti --net dmz2 --ip 172.24.2.40 --name backup backup ``` Tous les fichiers nécessaire sont désormais présents, nous pouvons désormais executé notre script. ```bash chmod +x ./deploy.sh ./deploy.sh ``` En executant un `docker ps`, nous pouvons voir que nos conteneurs sont correctement lancées. ![](https://i.imgur.com/8gQ43jA.png)