-
Notifications
You must be signed in to change notification settings - Fork 0
TP6
layout: default title: "TP6 : Sécurisation du service web public" parent: Index des TPs nav_exclude: false has_children: false nav_order: 6 modifié le: 2024-03-14
Documentez et faites le point ici sur l'état de sécurisation de votre serveur.
-
Mises à jour régulières :
- Le serveur est maintenu à jour avec les dernières mises à jour de sécurité via
sudo apt update.
- Le serveur est maintenu à jour avec les dernières mises à jour de sécurité via
-
Désactivation des services inutiles :
-
Un audit des services actifs a été effectué avec
sudo systemctl list-units --type=service --state=running. Puis viasudo systemctl disable <nom_service>, pour empêcher le service de se lancer au prochain démarrage et unsudo systemctl stop <nom_service>, pour arrêter le service si en cours d'utilisation, les services ci-dessous ont été désactivés:
Remarque : Le service
qemu-guest-agent.servicen'a pas été désactivé car on ne sait pas si le VPS est une VM ou non et donc ne pas causer de problème. -
-
Contrôle des ports ouverts :
- Les ports ouverts ont été vérifiés avec
sudo netstat -tulnp,sudo ss -tulnpetsudo nmap -sS -p- <IP_VPS>.
Uniquement les ports destinés aux TPs ont été ouverts, les autres sont fermés.
- Les ports ouverts ont été vérifiés avec
De plus fail2ban a également été configuré, voici un extrait du fichier jail.local :
[sshd]
enabled = true
port = x
filter = sshd
maxretry = 3
findtime = 5m
bantime = 30m
bantime.increment = true
bantime.factor = 2
bantime.rndelay = 5m
bantime.maxtime = 72h
En addition, dans le fichier sshd_config, les paramètres de sécurité ont aussi été configurés, notamment PermitRootLogin no et PasswordAuthentication no. On également ajouté les paramètre AllowUsers <nom_utilisateur> afin de permettre l'accès uniquement aux utilisateurs autorisés via leur clé SSH comme seul moyen de connexion au serveur.
-
Vérification de la version installée :
- La version de Docker est vérifiée via
docker --versionet maintenue à jour.
- La version de Docker est vérifiée via
-
Sécurisation des paramètres de Docker :
- Les paramètres de sécurité sont contrôlés avec
sudo docker info | grep Security. - Restriction des accès à l’API Docker via
/etc/docker/daemon.json:{ "icc": false, "userns-remap": "default" } - Redémarrage du service Docker :
sudo systemctl restart docker
- Les paramètres de sécurité sont contrôlés avec
-
Contrôle des images utilisées :
- Seules des images officielles et sans vulnérabilités sont utilisées (contrôlées via
docker scan <nom_image>). - Le tag
latestest évité afin d’assurer la stabilité.
- Seules des images officielles et sans vulnérabilités sont utilisées (contrôlées via
-
Vérification des ports exposés :
- Commande
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}"exécutée pour vérifier que seuls les ports nécessaires sont exposés. - Un audit complémentaire a été réalisé avec
sudo netstat -tulnp | grep dockeretsudo ss -tulnp | grep docker. - Seul le port 80 est publié pour Nginx, la base de données et PHP restent en réseau interne.
- Commande
-
Nginx :
- Désactivation des versions TLS faibles :
ssl_protocols TLSv1.2 TLSv1.3;
- Masquage des informations sensibles :
server_tokens off;
- Désactivation des versions TLS faibles :
-
Base de données :
- Aucune exposition externe du service MySQL/MariaDB.
- Utilisation d’un utilisateur limité pour les connexions.
- Documentez ici les modifications effectuées sur votre infrastructure pour isoler la base de données.
Le fichier compose.yml a été édité de sorte à :
(1) créer deux réseaux distincts "dmz_net et db_net",
(2) à connecter le serveur web au réseau dmz_net,
(3) à connecter le serveur php au réseau dmz_net et db_net et
(4) à connecter la base de données au réseau db_net.

- Etablissez une procédure de validation de l'isolation de la base de données.
Voici un petit rappel des adresses IP des différents services :

Un ping depuis le serveur web vers le serveur php n'aboutit pas :

Un ping entre le serveur php et la base de données fonctionne :

Un ping entre le serveur web et la base de données échoue également :

- Documentez les modifications effectuées
Dans notre fichier woodytoys.sql, nous rajouter les lignes suivantes en tête du fichier :
CREATE USER IF NOT EXISTS '${MYSQL_USER}'@'%' IDENTIFIED BY '${MYSQL_PASSWORD}';
GRANT SELECT ON woodytoys.* TO '${MYSQL_USER}'@'%';
FLUSH PRIVILEGES; -- Pour appliquer les droitsPS: À noter ici que le fichier sql ne lit pas le fichier .env, il faut donc le modifier manuellement.
- Proposez une procédure de validation du fait que le serveur web ne peut pas effectuer d'opération dangereuse sur la DB.
- Documentez la procédure de backup de votre base de données.
- Si vous avez réalisé le bonus proposé, documentez-le et prouvez son fonctionnement.
Pour le backup plus le plus réaliste que possible, un script backup_db.sh a été ajouté au container mariadb, dans le dossier backups monté sur /backups. Puis un cron a été ajouté au container mariadb pour faire le backup tous les jours à 00h00.
backup_db.sh :
BACKUP_DIR="/backups"
DB_USER="root"
DB_PASS="mypass"
DATE=$(date +\%Y\%m\%d)
BACKUP_FILE="$BACKUP_DIR/all_databases_$DATE.sql.gz"
RETENTION_DAYS=3
mkdir -p "$BACKUP_DIR"
echo "Tentative de dump à $(date)" >> "$BACKUP_DIR"/backup.log 2>&1
/usr/bin/mariadb-dump --all-databases -u$DB_USER -p$DB_PASS | gzip > "$BACKUP_FILE"
echo "Dump terminé à $(date)" >> "$BACKUP_DIR"/backup.log 2>&1
find "$BACKUP_DIR" -type f -name "all_databases_*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup terminé : $BACKUP_FILE"Cron job :
0 0 * * * /bin/bash /backups/backup_db.shVoici la confirmation de la création du backup :
- Documentez brièvement comment accéder aux logs de la DB.
- Montrez par un screenshot la mise en place de votre certificat auto-signé.
Que pense votre navigateur du certificat utilisé ? Pourquoi ? Expliquez la problématique qui est ici mise en évidence.
Le navigateur affiche un avertissement, car le certificat est auto-signé et pas encore validé par une autorité de certification de confiance.
- Documentez la configuration en HTTPS de nginx. Montrez via des screenshots bien choisi qu'elle est fonctionnelle.
Une fois la clé et le certificat genéré et auto-signé, le fichier nginx.conf a ete modifié de sorte que le site www soit accessible via HTTPS. Dans un premier temps on redirige le tarffic du port 80 sur le port 443 comme ci-dessous :
server {
listen 80;
server_name www.l1-7.ephec-ti.be.;
return 301 https://$host$request_uri;
}L'hôte écoute désormais sur le port 443, puis nous utilisons la directive ssl_certificate pour indiquer le chemin vers le certificat et la directive ssl_certificate_key pour indiquer le chemin vers la clé.
server {
listen 443 ssl;
server_name www.l1-7.ephec-ti.be;
...
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
...
}
Documentez et prouvez la mise en place du certificat let's Encrypt sur vos virtuals hosts.
Avant tout, directement dans le conteneur web, nous avons utilisé la commande certbot --nginx -d www.l1-7.ephec-ti.be pour genérer la clé et le certificat.
- Examinez les logs dans le fichier
/var/log/letsencrypt/letsencrypt.log
- Trouvez les trois challenges ACME proposés par let's encrypt, et le token utilisé
Challenges ACME et token utilisé :
{
"identifier": {
"type": "dns",
"value": "www.l1-7.ephec-ti.be"
},
"status": "pending",
"expires": "2025-04-01T10:36:23Z",
"challenges": [
{
"type": "http-01",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall/2301878056/495179551606/rFD9UA",
"status": "pending",
"token": "kGbbds3-Adq88JZ_rI0x3KN_rkpGiC_h5Ghzr0APliQ"
},
{
"type": "dns-01",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall/2301878056/495179551606/_NQ8zA",
"status": "pending",
"token": "kGbbds3-Adq88JZ_rI0x3KN_rkpGiC_h5Ghzr0APliQ"
},
{
"type": "tls-alpn-01",
"url": "https://acme-v02.api.letsencrypt.org/acme/chall/2301878056/495179551606/rOtykQ",
"status": "pending",
"token": "kGbbds3-Adq88JZ_rI0x3KN_rkpGiC_h5Ghzr0APliQ"
}
]
}- Trouvez la configuration nginx temporaire utilisée par certbot pour répondre au challenge
...
-
Quelle est l'URL où se trouve le token sur votre serveur nginx?
-
Voyez-vous le certificat reçu? De combien de parties se compose-t-il?
Le certificat se compose de 2 parties.
-----BEGIN CERTIFICATE-----
MIID
.....
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIE
...
-----END CERTIFICATE-----
- Où sont stockés les fichiers du certificat et de la clé privée générées par
certbot?
Ils sont stockés respectivement aux emplacements /etc/letsencrypt/live/www.l1-7.ephec-ti.be/fullchain.pem et /etc/letsencrypt/live/www.l1-7.ephec-ti.be/privkey.pem
- Vérifiez votre configuration
nginx: qu'est ce qui a changé?
Les valeurs des champs ssl_certificate et ssl_certificate_key ont été remplacé par /etc/letsencrypt/live/www.l1-7.ephec-ti.be/fullchain.pem et /etc/letsencrypt/live/www.l1-7.ephec-ti.be/privkey.pem.
- Vérifiez si votre site web possède à présent un certificat signé par Let's Encrypt et s'il est accepté par votre navigateur
Le certificat est signé par Let's Encrypt et est accepté par le navigateur.
- Examinez le certificat reçu avec l'outil OpenSSL, et identifiez les champs indiquant la signature du CA.
Dans le certificat, nous pouvons voir les champs suivants :
Issuer: C = US, O = Let's Encrypt, CN = E5
Validity
Not Before: Mar 25 09:38:00 2025 GMT
Not After : Jun 23 09:37:59 2025 GMT
Subject: CN = www.l1-7.ephec-ti.be
Subject Public Key Info:
....
Signature Value:
...
- Vérifiez également le statut HTTPS du second Virtual Host :
blog.lx-y.ephec-ti.be. Que se passe-t-il? Comment pouvez-vous corriger ça ?
En essyant d'accéder à https://blog.l1-7.ephec-ti.be, le site redirige automatiquement vers https://www.l1-7.ephec-ti.be.
Il faut alors utiliser certbot pour obtenir un certificat pour les deux domaines et changer la configuration de nginx.conf pour que l'hôte écoute sur le port 443.
Si vous avez réalisé l'obtention d'un certificat wildcard, documentez la procédure et prouvez qu'elle fonctionne sur votre domaine (par ex. via des screenshots).
Dans le conteneur web, on a rentré la commande certbot certonly --manual --preferred-challenges=dns --email <mail> --agree-tos -d \*.l1-7.ephec-ti.be
Suite à celà, on a reçu le _acme-challenge.l1-7.ephec-ti.be avec un token également puis nous avons ensuite placé dans le ficher zone l1-7.zone du dns la ligne suivante : _acme-challenge.l1-7.ephec-ti.be IN TXT token.
On a ensuite changé la configuration de nginx.conf en rajoutant ces deux lignes dnas le bloc http :
http {
...
ssl_certificate /etc/letsencrypt/live/l1-7.ephec-ti.be/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/l1-7.ephec-ti.be/privkey.pem; # managed by Certbot
server{
...
}
server{
...
}
}En vérifiant le certificat sur notre navigateur, on peut voir que maintenant le certificat a désormais un wilcards, qu'il est signé par Let's Encrypt et que c'est tout le domaine qui profite du certificat.