# Sécurisation d'un serveur Linux sous debian
:robot_face: Romain CARRE
:angel: Benjamin INGLEDEW
:black_joker: Rahiti PETIT
## Présentation
Ce document à pour but de présenter les mesures de sécurité par ordre de priorité donné par l'ANSSI. Ce document démontre aussi comment mettre en place ses mesures.
## Environnement
Toutes les mesures on été misent en place sur une machine virtuelle Debian 11.5.0
## Analyses initales
Suite à l'installation du serveur, la première étape à réaliser est un lancement de scan `debsecan` et `lynis` afin de vérifier le niveau de sécurité initial du serveur.
*Analyse `debsecan`*
```sh
debsecan --suite $(lsb_release --codename --short) --only-fixed --format detail
# Returns nothing
debsecan --suite $(lsb_release --codename --short) --format detail
# CVE-2022-3037
# Use After Free in GitHub repository vim/vim prior to 9.0.0322. ...
# installed: xxd 2:8.2.2434-3+deb11u1
# (built from vim 2:8.2.2434-3+deb11u1)
# [...]
```
*Analyse `lynis`*
```sh
/usr/sbin/lynis audit system | ansi2html -la > report.html
# Hardening index : 61 [############ ]
```
Le scan de Lynis nous à mis une note de 61, nous allons mettre en place des mesures de durcissement afin d'augmenter ce score.
## Début du hardening
Le hardening est effectué en se basant sur nos expériences personnelles et principalement sur différents référentiels de l'ANSSI:
* https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf
* https://www.ssi.gouv.fr/uploads/2014/01/NT_OpenSSH.pdf
* https://www.ssi.gouv.fr/uploads/2013/05/anssi-guide-recommandations_mise_en_oeuvre_site_web_maitriser_standards_securite_cote_navigateur-v2.0.pdf
### Repartitionnement
Niveau de durcissement : `I`
Cette section consiste à modifier les options des différents points de montage (voir page 16 du [référentiel](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf)) de sorte à restreindre certains droits
* Cacher les processus des autres utilisateurs ([tutorial](https://www.cyberciti.biz/faq/linux-hide-processes-from-other-users/))
```bash
echo 'proc /proc proc defaults,nosuid,nodev,noexec,relatime,hidepid=2 0 0' >> /etc/fstab
```
* Retirer les SUID et les droits d'exécution des programmes dans `/dev/shm` ([tutorial](https://unix.stackexchange.com/questions/670362/mounting-dev-shm-with-noexec))
```bash
echo 'tmpfs /dev/shm tmpfs defaults,nodev,nosuid,noexec 0 0' >> /etc/fstab
```
### Modification de `UMASK` et des droits des répertoires `/home`
Niveau de durcissement : `R`
La modification de `UMASK` et des droits des répertoires `/home` est important pour le durcissement du serveur. En effet, cela empêche que différents utilisateurs voient les documents des autres utilisateurs.
Les dossiers présents dans `/home` doivent être lisible seulement par les utilisateurs auxquels ils appartiennent. Cette mesure empêche d'avoir la main sur des fichiers potentiellement sensibles et à caractère personnel.
```bash
chmod 770 /home/*
sed -i 's/^UMASK.*/UMASK 0077/g' /etc/login.defs
```
Ici, nous commençons par changer les droits des répertoires déjà existants avant de modifier le fichier de configuration utilisé lors de la création de nouveaux utilisateurs.
### Modification de la configuration SSH
Niveau de durcissement : `I`
Les modifications au niveau de la configuration SSH sont mises en place afin d'éviter une connexion **root** et renforce le système d'authentification.
En mettant en place les mesures suivantes, nous rajoutons une couche de sécurité supplémentaire au service SSH
Les méthodes utilisées
* Désactiver la connexion de **root** ([tutorial](https://mediatemple.net/community/products/dv/204643810/how-do-i-disable-ssh-login-for-the-root-user))
```bash
# /etc/ssh/sshd_config
PermitRootLogin no
```
* Obliger l'authentification par clé **ET** mot de passe ([tutorial : method 2](https://askubuntu.com/questions/1019999/key-based-ssh-login-that-requires-both-key-and-password))
```bash
# /etc/ssh/sshd_config
#PasswordAuthentication no
AuthenticationMethods "publickey,password"
```
* Autorisation de la connexion à une liste d'utilisateurs à partir d'une adresse précise
*Dans le cadre de ce projet, nous allons autoriser seulement notre machine à se connecter au serveur*
```bash
# /etc/ssh/sshd_config
AllowUsers debian@10.0.2.2
```
### Sécurisation SFTP
Niveau de durcissement : `R`
Ici, nous allons faire en sorte de que restreindre les accès au serveur SFTP.
Nous autorisons l'utilisateur `ftp-user` à utiliser seulement le serveur SFTP. Dans notre scénario, cet utilisateur a seulement besoin de récupérer ou déposer des fichiers. Obtenir un shell sur le serveur n'est donc pas nécessaire.
```bash
# create 1 user that can connect to SFTP only
useradd ftp-user -m -s /bin/false
# /etc/ssh/sshd_config
Subsystem sftp /usr/lib/openssh/sftp-server
Match Group ftp-user
ChrootDirectory /srv/sftp
ForceCommand internal-sftp
AllowTCPForwarding no
X11Forwarding no
```
### Désactivation de cron
Niveau de durcissement : `R`
Sur notre serveur (du moins pour le moment) `cron` n'est d'aucune utilité et peut être désactivé. Cela empêche un utilisateur malveillant d'exploiter une commande (souvent exécutée en tant que **root**) afin de faire de la latéralisation voire de l'élévation de privilèges.
```bash
systemctl stop cron
systemctl disable cron
```
## Installation d'un paquet de mises à jour automatiques
Niveau de durcissement : `I`
En ayant des paquets à jours, nous pouvons installer les patchs dès leur publication. Les paquets suivants permettent d'effectuer les mises à jour de manière automatique.
```bash
apt-get install unattended-upgrades apt-listchanges
```
## Désactivation du chargement des modules noyau
Niveau de durcissement : `R`
Cette mesure empêche un attaquant de charger des modules noyaux malveillants
Ajouter ceci dans le fichier `/etc/sysctl.conf`
```bash
kernel.modules_disabled = 1
```
## Paramétrage des sysctl
Niveau de durcissement : `I`
Cette mesure nous permet de configurer des paramètres réseaux et systèmes au niveau du noyau dans le but de désactiver ceux qui sont inutilisés ou qui pourrait faire office de vecteur d'attaque.
Ajouter ceci dans le fichier `/etc/sysctl.conf`
```bash
# Pas de routage entre les interfaces
net.ipv4.ip_forward = 0
# Filtrage par chemin inverse
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
# Ne pas envoyer de redirections ICMP
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# Refuser les paquets de source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Ne pas accepter les ICMP de type redirect
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Loguer les paquets ayant des IPs anormales
net.ipv4.conf.all.log_martians = 1
# RFC 1337
net.ipv4.tcp_rfc1337 = 1
# Ignorer les réponses non conformes à la RFC 1122
net.ipv4. icmp_ignore_bogus_error_responses = 1
# Augmenter la plage pour les ports éphémères
net.ipv4.ip_local_port_range = 32768 65535
# Utiliser les SYN cookies
net.ipv4.tcp_syncookies = 1
# Désactiver le support des "router solicitations"
net.ipv6.conf.all.router_solicitations = 0
net.ipv6.conf.default.router_solicitations = 0
# Ne pas accepter les "router preferences" par "router advertisements"
net.ipv6.conf.all.accept_ra_rtr_pref = 0
net.ipv6.conf.default.accept_ra_rtr_pref = 0
# Pas de configuration auto des prefix par "router advertisements"
net.ipv6.conf.all.accept_ra_pinfo = 0
net.ipv6.conf.default.accept_ra_pinfo = 0
# Pas d'apprentissage du routeur par défaut par "router advertisements"
net.ipv6.conf.all.accept_ra_defrtr = 0
net.ipv6.conf.default.accept_ra_defrtr = 0
# Pas de configuration auto des adresses à partir des "router advertisements "
net.ipv6.conf.all.autoconf = 0
net.ipv6.conf.default.autoconf = 0
# Ne pas accepter les ICMP de type redirect
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
# Refuser les packets de source routing
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
# Nombre maximal d'adresses autoconfigurées par interface
net.ipv6.conf.all.max_addresses = 1
net.ipv6.conf.default.max_addresses = 1
```
## Paramétrage des sysctl système
Niveau de durcissement : `I`
Ajouter ceci dans le fichier `/etc/sysctl.conf`
```bash
# Désactivation des SysReq
kernel.sysrq = 0
# Pas de core dump des exécutables setuid
fs.suid_dumpable = 0
# Interdiction de déréférencer des liens vers des fichiers dont
# l'utilisateur courant n'est pas le propriétaire
# Peut empêcher certains programmes de fonctionner correctement
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
# Activation de l'ASLR
kernel.randomize_va_space = 2
# Interdiction de mapper de la mémoire dans les adresses basses (0)
vm.mmap_min_addr = 65536
# Espace de choix plus grand pour les valeurs de PID
kernel.pid_max = 65536
# Obfuscation des adresses mémoire kernel
kernel.kptr_restrict = 1
# Restriction d'accès au buffer dmesg
kernel.dmesg_restrict = 1
# Restreint l'utilisation du sous système perf
kernel.perf_event_paranoid = 2
kernel. perf_event_max_sample_rate = 1
kernel.perf_cpu_time_max_percent = 1
```
## Paramétrage du module Yama
Niveau de durcissement : `R`
Yama permet d'empêcher la manipulation des processus en cours avec `ptrace` et potentiellement voler des identifiants utilisateurs sans avoir à faire de phishing
Ajouter ceci dans le fichier `/etc/sysctl.conf`
```bash
# /etc/sysctl.conf
kernel.yama.ptrace_scope = 3
```
Le simple fait de paramétrer Yama nous a permis de gagner 2 points de score Lynis
## Désactivation des comptes utilisateurs non utilisés
Niveau de durcissement : `R`
Désactiver les comptes utilisateurs non utilisés permet d'empêcher ces utilisateurs de se connecter et ainsi restreindre leur accès au serveur
```bash
usermod -L www-data
usermod -s /bin/false www-data
```
## Expiration des sessions utilisateurs
Niveau de durcissement : `R`
Cette mesure permet de déconnecter une session laissée ouverte trop longtemps afin d'empêcher l'accès à partir d'un compte compromis
Ici, nous modifions les fichiers chargés au démarrage d'un shell `bash` de sorte à set la variable d'environnement ([page 28](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf#page=28))
```bash
echo 'export TMOUT=1800' >> /home/debian/.bashrc
echo 'export TMOUT=1800' >> /etc/skel/.bashrc
```
## Sécurisation de modules utilisant PAM
Niveau de durcissement : `R`
Nous changeons la politique de mot de passe du programme `passwd` de sorte que les mots de passe fassent au minimum 12 caractères et contenant 3 classes de caractères différents.
Grâce à cette mesure, si le hash d'un mot de passe est récupéré, il sera difficile à casser et ralentira grandement un attaquant pendant sa tentative de compromission d'un compte utilisateur
```bash
apt install libpam-cracklib
# /etc/pam.d/passwd
password required pam_cracklib.so minlen=12 minclass=3 \
dcredit=0 ucredit=0 lcredit=0 \
ocredit=0 maxrepeat=1 \
maxsequence=1 gecoscheck \
reject_username enforce_for_root
```
Nous bloquons les comptes pendant 5 minutes après 3 échecs d'authentification. Cela permet non seulement de détecter les tentatives d'attaques par dictionnaire mais aussi de les empêcher.
```bash
# /etc/pam.d/sshd ; /etc/pam.d/login
auth required pam_tally.so deny=3 lock_time=300
```
Nous changeons l'algorithme de hachage des mots de passe afin d'utiliser **SHA-512**
Cette mesure rend la phase de bruteforce hors-ligne beaucoup plus lente car l'algorithme est complexe
```bash
# /etc/pam.d/common-password
password required pam_unix.so obscure sha512 rounds=65536
```
## Désactivation des SUID inutilisés
Niveau de durcissement : `I`
Mettre en place cette mesure empêche un attaquant d'exécuter un programme en tant qu'un autre utilisateur et potentiellement prendre possession du compte en question si le programme est vulnérable.
En utilisant [cette liste](https://www.ssi.gouv.fr/uploads/2016/01/linux_configuration-fr-v1.2.pdf#page=35) nous pouvons retirer certains droits SUID qui peuvent être dangereux dans certains cas de figure
```bash
chmod -s /usr/bin/mount
chmod -s /usr/bin/umount
chmod -s /usr/bin/chsh
chmod -s /usr/bin/gpasswd
chmod -s /usr/bin/newgrp
chmod -s /usr/bin/passwd
chmod -s /usr/bin/crontab
chmod -s /usr/bin/chage
chmod -s /usr/bin/wall
chmod -s /usr/bin/chage
```
## Répertoires temporaires dédiés aux comptes
Niveau de durcissement : `I`
Créer un répertoire temporaire dédié pour chaque utilisateur permet de restreindre l'accès à des fichiers potentiellement sensibles à n'importe qui.
```bash
apt install libpam-tmpdir -y
```
## Journalisation de l'activité par auditd
Niveau de durcissement : `R`
En journalisation l'activité d'un serveur par `auditd` nous pouvons avoir des traces sur ce qui est fait dessus et ainsi détecter des comportements anormaux voire malveillants.
```bash
cat > /etc/audit/audit.rules << EOF
-w /sbin/insmod -p x
-w /sbin/modprobe -p x
-w /sbin/rmmod -p x
-w /etc/ -p wa
-a exit ,always -S mount -S umount2
-a exit ,always -S ioperm -S modify_ldt
-a exit ,always -S get_kernel_syms -S ptrace
-a exit ,always -S prctl
-a exit ,always -F arch=b64 -S unlink -S rmdir -S rename
-a exit ,always -F arch=b64 -S creat -S open -S openat -F exit=-EACCESS
-a exit ,always -F arch=b64 -S truncate -S ftruncate -F exit=-EACCESS
-e 2
EOF
```
## Groupe dédié à l'utilisation de `sudo`
Niveau de durcissement : `I`
On créé le groupe **sudogrp** et donne accès au binaire seulement aux utilisateurs membres de ce groupe (ici, **debian** a le droit d'utiliser `sudo`)
Cela permet d'avoir la main sur les utilisateurs autorisés à utiliser `sudo`
```bash
groupadd sudogrp
usermod -a -G sudogrp debian
chgrp sudogrp $(which sudo)
chmod 4750 $(which sudo)
```
## Authentification des utilisateurs exécutant `sudo`
Niveau de durcissement : `M`
Il ne faut jamais mettre la directive `NOPASSWD` dans une règle `sudo` car cela permet de bypass l'authentification des utilisateurs pour exécuter une commande privilégiée
## Arguments explicites dans les spécifications `sudo`
Niveau de durcissement : `I`
Il n'est pas recommandé d'utiliser de wildcard telles que `*` dans les droits `sudo` car cela pourrait permettre d'ajouter des arguments inattendus dans la commande. L'absence
d’arguments auprès d'une commande doit être spécifiée par la présence d’une chaîne
vide `""`
## Usage de sudoedit
Niveau de durcissement : `I`
Afin de ne pas corrompre l'utilisation de la commande `sudo` et le fichier `sudoers` il est vital de passer par la commande `sudoedit` lorsque l'on souhaite modifier les privilèges des utilisateurs
## AppArmor
Niveau de durcissement : `E`
Ce logiciel permet d'associer à chaque programme un profil de sécurité qui restreint les capacités de celui-ci
C'est un outil qui permet de verrouiller les applications en limitant strictement leur accès aux seules ressources auxquelles elles ont droit sans perturber leur fonctionnement.
### Installation
L'installation et la configuration de AppArmor a été faite en se basant sur [ce blog post](https://www.malekal.com/apparmor-debian-ubuntu-installation-configuration/)
```bash
apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extra
mkdir -p /etc/default/grub.d
echo 'GRUB_CMDLINE_LINUX_DEFAULT="$GRUB_CMDLINE_LINUX_DEFAULT apparmor=1 security=apparmor"' > /etc/default/grub.d/apparmor.cfg
cp /usr/share/apparmor/extra-profiles/* /etc/apparmor.d/
aa-enforce /etc/apparmor.d/*
```
### Sécurisation de nginx et php/fpm
```bash
cat > /etc/apparmor.d/usr.sbin.nginx << EOF
#include <tunables/global>
/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/nameservice>
#include <abstractions/apache2-common>
#include <abstractions/nis>
#include <abstractions/openssl>
#include <abstractions/ssl_keys>
capability dac_override,
capability dac_read_search,
capability net_bind_service,
capability setgid,
capability setuid,
network inet tcp,
# pour écrire dans la console
/dev/pts/[0-9] rw,
# binary, pid
/usr/bin/nginx mr,
/run/nginx.pid rw,
/usr/bin/php8.1 mrix,
# configuration
/etc/nginx r,
/etc/nginx/** rl,
/usr/share/nginx r,
/usr/share/nginx/** r,
/etc/ssl r,
/etc/ssl/openssl.cnf r,
# cache
owner /var/cache/nginx rw,
owner /var/cache/nginx/** rw,
owner /var/lib/nginx rw,
owner /var/lib/nginx/** rw,
# webroot
owner /var/www/html rw,
owner /var/www/html/** rw,
# logs
owner /var/log/nginx/* rw,
}
EOF
aa-enforce /usr/sbin/nginx
cat >> /etc/apparmor.d/usr.sbin.php-fpm << EOF
#include <tunables/global>
profile php-fpm /usr/sbin/php-fpm{7.4,8.0,8.1} flags=(attach_disconnected) {
#include <abstractions/base>
#include <abstractions/openssl>
#include <abstractions/ssl_certs>
capability setuid,
capability setgid,
capability chown,
capability kill,
capability dac_read_search,
capability dac_override,
network unix,
audit deny network inet,
# CONF
/etc/php/** rl,
/etc/hosts r,
/etc/host.conf r,
/etc/gai.conf r,
/etc/resolv.conf r,
/etc/nsswitch.conf r,
/etc/ImageMagick-6/ r,
/etc/ImageMagick-6/** r,
# php libraries
/usr/share/php*/ r,
/usr/share/php*/** mr,
/usr/share/ImageMagick-6 r,
/usr/share/ImageMagick-6/** r,
# Xlibs
/usr/X11R6/lib{,32,64}/lib*.so* mr,
# php extensions
/usr/lib{64,}/php/*/*.so mr,
# ICU (unicode support) data tables
/usr/share/icu/*/*.dat r,
/proc/@{pid}/attr/current rw,
/sys/devices/system/node r,
/sys/devices/system/node/** r,
/tmp/ rw,
/tmp/** rwk,
# Sessions
/var/lib/php/sessions rw,
/var/lib/php/sessions/** rwk,
# LOG
/var/log/php7.4-fpm.log rw,
/var/log/php8.0-fpm.log rw,
/var/log/php8.1-fpm.log rw,
# SOCKET
/run/php/php7.4-fpm.sock rwlk,
/run/php/php8.0-fpm.sock rwlk,
/run/php/php8.1-fpm.sock rwlk,
/run/php/php7.4-fpm.pid rwlk,
/run/php/php8.0-fpm.pid rwlk,
/run/php/php8.1-fpm.pid rwlk,
# CACHE
owner /var/cache/opcache rw,
# WEBROOT
owner /var/www/** rwk,
}
EOF
aa-enforce /usr/sbin/php-fpm8.1
```
Avec cette configuration, les webshells php tels que `system($_GET['cmd']);` seront bloqués par AppArmor
## nftables (port 22 80 443)
Niveau de durcissement : `I`
`nftables` est un sous-système du noyau Linux fournissant le filtrage et la classification des paquets.
Grâce à celui-ci, nous allons seulement autoriser la communication avec les ports 22,80 et 443. Pour cela nous devons commencer par créer les règles.
```bash
nft add rule my_incoming_filter input tcp dport 80 accept
nft add rule my_incoming_filter input tcp dport 443 accept
nft add rule my_incoming_filter input tcp dport 22 accept
nft add rule my_incoming_filter input drop
```
On autorise donc les ports 22,80 et 443 en entrée grâce à `tcp dport` pour destination port qui permet de cibler le port de destination des paquets et la commande `drop` permet de refuser tous les autres ports.
Pour voir le changement que nous avons fait il suffit de taper
```bash
nft list table ip my_incoming_filter
```