---
# System prepended metadata

title: '**IMPLEMENTATION D''UN ENVIRONNEMENT DE TRAVAIL POUR EPSILON CORPORATION**'

---

# **IMPLEMENTATION D'UN ENVIRONNEMENT DE TRAVAIL POUR EPSILON CORPORATION**

> Xavier RACINE
> Bilal AGHARBI
> Pierrick HERVE
> Benoit DEJONCHEERE

> CAP GEMINI
> POEI Ingénieur système Scripting

## Sommaire

1. [Contexte](#I-Contexte)
    1. [Cahier des charges](#1-Cahier-des-charges)
    2. [Spécifications](#2-Spécifications)
2. [Implémentation](#II-Implémentation)
    1. [Bonnes pratiques](#1-Bonnes-pratiques)
    2. [Provisioning](#2-Provisioning)
        i. [Identifier le VPC à utiliser](#i-Identifier-le-VPC-à-utiliser)
        ii. [Groupes de sécurité](#ii-Groupes-de-sécurité)
        iii. [Création de clé ssh](#iii-Création-de-clé-ssh)
        iv. [Le vif du sujet](#iv-Le-vif-du-sujet)
        v. [Ajout d’un volume de stockage pour la machine sauvegardes](#v-Ajout-d’un-volume-de-stockage-pour-la-machine-sauvegardes)
        vi. [Modification du fichier hosts d'ansible](#vi-Modification-du-fichier-hosts-d'ansible)
    3. [Configuration globale machine ansible](#3-Configuration-globale-machine-ansible)
        i. [Durcissement ssh](#i-Durcissement-ssh)
        ii. [Installation Docker](#ii-Installation-Docker)
        iii. [Exploitation Gitlab](#iii-Exploitation-Gitlab)
    4. [Configuration machine frontend](#4-Configuration-machine-frontend)
        i. [Configuration de ProFTP](#i-Configuration-de-ProFTP)
        ii. [Configuration du VPN](#ii-Configuration-du-VPN)
    5. [Configuration machine applications](#5-Configuration-machine-applications)
        i. [Pipeline jenkins](#i-Pipeline-jenkins)
        ii. [Automatisation de Jekyll: deuxième tentative](#ii-Automatisation-de-Jekyll:-deuxième-tentative)
    6. [Configuration machine sauvegarde](#6-Configuration-machine-sauvegarde)
        i. [Configuration NFS](#i-)
3. [Implémentations des fonctionnalités en dehors de l'architecture machine](#III-Implémentations-des-fonctionnalités-en-dehors-de-l-architecture-machine)
    1. [Traduction de la documentation en Latex](#1-Traduction-de-la-documentation-en-Latex)

---

## I. Contexte

### 1. Cahier des charges

Vous trouverez ci-dessous le cahier des charges du projet final.
![](https://i.imgur.com/wlpBI8H.jpg)
![](https://i.imgur.com/bJNWtUk.png)


### 2. Spécifications

Au vu du cahier des charges très fourni nous avons commencé par une étape de brainstorming individuel puis de brainstorming collectif pour construire ensemble une architecture logicielle qui réponde aux besoins de l'entreprise Epsilon Corporation.

Nous avons abouti à l'architecture suivante. La machine ansible servira de machine cliente.

![](https://i.imgur.com/kOCbdLE.png)

Nous avons choisi les technologies suivantes
* ansible pour le déploiement automatisé pour sa versatilité (de très nombreux modules)
* docker pour isoler les applications pour sa puissance et sa simplicité
* jenkins pour la publication automatique des nouvelles versions des applications
* openvpn pour sa robustesse et le fait qu'il soit libre de droit
* RAID 5 pour la résistance aux pannes
* apache2 comme serveur web pour sa simplicité et sa praticité d'utilisation
* le projet communautaire "Witiko" pour traduire la documentation markdown en Latex
* proftpd pour ...

Puis nous avons réparti les tâches à réaliser sous la forme de différents rôles ansible.
Vous trouverez une version non exhaustive de cette répartition ci-dessous.

Rôle ansible
* ansible
  * node manager qui éxecutera le méga script
* aws
  * créer le groupe de sécurité
  * configurer le parfeu/groupe de sécurité
  * création de machines
  * ip fixe
* docker
  * installer docker
* 1 role pour chaque sujet, réseau interne
  * ssh
  * nfs
  * ftp
  * firewall
* pipeline jenkins, manuel, écrire doc pour reproduire le build/run/test pour la machine applications pour les trois sites web
* 1 par machine pour faire les installations spécifiques
  * pour les 3
    * installer les logiciels
    * création users et projects gitlab
    * installer un conteneur gitlab et cloner le projet sur un volume persistant
    * configuration des machines (nom de l'utilisateur, etc)
  * frontend
    * installation et configuration vpn
    * pas de conteneur pour jekyll mais plutôt le gitlab-ci.yml
  * applications
  * sauvegarde
    * gestion haute dispo

---

## II. Implémentation

### 1. Bonnes pratiques

L'implémentation étant principalement basée sur la rédaction de playbook ansible nous avons pu facilement travailler en parallèle.

Vous trouverez ci-dessous les bonnes pratiques de travail qui ont été respectée durant l'implémentation des spécifications.

Travail en équipe
* interagir fréquemment pour favoriser l'entraide, le partage de connaissance et résoudre les points bloquants (sous la forme d'un point quotidien au minimum)

Praticité/Outils

* développer les scripts sur vs-code puis push en scp sur les machines virtuelles cibles (pour avoir accès aux fonctionnalités de développement avancés notamment le copier-coller, l'autocompletion et la pagination syntaxique)

Viabilité/Fonctionnement

* au fur et à mesure de l'implémentation des spécifications, des tests seront réalisés pour s'assurer du bon fonctionnement (sous forme de commandes isolées ou de scripts)

Sauvegardes

* faire des clones et des snapshots des machines virtuelles après les étapes clés d'installation et de configuration

Documentation

* remplir la documentation au fur et à mesure

### 2. Provisioning
Dans cette étape, nous nous occupons de la création des trois VM frontent, applications et sauvegardes.

#### i. Identifier le VPC à utiliser

On commence par identifier le VPC de la région par défaut: 

```yaml
- name: info VPC
  ec2_vpc_net_info:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    region: "{{ region }}"
    filters:
      "isDefault": "true"
  register: default_vpc
```

Dans ce VPC par défaut, nous choisissons un subnet dans lequel seront nos trois machines:

```yaml
- name: subnets du VPC defaut
  ec2_vpc_subnet_info:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    region: "{{ region }}"
    filters:
      vpc-id: "{{ default_vpc['vpcs'][0]['vpc_id'] }}"
  register: subnet_info

- name: pick d'un subnet random
  set_fact:
    default_vpc_id: "{{ default_vpc['vpcs'][0]['vpc_id'] }}"
    random_subnet: "{{ subnet_info.subnets|map(attribute='id')|list|random }}"
```

#### ii. Groupes de sécurité

Dans cette section, le but est de créer les groupes de sécurité associés à chaque machine. L'idée est d'ouvrir les ports correspondant à nos différentes applications. Le port 22, quand à lui, est ouvert uniquement pour la machine ansible.

```yaml
- name: info ip publique
  ipify_facts:

- name: groupe de securite frontend
  ec2_group:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: groupe_gr2_frontend
    description: frontend rules
    vpc_id: "{{ default_vpc_id }}"
    region: "{{ region }}"
    state: present
    rules:
      - proto: tcp
        ports:
        - 80
        - 21 
        cidr_ip: 0.0.0.0/0
        rule_desc: autoriser tout ports 21 et 80
      - proto: tcp
        ports:
        - 22
        cidr: "{{ ipify_public_ip }}/32"
        rule_desc: autoriser connexion SSH depuis machine hote
  register: groupe_frontend

        

- name: groupe de securite applications
  ec2_group:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: groupe_gr2_applications
    description: applications rules
    vpc_id: "{{ default_vpc_id }}"
    region: "{{ region }}"
    state: present
    rules:
      - proto: tcp
        ports:
        - 22
        cidr_ip: "{{ ipify_public_ip }}/32"
        rule_desc: autoriser connexion SSH depuis machine hote
      - proto: tcp
        ports:
        - 4000 #jekyll
        - 9080 #jenkins, /!\ ce n'est pas le port par défaut /!\
        - 8080 #tomcat
        - 8081 #php-myadmin
        - 3306 #php-mysql
        - 8888 #python
        cidr_ip: 0.0.0.0/0
        rule_desc: restriction des ports applicatifs au subnet
  register: groupe_applications

- name: groupe de securite sauvegardes
  ec2_group:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: groupe_gr2_sauvegardes
    description: sauvegardes rules
    vpc_id: "{{ default_vpc_id }}"
    region: "{{ region }}"
    state: present
    rules:
      - proto: tcp
        ports:
        - 22
        cidr_ip: "{{ ipify_public_ip }}/32"
        rule_desc: autoriser connexion SSH depuis machine hote
  register: groupe_sauvegardes
```
Cette partie reste à débugger, et n'a pas été implémentée.

#### iii. Création de clé ssh
Avant de s'occuper des VM, il reste à créer une clé SSH qui sera commune aux trois machines.

```yaml=
- name: suppression ancienne cle ssh
  ec2_key:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    region: "{{ region }}"
    name: ssh_pair
    state: absent

- name: creation cle ssh
  ec2_key:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    region: "{{ region }}"
    name: ssh_pair
    state: present
  register: ec2_pair

- name: sauvegarde cle privee
  copy: content="{{ ec2_pair.key.private_key }}" dest="/home/centos/.aws/ssh_pair.pem" mode=0400
```

#### iv. Le vif du sujet
Maintenant, tout est prêt pour créer les instances EC2.

```yaml=
vars:
  ec2_list: #ignorer l'item "ip", fixer l'ip privée n'a pas été implémenté
  - { name: 'gr2_frontend', ip: '10.10.10.8' }
  - { name: 'gr2_applications', ip: '10.10.10.9' }
  - { name: 'gr2_sauvegardes', ip: '10.10.10.10' }
  image_id: ami-02334c45dd95ca1fc #image de CentOS 7

- name: creation instances
  ec2_instance: 
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    name: "{{ item.name }}"
    key_name: "ssh_pair"
    vpc_subnet_id: "{{ random_subnet }}"
    instance_type: t3.xlarge
    security_group: default
    region: "{{ region }}"
    network:
      assign_public_ip: yes
    image_id: "{{ image_id }}" 
    state: present
  loop: "{{ ec2_list }}"
  register: device_list
```

Remarque: avec des groupes de sécurité fonctionnels, on pourrait remplacer
`security_group: default` par `security_group: "{{ 'groupe_' + item.name }}"`

On enregistre dans `device_list` toutes les données renvoyées par `ec2_instance` pour ne pas perdre les informations sur les machines que l'on vient de créer.

```yaml
- name: sauvegarde public DNS - ID instance - IP privee
  lineinfile:
    path: /home/centos/.aws/public_dns_instance
    line: "{{ item.item.name }}:{{ item.instances[0].public_dns_name }}:{{ item.instances[0].instance_id }}:{{ item.instances[0].private_ip_address }}"
    insertafter: EOF
    state: present
    create: yes
  with_items: "{{ device_list.results }}"
```

On enregistre les données importantes des machines créées dans un fichier au format suivant:
`"nom":"dns public":"id instance":"ip privée"`
Ce format facilite la récupération et l'utilisation de ces informations, comme par exemple pour la modification automatisée du fichier hosts d'ansible.

#### v. Ajout d'un volume de stockage pour la machine sauvegardes

Comme la machine sauvegardes a vocation a accueillir de grandes quantités de données, on lui rajoute un volume de stockage.

```yaml=
- name: isolement id sauvegardes
  shell: cat /home/centos/.aws/public_dns_instance | grep sauvegardes | cut -d":" -f3
  register: sauvegarde

- name: ajout volume sauvegardes
  ec2_vol:
    aws_access_key: "{{ aws_access_key }}"
    aws_secret_key: "{{ aws_secret_key }}"
    instance: "{{ sauvegarde.stdout }}"
    volume_size: 16 #taille en Go
    state: present
    region: "{{ region }}"
    zone: "{{ random_subnet }}"
```

#### vi. Modification du fichier hosts d'ansible
Comme mentionné plus haut, on modifie le fichier hosts de ansible, pour pouvoir éxecuter les autres playbooks ciblant directement ces machines en toute sérénité.

```yaml=
- name: modifications ansible hosts
  shell: ansible_hosts.sh
  args:
    executable: /bin/bash
```

```bash=
#!/bin/bash

hosts_file="/home/centos/projet_final/hosts"
info_file="/home/centos/.aws/public_dns_instance"

while read line
do
    echo -e "[$(echo "$line" | cut -d: -f1)]" >> $hosts_file
    echo -e "$(echo "$line" | cut -d: -f2) ansible_ssh_private_key_file=/home/centos/.aws/ssh_pair.pem" >> $hosts_file
done < $info_file
```

En suivant le format du fichier `/home/centos/.aws/public_dns_instance`, on rajoute donc des blocs de la forme
```
[nom de machine]
dns-public ansible_ssh_private_key_file=/home/centos/.aws/ssh_pair.pem
```


### 3. Configuration globale machine ansible

Les trois machines devant être accessible en ssh, nous avons choisi de durcir le ssh pour renforcer la sécurité.

#### i. Durcissement ssh

Pour cela nous avons écrit le playbook ansible suivant, qui se charge d'installer openssh et de modifier certaines lignes du fichier `/etc/ssh/sshd_config` en prenant en compte le fait que les lignes peuvent commencer par un # ou non.

Nous pourrions [aller plus](https://www.digitalocean.com/community/tutorials/how-to-harden-openssh-on-ubuntu-18-04-fr) loin en restreignant aussi les autres méthodes d'authentification possible, interdire aux utilisateurs de passer des variables d'environnement personnalisées, désactiver la bannière de connexion, modifier les options de tunneling et de redirections, etc.

```yaml
---
- hosts: all
  become: yes
  vars:

  tasks:
    # Installation de openssh
    - name: installation de openssh
      package:
        name: openssh
        state: latest

    # Durcissement de l'accès en ssh des serveurs ...
    # ... en modifiant les lignes du fichier /etc/ssh/sshd_config
    - name: changement du port par défaut
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)Port'
        line: Port 2222

    - name: interdiction de la connexion par mot de passe
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)PasswordAuthentication'
        line: PasswordAuthentication no

    - name: authorisation de la connexion par clé
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)PubkeyAuthentication'
        line: PubkeyAuthentication yes

    - name: interdiction de la connexion en root
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)PermitRootLogin'
        line: PermitRootLogin no

    - name: maximum trois essais d'affilés pour se connecter
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)MaxAuthTries'
        line: MaxAuthTries 3

    - name: maximum cinq sessions en parallèle
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^(#?)MaxSessions'
        line: MaxSessions 10
...
```

#### ii. Installation Python3.10

Nous avons automatisé l'installation de Python3 afin d'éviter les effets de bord, dues notamment à l'utilisation de Docker. Voici le playbook:

```yaml=
---

- name: yum groupinstall
  yum:
    name: "@Development Tools"
    state: latest

- name: Installation de packages divers
  yum:
    name:
      - openssl-devel
      - libffi-devel
      - bzip2-devel
    state: latest

- name: importer la compression python3.10.tar.gz
  get_url:
    url: https://www.python.org/ftp/python/3.10.0/Python-3.10.0.tgz
    dest: /tmp
    mode: '0440'

- name: decompresser l'archive python
  unarchive:
    src: /tmp/Python-3.10.0.tgz
    dest: /tmp
    remote_src: yes

- name: commande ./configure --enable-optimizations
  shell: ./configure --enable-optimizations
  args:
    chdir: /tmp/Python-3.10.0

- name: commande make altinstall
  shell: make altinstall
  args:
    chdir: /tmp/Python-3.10.0
    
- name: Installation boto #Nécessaire pourle provisioning
  pip:
    name: "{{ item }}"
  with_items:
    - boto
    - boto3
    - botocore

...
```

#### iii. Installation Docker

Nous avons choisi d'automatiser l'installation de [docker](https://docs.docker.com/engine/install/centos/) sur les machines du réseau, en écrivant le playbook suivant.

Nous prenons garde à bien supprimer les installations pré-existantes qui pourrait rentrer en conflit avec la version "latest" que l'on installe.

On a récupéré la "baseurl" du dépôt du fichier "/etc/yum.repos.d/docker-ce.repo" présent à l'installation de docker sur une autre machine.

```yaml
---
- hosts: localhost
  become: yes
  vars:
    paquets_desinstall_docker:
      - docker
      - docker-engine
      - docker.io
      - containerd
      - runc
    paquets:
      - git
      - ca-certificates
      - wget
      - gnupg2
      - curl
      - yum-utils
      - device-mapper-persistent-data
      - lvm2
      - python3
    paquets_install_docker:
      - docker
      - docker-client
      - containerd
      - python2-pip

  tasks:
    # Desinstallation des anciennes versions de docker
    - name: desinstallation des anciennes versions de docker
      package: name="{{ paquets_desinstall_docker }}" state=absent update_cache=yes

    # Installation de docker avec un depot
    # - gestion des paquets
    - name: mise a jour du cache
      package: name="*" state=latest update_cache=yes

    - name: installation des paquets
      package: name={{ paquets }} state=latest update_cache=yes

    # - gestion du depot
    - name: ajout de la cle
      rpm_key: key=https://download.docker.com/linux/centos/gpg state=present

    - name: ajout du depot
      yum_repository:
        name: docker
        description: docker repo
        baseurl: baseurl=https://download.docker.com/linux/centos/$releasever/$basearch/stable
        state: present

    - name: mise a jour du cache
      package: name="*" state=latest update_cache=yes

    # - installation de docker
    - name: installation de Docker
      package: name={{ paquets_install_docker }} state=latest update_cache=yes

    - name: Start Docker service
      service: name=docker state=started enabled=yes
...
```

On execute le playbook avec la commande suivante pour forcer l'utilisation du python 3 au cas où plusieurs versions de python soient installées.
`ansible-playbook -i /etc/ansible/hosts gestion_gitlab.yml --private-key=~/POEIgroupe2.pem -e 'ansible_python_interpreter=/usr/bin/python3'`

Apreès l'execution avec succès du playbook on vérifie le bon fonctionnement de docker avec la commande
`sudo docker run hello-world`

#### iv. Exploitation Gitlab

Nous avons dans un premier temps envisager d'automatiser la création des groupes, utilisateurs et projets gitlab à l'aide de ansible. Néanmoins nous avons rapidement réalisé la non faisabilité de cette entreprise.
Du fait d'[erreurs 403](https://gitlab.com/gitlab-org/gitlab/-/issues/244345) qui seront résolues dans une prochaine version de gitlab et qui empêche de créer des groupes ou des utilisateurs via les modules gitlab de ansible.
Nous avons donc choisi de réaliser manuellement la partie de création du groupe, des projets et des utilisateurs gitlab avec l'aide de la GUI web. Et réaliser seulement la partie exploitation avec ansible.

```yaml
---
- hosts: sauvegarde
  become: yes
  vars:
    gitlab_host: "https://gitlab.com"
    gitlab_token: "glpat-9xxraELonvEDvXSrge7P"
    gitlab_api_url: "{{ gitlab_host }}/api/v4"

  tasks:
    # Installation du prérequis python (les versions antérieures ou égal à la 1.12.1 sont les seules compatibles)
    - name: Install python-gitlab
      pip:
       name: python-gitlab==1.12.1

    # Creation du groupe gitlab qui contiendra les 3 projets
    - name: supprimer l'ancien groupe gitlab
      gitlab_group:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        validate_certs: True
        name: POEI_groupe_2
        state: absent

    - name: "Create GitLab Group"
      gitlab_group:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        validate_certs: True
        name: POEI_groupe_2
        state: present

    # Mise en fonction gitlab : machine "sauvegarde"
    # - creation des utilisateurs gitlab
    - name: Supprimer l'ancien utilisateur
      gitlab_user:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        username: "gitlab_sauvegarde"
        validate_certs: True
        name: "rien"
        password: "rien"
        email: "gitlab_sauvegarde@gmail.com"
        state: absent

    # - generation d'une paire de cle ssh
    - name: generation d'une paire de cle ssh, de longueur 4096 bits et avec rsa
      openssh_keypair:
        path: /home/centos/.id_rsa_keys_gitlab

    # mettre un deploy_key pour associer la cle privee au projet et faire le lien avec l'utilisateur

    - name: creation de l'utilisateur gitlab gitlab_sauvegarde
      gitlab_user:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        username: "gitlab_sauvegarde"
        validate_certs: True
        name: "none"
        password: "none"
        email: "gitlab_sauvegarde@gmail.com"
        state: present
        sshkey_name: .id_rsa_keys_gitlab.pub
        sshkey_file: 
        #group: POEI groupe 2
        access_level: owner

    # - creation des projets
    - name: suppression de l'ancien projet
      gitlab_project:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        validate_certs: True
        name: sauvegarde
        state: absent

    - name: Create GitLab Project in group Ansible
      gitlab_project:
        api_url: "{{ gitlab_host }}"
        api_token: "glpat-9xxraELonvEDvXSrge7P"
        validate_certs: True
        name: data
        #group: POEI_groupe_2
        import_url: "https://gitlab.com/p3061/data.git"
        state: present

    # Mise en fonction gitlab : machine "applications"
    # - creation des utilisateurs gitlab
    # - creation du groupe
    # - creation des projets
...
```

Le gitlab token a lui aussi été généré par avec la GUI web.
Il nous sert à nous authentifier auprès de l'API gitlab qui ne permet plus d'utiliser username et password pour se connecter depuis la version 10.2.

Vous trouverez ci-dessous la partie exploitation de gitlab traitée à l'aide de commandes bash (lancement conteneur gitlab, clone des projets).
Nous aurions pu choisir deux autres solutions pour l'implémentation :
* utiliser un Dockerfile avec ansible(https://hub.docker.com/r/gitlab/gitlab-ce/dockerfile/)
* utiliser un docker-compose.yml avec ansible (https://docs.gitlab.com/ee/install/docker.html)
Nous avons délaissé les solutions automatisées au profit d'une solution manuelle étant donné le temps imparti.

Vous trouverez ci-dessous la manipulation que nous avons réalisée pour mettre en place le projet gitlab "data" sur la machine "sauvegarde" (la manipulation étant identique sur la machine "applications" avec les projets gitlab "web" et "ansible").

Nous avons utilisé l'image docker [gitlab/gitlab-ce](https://hub.docker.com/r/gitlab/gitlab-ce).

Le fait que les projets gitlab soient privées nécessite de s'authentifier pour les cloner, bien que les manipulations soient plus longues l'on a choisi de laisser les projets en privée.

```bash
# On lance le conteneur gitlab/gitlab-ce
export GITLAB_HOME=$HOME/gitlab

sudo docker run --detach \
  --publish 443:443 --publish 80:80 \
  --name gitlab \
  --restart always \
  --volume $GITLAB_HOME/config:/etc/gitlab:Z \
  --volume $GITLAB_HOME/logs:/var/log/gitlab:Z \
  --volume $GITLAB_HOME/data:/var/opt/gitlab:Z \
  --volume $GITLAB_HOME/project:/home/:Z \
  gitlab/gitlab-ee:latest

# On clone le projet "data"
sudo ssh-keygen -t rsa -C "pierrick.herve56@gmail.com" -b 4096
    # en précisant comme chemin : /home/centos/gitlab/.ssh/id_rsa
    
# A l'aide de la GUI web de gitlab on enregistre la clé publique dans les paramètres de l'utilisateur

# on clone le projet en dehors du conteneur dans un des volumes
cd /home/centos/gitlab/project
sudo git clone https://PierrickHERVE@gitlab.com/p3061/data.git
    # en renseignant le mot de passe

# On aurait aussi pu cloner le projet en envoyant la commande au conteneur
sudo docker exec -ti gitlab "git clone https://PierrickHERVE@gitlab.com/p3061/data.git"

# On vérifie nos commandes en observant le contenu des volumes persistants créés
sudo ls /home/centos/gitlab/config
sudo ls /home/centos/gitlab/logs
sudo ls /home/centos/gitlab/data

# PS : si l'on souhaite supprimer le conteneur déjà existant pour repartir de zéro
sudo docker stop gitlab \
&& sudo docker rm gitlab \
&& sudo docker system prune -y
```

### 4. Configuration machine frontend


#### i. Configuration du serveur ProFTP

Nous avons configuré le serveur FTP avec deux playbooks ansible, l'un pour l'installation et le démarrage des services, l'autre pour sa configuration. L'arborescence est décrite ci-dessous:

![](https://i.imgur.com/aGkbXdr.png)

Voici les playbooks utilisés:

```yaml=
---

# main.yml


- vars_files:
    - noms_users_file.yml
  
  tasks:
    - name: Ajouter le dépot epel-release
      yum:
        name: epel-release
        state: latest

    - name: Installer proftpd
      yum:
        name:
          - proftpd
          - proftpd-utils
        state: latest

    - name: démarrer le service proftpd
      service:
        name: proftpd
        enabled: yes

    - name: Autoriser le service proftpd
      firewalld:
        service: proftpd
        permanent: yes
        state: enabled
      notify: redemarrer firewalld

    - include: conf_ftp.yml
    
  handlers:
    - name: redemarrer firewalld
      service:
        name: firewalld
        state: restarted
...    
```

Puis:
```yaml=
---

# conf_ftp.yml

- name: ajouter le shell /bin/false dans /etc/shells
  lineinfile:
    dest: /etc/shells
    line: '/bin/false'

- name: créer les utilisateurs FTP
  user:
    name: groupe2
    state: present
    uid: 2222
    shell: /bin/false
        
- name: copie du fichier de configuration proftpd
  command: cp /etc/proftpd.conf /etc/proftpd_backup.conf

- name: configurer le nouveau port dans le fichier de configuration
  lineinfile:
    path: /etc/proftpd.conf
    line: 'Port 5000'
...    
```

#### ii. Configuration du VPN

Nous avons décidé d'utiliser OpenVPN. Sa configuration se fait en plusieurs parties, avec notamment la création de clés et de certificats.

Nous avons automatisé la configuration via Ansible, à travers plusieurs playbooks, qui seront appelés à la fin par le fichier `tasks/main.yml`.

Seront présentés respectivement ci-dessous les playbooks : 
- de l'installation des packages
- de la configuration du fichier `/etc/openvpn/server.conf`
- de la création des clés et certifications
- de la gestion du pare-feu et du routage réseau

```yaml=
---

- name: installation epel-release
  yum:
    name: epel-release
    state: latest
    update_cache: yes

- name: installation OpenVPN et wget
  yum:
    name:
      - openvpn
      - wget
    state: latest
    update_cache: yes

- name: importation de l'archive easy-rsa
  get_url:
    url: https://github.com/OpenVPN/easy-rsa/archive/v3.0.8.tar.gz
    dest: /home/centos
    mode: '0440'

- name: extraire les fichiers de l'archive
  unarchive:
    src: /home/centos/v3.0.8.tar.gz
    dest: /home/centos/

- name: créer le répertoire easy-rsa dans /etc/openvpn
  file:
    path: /etc/openvpn/easy-rsa
    state: directory
    owner: root
    group: root
    mode: '0755'

- name: déplacer le dossier easy-rsa-3.0.8/ vers /etc/openvpn/easy-rsa
  command: mv /home/centos/easy-rsa-3.0.8 /etc/openvpn/easy-rsa

...
```

```yaml=
---

- name: copier le fichier server.conf vers /etc/openvpn
  copy:
    src: /usr/share/doc/openvpn/sample/sample-config-files/server.conf
    dest: /etc/openvpn/
    owner: root
    group: root
    mode: '0644'

- name: modification du fichier /etc/openvpn/server.conf
  lineinfile:
    path: /etc/openvpn/server.conf
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
  with_items:
    - { regexp: '^(.*)topology subnet', line: 'topology subnet' }
    - { regexp: '^(.*)push "redirect-gateway def1 bypass-dhcp"', line: 'push "redirect-gateway def1 bypass-dhcp"' }
    - { regexp: '^(.*)push "dhcp-option DNS 208.67.222.222"', line: 'push "dhcp-option DNS 208.67.222.222"' }
    - { regexp: '^(.*)push "dhcp-option DNS 208.67.220.220"', line: 'push "dhcp-option DNS 208.67.220.220"' }
    - { regexp: '^(.*)user nobody', line: 'user nobody' }
    - { regexp: '^(.*)group nobody', line: 'group nobody' }
    - { regexp: '^(.*)tls-auth ta.key 0', line: ';tls-auth ta.key 0' }

- name: ajouter ligne "tls-crypt myvpn.tlsauth"  après ";tls-auth ta.key 0"
  lineinfile:
    path: /etc/openvpn/server.conf
    insertafter: '^(.*)tls-auth ta.key 0'
    line: "tls-crypt myvpn.tlsauth"
    state: present

- name: generation de la clé de cryptage specifié dans le fichier de config
  command: openvpn --genkey --secret /etc/openvpn/myvpn.tlsauth
  
...
```

```yaml=
---

- name: copie du fichier vars.example en vars
  copy:
    src: /etc/openvpn/easy-rsa/easyrsa3/vars.example
    dest: /etc/openvpn/easy-rsa/easyrsa3/vars
    owner: root
    group: root
    mode: '0644'
    remote_src: yes


- name: modification du fichier /etc/openvpn/easy-rsa/easyrsa3/vars
  lineinfile:
    path: /etc/openvpn/easy-rsa/easyrsa3/vars
    regexp: "{{ item.regexp }}"
    line: "{{ item.line }}"
  with_items:
    - { regexp: '^(.*)set_var EASYRSA_REQ_COUNTRY "US"', line: 'set_var EASYRSA_REQ_COUNTRY "FR"' }
    - { regexp: '^(.*)set_var EASYRSA_REQ_PROVINCE "California"', line: 'set_var EASYRSA_REQ_PROVINCE "Bretagne"' }
    - { regexp: '^(.*)set_var EASYRSA_REQ_CITY "San Francisco"', line: 'set_var EASYRSA_REQ_CITY "Lorient"' }
    - { regexp: '^(.*)set_var EASYRSA_REQ_ORG "Copyleft Certificate Co"', line: 'set_var EASYRSA_REQ_ORG "Groupe"' }
    - { regexp: '^(.*)set_var EASYRSA_REQ_EMAIL "me@example.net"', line: 'set_var EASYRSA_REQ_EMAIL "capge@groupe2.org"' }
    - { regexp: '^(.*)set_var EASYRSA_REQ_OU "My Organizational Unit"', line: 'set_var EASYRSA_REQ_OU "POEI gr2"' }
    
- name: ajout des lignes KEY_NAME et KEY_CN
  lineinfile:
    path: /etc/openvpn/easy-rsa/easyrsa3/vars
    line:
      - export KEY_NAME="server"
      - export KEY_CN=openvpn.poeigroupe2.com

- name: execution ./easyrsa clean-all
  command: ./easyrsa clean-all
  args:
    chdir: /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3

- name: execution ./easyrsa build-ca
  expect:
    command: ./easyrsa build-ca
    chdir: /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3
    responses:
      (?i)Enter New CA Key Passphrase: "stage"
      (?i)(.*)Enter New CA Key Passphrase: "stage"
      (?i)Common Name(.*): "groupe 2"

- name: execution ./easyrsa build-server-full server
  expect:
    command: ./easyrsa build-server-full server
    chdir: /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3
    responses:
      (?i)Enter PEM pass phrase: "stage"
      (?i)(.*)Enter PEM pass phrase: "stage"
      (?i)Enter pass phrase(.*): "stage"
      
- name: execution ./easyrsa gen-dh
  command: ./easyrsa gen-dh
  args:
    chdir: /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3

- name: execution ./easyrsa build-client-full client1
  command: ./easyrsa build-client-full client1
  args:
    chdir: /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3

- name: copie des clés et certificats
  copy:
    src:
      - /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3/pki/ca.crt
      - /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3/pki/dh.pem
      - /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3/pki/private/ca.key
      - /etc/openvpn/easy-rsa/easy-rsa-3.0.8/easyrsa3/pki/private/server.key
    dest: /etc/openvpn
    remote_src: yes      

...
```

```yaml=
---

- name: ajout du service openvpn
  firewalld:
    name: openvpn
    zone: public
    permanent: yes
    state: enabled

- name: ajout d'un masquerade
  firewalld:
    masquerade: yes
    state: enabled
    permanent: yes
    zone : public

- name: creation de la variable du premier reseau
  command: VAR=$(ip route get 208.67.222.222 | awk 'NR==1 {print $(NF-2)}')

- name: ajouter règle de routage utilisant la variable VAR
  command: firewall-cmd --permanent --direct --passthrough ipv4 -t nat -A POSTROUTING -s 10.8.0.0/24 -o $VAR -j MASQUERADE

- name: reload firewalld
  service:
    name: firewalld
    state: reloaded

- name: modifier le fichier /etc/sysctl.conf
  lineinfile:
    path: /etc/sysctl.conf
    line: 'net.ipv4.ip_forward = 1'

- name: redemarrer NetworkManager
  service:
    name: NetworkManager
    state: restarted
...
```

### 5. Configuration machine applications

Playbook ansible et arborescence des rôles

#### i. Pipeline jenkins

Pour réaliser l'automatisation de la mise à jour des sites web nous avons avions le choix entre [gitlab CI/CD](https://docs.gitlab.com/ee/ci/) et [jenkins](https://www.jenkins.io/).

Nous avons choisi jenkins pour sa simplicité d'utilisation et sa praticité et afin de consacrer plus de temps au premières étapes de la mise en place de l'architecture telles que la création des machines et les paramètrages réseaux. Bien que conscient du fait que jenkins soit automatisable moins facilement.

Le pipeline jenkins que nous avons mis en place, sur le modèle build/run/test et qui est déclenché par un commit du développeur sur le projet gitlab "applications" est le suivant.
1. démarrer le conteneur jenkins
2. faire un jekyll build pour construire la nouvelle page web
3. vérifier que la page web a été mise à jour

Commençons d'abord par faire fonctionner le tout manuellement.

Pour réaliser l'implémentation d'un environnement Jekyll via Docker, nous suivons les étapes suivantes:

1. Installer un container Ruby
```bash
$ docker pull ruby
$ docker run --name jekyll-template -it ruby /bin/bash
```

2. Installer Jekyll, et initialiser un dossier source

Une fois dans le conteneur
```
# gem install jekyll bundler
# jekyll new src
# exit
```

3. Récupérer le dossier /src sur la machine hôte
```bash
$ docker cp jekyll-template:/src .
$ docker rm jekyll-template
```
Le container jekyll-template n'étant plus nécessaire à ce stade, il peut être supprimé.

4. Créer un dossier dans lequel récupérer les pages web
`$ mkdir site`

5. Ecrire le Dockerfile du container Ruby sur lequel tournera Jekyll
```dockerfile
FROM ruby:3.0
RUN gem install jekyll bundler
WORKDIR /src
```

6. Build une image depuis ce Dockerfile
```bash
$ bundle install
$ sudo docker build -t my-jekyll -f Dockerfile .
```

7. Lancer un container basé sur cette image
```bash
$ cd ~
$ sudo docker run --name jekyll --volume $(pwd)/src:/src:z \ 
    --volume $(pwd)/site:/site:z -p 4000:4000 -it my-jekyll
```
L'option :z du montage des volumes est essentielle pour permettre un partage effectif du contenu des dossiers.

8. C'est parti !
```bash
$ sudo docker start jekyll
$ sudo docker exec jekyll bundle install
$ sudo docker exec jekyll bundle exec jekyll build --destination /site
$ sudo docker stop jekyll
```

Nous aurions aussi pu choisir d'utiliser le [Dockerfile proposé par liulantao](https://hub.docker.com/r/liulantao/jenkins-ruby/dockerfile) déjà concu pour travailler avec des projets développés en ruby. Néanmoins nous avons préféré nous servir d'un conteneur plus petit et plus simple suffisant amplement à notre besoin et réduisant ainsi les effets de bord.

Regroupons maintenant ces étapes en créant un pipeline jenkins composé de trois tâches/jobs.
On se servira d'un [conteneur jenkins](https://github.com/jenkinsci/docker/blob/master/README.md) en complément que l'on initialisera avec un playbook ansible (docker_image, docker_container, uri).

1. démarrer le conteneur jenkins

Nouvel item
1_BUILD
nom : construire un projet freestyle
description : démarrer le conteneur jenkins
paramètre
    string
        gitlabSourceBranch : master
        to prevent errors due to manual addition of a file directly through web GUI (https://github.com/jenkinsci/gitlab-plugin/issues/539)
ce qui declenche le build
    scrutation de l'outil de gestion de version
        planning : * * * * * (chaque minute)
gestion de code source
    git
        credentials
            name : PierrickHERVE
            psswd: ........
        et choisir le credential nouvellement créée
executer un script shell
    sudo docker build --no-cache -t my-jekyll -f Dockerfile . && cd ~ && sudo docker run --name jekyll --volume $(pwd)/src:/src:z --volume $(pwd)/site:/site:z -p 4000:4000 -it my-jekyll

On en profite pour ajouter l'option `--no-cache` pour forcer l'execution des étapes du Dockerfile et pouvoir télécharger la version la plus récente des paquets utilisés même si le Dockerfile n'a pas été modifié.

```bash
$ sudo docker build --no-cache -t my-jekyll -f Dockerfile .
$ cd ~
$ sudo docker run --name jekyll --volume $(pwd)/src:/src:z \ 
    --volume $(pwd)/site:/site:z -p 4000:4000 -it my-jekyll
```

Et si l'on souhaite redémarrer le conteneur de zéro.
```bash
sudo docker stop jekyll && sudo docker rm jekyll && sudo docker system prune -y
```
 

2. faire un jekyll build pour construire la nouvelle page web

nouvel item
2_RUN
description : faire un jekyll build pour construire la nouvelle page web
ce qui délcenche le build
    construire après le build sur d'autres projets
        projet à surveiller : 1_BUILD
    déclencher que si la construction est stable

```bash
$ sudo docker start jekyll
$ sudo docker exec jekyll bundle install
$ sudo docker exec jekyll bundle exec jekyll build --destination /site
$ sudo docker stop jekyll
```

3. vérifier que la page web a été mise à jour

nouvel item
3_TEST

visualiser la page
ou vérifier qu'il y a un niouveau dossier ou plus récent ou une page de plus

#### ii. Automatisation de Jekyll: deuxième tentative
Dans cette section se trouve une ébauche d'autmatisation d'installation de Jekyll et Jenkins. De nombreux points sont simplement la traduction des commandes ci-dessus par Ansible.

Le lancement manuel de la tâche sur Jenkins est fonctionnel, mais le déclenchement via gitlab n'est pas implémenté.

1. La première étape est d'installer les modules pythons nécessaire. On rappelle que Docker est déjà installé sur les différentes machines (voir [Installation Docker](#ii-Installation-Docker))

```yaml=
- name: python3-pip installation
  yum:
    name:
      - python3-pip
    state: present
    update_cache: true

- name: setuptools installation
  yum:
    name:
      - python-setuptools
    state: present
    update_cache: true

- name: Docker-python
  yum:
    name:
      - python-docker
    state: present
    update_cache: true
```

2. Cette étape n'est que la traduction de l'installation d'un container Jekyll détaillée plus haut dans [Pipeline jenkins]( #i-Pipeline-jenkins)

```yaml=
- name: installation d'un ruby template
  docker_container:
    name: jekyll-template
    image: ruby
    command: gem install jekyll bundler
    state: started

- name: création src
  shell: docker exec jekyll-template jekyll new src

- name: copie template
  shell: docker cp jekyll-template:/src /home/centos

- name: suppression template
  docker_container:
    name: jekyll-template
    state: absent

- name: creation dossier cible
  file:
    name: /home/centos/site
    state: directory

- name: copie dockerfile
  copy: 
    src: ./Dockerfile
    dest: /home/centos/Dockerfile
    owner: root
    group: root
    mode: '0644'

- name: building image
  docker_image:
    name: my-jekyll
    build:
      source: build
      path: /home/centos
    state: present

- name: docker jekyll
  docker_container:
    name: jekyll
    image: my-jekyll
    volumes:
      - /home/centos/src:/src:z
      - /home/centos/site:/site:z
    ports:
      - "4000:4000"
    state: started
```

avec pour Dockerfile
```dockerfile
FROM ruby:3.0
RUN gem install jekyll bundler
WORKDIR /src
```

3. Ensuite, on prépare le terrain pour Jenkins:
```yaml=
- name: creation depots
  file:
    name:
      - /home/centos/www
      - /home/centos/www_converted
      - /home/centos/jenkins
    state: directory

- name: envoi cle ssh
  copy:
    src: /home/centos/.aws/ssh_pair.pem
    dest: /home/centos/jenkins
    mode: '0400'

- name: envoi script bash
  copy:
    src: ./arnaque.sh
    dest: /home/centos/jenkins

- name: envoi script principal template
  template:
    src: converter.j2
    dest: /home/centos/jenkins/converter.sh
    mode: '0755'
```

Les dossiers www et www_converted ont vocation à être chacun un dépot Git: le premier contenant la source, et le deuxième contenant les fichiers convertis.

Le but du script est de prendre chaque fichier markdown du dossier www, de le convertir par Jekyll, et de l'envoyer dans www_converted, en conservant l'arborescence. 

Voici le template converter.j2:

```bash=
#!/bin/bash

FILES="/www/*"
dns_applications="{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"

for f in $FILES
do
	if [[ $f =~ *.md ]] or [[ $f =~ *.markdown ]]
		then 
			mv $f /src/to_be_converted.md
			ssh -i /jenkins/POEIgroupe2.pem centos@$dns_applications bash /home/centos/jenkins/arnaque.sh
			mw /site/to_be_converted.html www_converted/$f
	fi
done
```
et le script arnaque.sh

```bash=
#!/bin/bash

sudo docker start jekyll
sudo docker exec jekyll bundle install
sudo docker exec jekyll bundle exec jekyll build --destination /site
sudo docker stop jekyll
```

Le script converter.sh sera lancé depuis le container Jenkins, et a besoin de se connecter sur gr2_applications par ssh pour pouvoir lancer la conversion fichier par fichier avec des appels répétés de arnaque.sh.

4. La dernière étape est de lancer enfin le container Jenkins.
```yaml=
- name: installer container jenkins
  docker_container:
    name: jenkins
    image: jenkins/jenkins:lts-jdk11
    state: started
    ports:
     - "{{ jenkins_port }}:{{ jenkins_port }}"
    volumes:
      - jenkins_home:/var/jenkins_home
      - /home/centos/src:/src:z
      - /home/centos/site:/site:z
      - /home/centos/www:/www:z
      - /home/centos/www_converted:/www_converted:z
  register: jenkins_container
```

On monte tous les volumes nécessaires au bon fonctionnement du script sur le container. Il reste enfin à configurer Jenkins. La configuration pourrait être faite manuellement, mais pour ne pas avoir à installer un navigateur, on peut utiliser le playbook suivant:

```yaml=
- name: token admin initial
  shell: docker exec {{jenkins_container.container.hostname}} cat /var/jenkins_home/secrets/initialAdminPassword
  register: jenkins_admin_password

- name: authentification crumb
  uri:
    url: 'http://localhost:{{ jenkins_port }}/crumbIssuer/api/xml?xpath=concat(//crumbRequestField,":",//crumb)'
    user: admin
    password: '{{ jenkins_admin_password.stdout }}'
    force_basic_auth: yes
    return_content: yes
  register: crumb

- name: creation admin jenkins
  uri:
    method: POST
    url: 'http://localhost:{{ jenkins_port }}/securityRealm/createAccountByAdmin'
    user: admin
    password: '{{ jenkins_admin_password.stdout }}'
    force_basic_auth: yes
    follow_redirects: all
    headers:
      Jenkins-Crumb: '{{ crumb.content.split(":")[1] }}'
      Cookie: '{{ crumb.set_cookie }}'
    body: 'username={{ jenkins_user }}&password1={{ jenkins_pass }}&password2={{ jenkins_pass }}&fullname={{ jenkins_fullname }}&email={{ jenkins_email }}'
```

Il ne reste plus qu'à créer la tâche qui execute converter.sh !

### 6. Configuration machine sauvegarde

La machine sauvegarde permettra de sauvegarder les données importants émanant de la machine contenant les applications. Ainsi, la machine "applications" est le serveur NFS, et la machine "sauvegarde" sera la machine cliente NFS.

#### i. Configuration NFS
La configuration du NFS sera faite avec Ansible, dans le rôle "NFS, dont l'arborescence est montrée ci-dessous :

![](https://i.imgur.com/f5lFjeG.png)

Ici, nous avons utilisé 3 playbooks. Le premier (nfs_serveur.yml) créera le serveur NFS, le deuxieme (nfs_client.yml) configurera le client NFS, et le troisième (main.yml) permettra d'appeler les 2 autres playbooks et fera office de lanceur. Les voici:

```yaml=
---

# nfs_server.yml

- tasks:
    - name: installer nfs-utils
      yum:
        name: nfs-utils
        state: latest

      notify:
        - demarrer nfs
        - autoriser nfs dans firewalld
        - redemarrer firewalld

    - name: créer le dossier de montage serveur
      file:
        path: /nfs_sauvegarde
        state: directory
        owner: root
        group: root
        mode: '0775'

    - name: copie du fichier /etc/exports
      template:
        src: /etc/ansible/roles/nfs/templates/exports.j2
        dest: /etc/exports
        owner: root
        group: root
        mode: '0644'

    - name: redemarrer nfs
      service:
        name: nfs-server
        state: restarted

    - name: commande exportfs pour tout rafraichir
      command: exportfs -var

...
```

```yaml=
---

# nfs_client.yml

- tasks:
    - name: Installation de nfs-utils
      yum:
        name:
          - nfs-utils
          - nfs4-acl-tools
        state: latest
      notify:
        - démarrer nfs
        - ajouter nfs dans firewalld
        - redemarrer firewalld

    - name: Création du point de montage
      file:
        path: /mnt/sauvegarde
        state: directory
        owner: root
        group: root
        mode: '0775'

    - name: Montage du volume
      mount:
        path: /mnt/sauvegarde
        src: {{ ansible_local.ip_facts.applications.ip }}:/nfs_sauvegarde
        fstype: nfs
        state: mounted
      
...      
```





Le fichier handlers/main.yml contient les commandes nécessaires aux deux playbooks (demarrage des services, configuration de Firewalld).

```yaml=
--
# handlers file for nfs

handlers:
  - name: demarrer nfs
    service:
      name:
        - nfs-server
        - rpcbind
      enabled: yes

  - name: autoriser nfs dans firewalld
    firewalld:
      name:
        - nfs
        - rpc-bind
        - mountd
      permanent: yes
      state: enabled

  - name: redemarrer firewalld
    service:
      name: firewalld
      state: reloaded

...

```

Le fichier templates/exports.j2 permet de copier dans /etc/exports du serveur NFS le fichier contenant le dossier de sauvegarde, les utilisateurs autorisés, ainsi que les permissions: `/nfs_sauvegarde *(rw)`

---

## III. Implémentations des fonctionnalités en dehors de l architecture machine

### 1. Traduction de la documentation en Latex

Pour traduire la documentation en Latex et générer un pdf du code latex en question nous avons réalisé les tâches suivantes.
* création d'une machine virtuelle avec un disque dur système de 32 Go pour acceuillir le conteneur [wikito](https://github.com/Witiko/markdown) qui est très volumineux
* executions des commandes
`docker run --rm -i witiko/markdown markdown-cli hybrid=true < documentation_v1.md`
`sudo apt install texlive`
`pdftex documentation_v1.tex`

Bien sûr il ne s'agit là que de la première étape de traduction et de nombreux arguments pourraient être transmis à la commande pdftex pour améliorer le rendu du document latex généré.

[Retour au sommaire](#Sommaire)