Installation et configuration de Debian 12

sur un serveur dédié ou un VPS

Par Mezalor le 16 août 2023

Configuration des outils de base de Debian

Dans cette partie nous configurerons les outils de bases du système : gestion des mise à jour, réglage de l'heure, ssh, le pare-feu, etc.

On commence par se connecter au serveur fraîchement installé par SSH avec l'utilisateur root et le mot de passe défini à l'étape 11 de la partie précédente.

ssh-keygen -f "/home/alice/.ssh/known_hosts" -R "10.11.12.13"
ssh root@10.11.12.13

Étape 1 (fac) : Durcissement du noyau

Le durcissement du noyau Linux consiste à accroître les mécanismes de protection du noyau. Cela permet à la fois d'apporter des protections ou des contre-mesures à des vulnérabilités logicielles ou matérielles potentielles de la plateforme et réduire dans le même temps la surface d’attaque du noyau, initialement très grande.
On se basera essentiellement sur les recommendations de l'ANSSI (voir [ANSSI R7-14]) aussi détaillées plus simplement ici et . D'autres références sont disponibles : en anglais et en français.

Pour modifier les paramètres du noyau, on utilisera l'outil sysctl. Pour que les changements apportés au noyau soit appliqués au démarrage du système il suffit de créer un fichier de configuration (avec l’extension .conf) dans le répertoire /etc/sysctl.d. Dans ces fichiers de configuration il suffira d'indiquer la clef du paramètre à changer suivie de sa valeur. Pour avoir une liste assez complète des paramètres du noyau vous pouvez aller sur ce site et pour déterminer les paramètre actuel du noyau de votre système on peut utiliser la commande sysctl -a.

On commence par les paramètres du noyau.

nano /etc/sysctl.d/10-kernel.conf
/etc/sysctl.d/10-kernel.conf
# Désactive les combinaisons de touches magiques (Magic System Request Key)
kernel.sysrq = 0
# Default : kernel.sysrq = 438

# Active l'ASLR
kernel.randomize_va_space = 2
# Default : kernel.randomize_va_space = 2

# Cache les adresses noyau dans /proc et les différentes autres interfaces,
# y compris aux utilisateurs privilégiés
kernel.kptr_restrict = 2
# Default : kernel.kptr_restrict = 0

# Restreint l'accès au buffer dmesg
kernel.dmesg_restrict = 1
# Default : kernel.dmesg_restrict = 1

# Interdit l'accès non privilégié à l'appel système perf_event_open (). Avec une
# valeur plus grande que 2, on impose la possession de CAP_SYS_ADMIN , pour pouvoir
# recueillir les évènements perf.
kernel.perf_event_paranoid = 3
# Default : kernel.perf_event_paranoid = 3

# Restreint l'utilisation du sous -système perf
kernel.perf_cpu_time_max_percent = 5
# Default : kernel.perf_cpu_time_max_percent = 25

# Restreint l'usage du BPF noyau aux utilisateurs privilégiés
kernel.unprivileged_bpf_disabled = 1
# Default : kernel.unprivileged_bpf_disabled = 2

# ptrace protections
kernel.yama.ptrace_scope = 1
# Default : kernel.yama.ptrace_scope = 0

# Désactive kexec
kernel.kexec_load_disabled = 1
# Default : kernel.kexec_load_disabled = 0

# Arrête complètement le système en cas de comportement inattendu du noyau Linux
kernel.panic_on_oops = 1
# Default : kernel.panic_on_oops = 0

# Interdit le chargement des modules noyau (sauf ceux déjà chargés à ce point)
kernel.modules_disabled = 1
# Default : kernel.modules_disabled = 0

On configure ensuite le réseau.

nano /etc/sysctl.d/20-network.conf
/etc/sysctl.d/20-network.conf
# Pas de routage entre les interfaces. Cette option est spéciale et peut
# entrainer des modifications d'autres options. En plaçant cette option au plus
# tôt , on s'assure que la configuration des options suivantes ne change pas.
net.ipv4.ip_forward = 0
# Default : net.ipv4.ip_forward = 0

# Vérifie que l'adresse source des paquets reçus sur une interface donnée aurait
# bien été contactée via cette même interface. À défaut, le paquet est ignoré.
# Selon l'usage, la valeur 1 peut permettre d'accroître la vérification à
# l'ensemble des interfaces, lorsque l'équipement est un routeur dont le calcul de
# routes est dynamique. Le lecteur intéressé est renvoyé à la RFC3704 pour tout
# complément concernant cette fonctionnalité.
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.rp_filter = 1
# Default : net.ipv4.conf.all.rp_filter = 0

# Refuse la réception de paquet ICMP redirect. Le paramétrage suggéré de cette
# option est à considérer fortement dans le cas de routeurs qui ne doivent pas
# dépendre d'un élément extérieur pour déterminer le calcul d'une route. Même
# pour le cas de machines non-routeurs, ce paramétrage prémunit contre les
# détournements de trafic avec des paquets de type ICMP redirect.
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
# Default : net.ipv6.conf.all.accept_redirects = 1
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
# Default : net.ipv4.conf.all.secure_redirects = 1
net.ipv4.conf.all.shared_media = 0
net.ipv4.conf.default.shared_media = 0
# Default : net.ipv4.conf.all.shared_media = 1

# Cette option ne doit être mise à 1 que dans le cas d'un routeur, car pour ces
# équipements l'envoi de ICMP redirect est un comportement normal. Un équipement
# terminal n'a pas de raison de recevoir un flux dont il n'est pas destinataire et
# donc d'émettre un paquet ICMP redirect.
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.send_redirects = 0
# Default : net.ipv4.conf.all.send_redirects = 1

# Refuse les informations d'en-têtes de source routing fournies par le paquet
# pour déterminer sa route.
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
# Default : net.ipv6.conf.all.accept_source_route = 0

# Loguer les paquets ayant des IPs anormales
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
# Default : net.ipv4.conf.all.log_martians = 0

# RFC 1337
net.ipv4.tcp_rfc1337 = 1
# Default : net.ipv4.tcp_rfc1337 = 0

# Ignorer les réponses non conformes à la RFC 1122
net.ipv4.icmp_ignore_bogus_error_responses = 1
# Default : 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
# Default : net.ipv4.ip_local_port_range = 32768 60999

# Utilise les SYN cookies. Cette option permet la prévention d'attaque de
# type SYN flood.
net.ipv4.tcp_syncookies = 1
# Default : net.ipv4.tcp_syncookies = 1

# Désactivation de l'IPv6
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
# Default : net.ipv6.conf.all.disable_ipv6 = 0

# Refuse le routage de paquet dont l'adresse source ou destination est celle de la
# boucle locale. Cela interdit l'émission de paquet ayant comme source 127/8.
net.ipv4.conf.all.route_localnet = 0
# Default : net.ipv4.conf.all.route_localnet = 0

# Ignore les "ICMP broadcasts" pour éviter de participer aux attaques Smurf
net.ipv4.icmp_echo_ignore_broadcasts = 1
# Default : net.ipv4.icmp_echo_ignore_broadcasts = 1

# Atténuation de l'effet de dispersion du JIT noyau au coût d'un compromis sur
# les performances associées.
net.core.bpf_jit_harden = 2
# Default : net.core.bpf_jit_harden = 0

On configure ensuite la mémoire virtuelle. Il ne s'agit pas ici d'augmenter la sécurité mais d'optimiser les performances de la mémoire SWAP en fonction de l'utilisation du serveur. Pour plus d'information voir ce document.

nano /etc/sysctl.d/30-memory.conf
/etc/sysctl.d/30-memory.conf
vm.swappiness = 15
# Default : vm.swappiness = 60
vm.dirty_ratio = 20
# Default : vm.dirty_ratio = 20
vm.dirty_background_ratio = 10
# Default : vm.dirty_background_ratio = 10

On continue par les propriétés des systèmes de fichiers.

nano /etc/sysctl.d/40-filesystem.conf
/etc/sysctl.d/40-filesystem.conf
# Désactive la création de coredump pour les exécutables setuid
fs.suid_dumpable = 0
# Default : fs.suid_dumpable = 0

# Disponible à partir de la version 4.19 du noyau Linux , permet d'interdire
# l'ouverture des FIFOS et des fichiers "réguliers" qui ne sont pas la propriété
# de l'utilisateur dans les dossiers sticky en écriture pour tout le monde.
fs.protected_fifos = 2
# Default : fs.protected_fifos = 1
fs.protected_regular = 2
# Default : fs.protected_regular = 2

# Restreint la création de liens symboliques à des fichiers dont l'utilisateur
# est propriétaire. Cette option fait partie des mécanismes de prévention contre
# les vulnérabilités de la famille Time of Check - Time of Use (Time of Check -
# Time of Use)
fs.protected_symlinks = 1
# Default : fs.protected_symlinks = 1

# Restreint la création de liens durs à des fichiers dont l'utilisateur est
# propriétaire. Ce sysctl fait partie des mécanismes de prévention contre les
# vulnérabilités Time of Check - Time of Use , mais aussi contre la possibilité de
# conserver des accès à des fichiers obsolètes
# fs.protected_hardinks = 1
# Default : n'existe pas

On termine par diverses propriétés.

nano /etc/sysctl.d/50-other.conf
/etc/sysctl.d/50-other.conf
# userfaultfd() est un appel système qui peut être abusé également,
# ce paramètre le restreint à la capacité CAP_SYS_PTRACE
vm.unprivileged_userfaultfd = 0
# Default : vm.unprivileged_userfaultfd = 0

# Cela limite le chargement des "TTY line disciplines" à la capacité CAP_SYS_MODULE
# pour empêcher les attaquants non privilégiés de charger des "line disciplines"
# vulnérables avec l'ioctl TIOCSETD
dev.tty.ldisc_autoload = 0
# Default : dev.tty.ldisc_autoload = 1

On peut finalement configurer les paramètres du noyau lors du démarrage en éditant le fichier de configuration /etc/default/grub de GRUB et en modifiant la ligne GRUB_CMDLINE_LINUX_DEFAULT. Cela fonction uniquement si GRUB a été installé comme chargeur d'amorçage. Pour EFISTUB il faudra modifier le fichier /boot/efi/startup.nsh.

nano /etc/default/grub
/etc/default/grub
GRUB_DEFAULT=0
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="quiet slab_nomerge=yes init_on_free=1 mce=0 spec_store_bypass_disable=seccomp spectre_v2=on pti=on"
GRUB_CMDLINE_LINUX=""
  • slab_nomerge=yes (CONFIG_SLAB_MERGE_DEFAULT) : désactive la fusion de caches slabs (allocations mémoire dynamiques) de taille identique. Cette fonctionnalité permet de différentier les allocations entre les différents caches slabs, et complique fortement les méthodologies de pétrissage du tas (heap massaging) en cas de heap overflow ;
  • init_on_free=1 (INIT_ON_FREE_DEFAULT_ON) : cela permet la remise à zéro de la mémoire à la libération, ce qui peut aider à atténuer les vulnérabilités d'utilisation après libération et à effacer les informations sensibles en mémoire.
  • mce=0 : Cela provoque une panique du noyau sur les erreurs non corrigibles dans la mémoire ECC qui pourraient être exploitées. Ceci n'est pas nécessaire pour les systèmes sans mémoire ECC.
  • spec_store_bypass_disable=seccomp : force le système à utiliser la contre-mesure par défaut pour la vulnérabilité Spectre v4 (Speculative Store Bypass) ;
  • spectre_v2=on : force le système à utiliser une contre-mesure pour la vulnérabilité Spectre v2 (Branch Target Injection) ;
  • pti=on : force l’utilisation de Page Table Isolation (PTI) y compris sur les processeurs se prétendant non impactés par la vulnérabilité Meltdown ;
  • page_poison=on (CONFIG_PAGE_POISONING) : déjà activé par défaut dans Debian 12 ;
  • init_on_alloc=1 (CONFIG_INIT_ON_ALLOC_DEFAULT_ON) : déjà activé par défaut dans Debian 12 ;
  • page_alloc.shuffle=1 (CONFIG_SHUFFLE_PAGE_ALLOCATOR) : déjà activé par défaut dans Debian 12 ;
  • randomize_kstack_offset=on (CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT) : déjà activé par défaut dans Debian 12 ;
  • vsyscall=none (CONFIG_LEGACY_VSYSCALL_NONE) : déjà activé par défaut dans Debian 12.

On finit en montant la partition /boot et en mettant à jour Grub.

mount /boot
update-grub
reboot

Étape 2 : Configuration de mises à jour

Dans la partie précédente, on a fait le choix de monter les partitions /tmp et /var en nosuid, nodev, noexec. Or le gestionnaire de paquets apt a besoin par défaut que les fichiers écrits dans /tmp et /var/lib/dpkg soient exécutables.
Nous allons donc d'abord configurer apt pour qu'il monte une partition temporaire dans /mnt/aptmp avant d’installer ou de mettre à jour un paquet et qu'il démonte cette partition après l'installation du paquet. On fait de même avec la partition /boot au cas où il y a une mise à jour du noyau.

mkdir /mnt/aptmp
nano /etc/apt/apt.conf.d/70debconf
/etc/apt/apt.conf.d/70debconf
DPkg::Pre-Install-Pkgs {
  "/usr/sbin/dpkg-preconfigure --apt || true";
  "mount -t tmpfs -o size=500m tmpfs /mnt/aptmp";
  "mount /boot";
};
DPkg::Post-Invoke {
  "umount /mnt/aptmp";
  "umount /boot";
};

Maintenant on change les répertoires qu'utilisent apt et dpkg.

mkdir /root/varlibdpkg
cp -rp /var/lib/dpkg/* /root/varlibdpkg/
nano /etc/apt/apt.conf.d/50directories
/etc/apt/apt.conf.d/50directories
APT::ExtractTemplates::TempDir "/mnt/aptmp";
Dir::State::status "/root/varlibdpkg/status";

Et on finit en rajoutant admindir=/root/varlibdpkg au fichier /etc/dpkg/dpkg.cfg.

nano /etc/dpkg/dpkg.cfg
/etc/apt/apt.conf.d/50directories
no-debsig
log /var/log/dpkg.log
admindir=/root/varlibdpkg

Il est important d'avoir un serveur toujours à jour notamment pour les mise à jour de sécurité. Pour cela on peut utiliser l'outil unattended-upgrades dont l'utilisation est décrite ici. On fait le choix ici d'utiliser une solution plus minimaliste en créant un petit script qui mettra à jour le système.

nano /root/apt_upgrade_daily.sh
/root/apt_upgrade_daily.sh
#!/bin/bash

# On met à jour la liste des paquets
apt-get update

# On log le nombre de mises à jour
echo "APT UPGRADE : `apt-get -s upgrade | grep upgraded,`" | systemd-cat -p info

# On effectue les mises à jour
apt-get -y upgrade

# On indique au journal la nécessité de redémarrer le serveur
if [ -f /var/run/reboot-required ]; then
  echo "APT UPGRADE : reboot required" | systemd-cat -p warning
fi

Il faut ensuite rendre ce script exécutable puis créer un timer systemd pour le lancer à intervalle régulier. Pour plus d'information sur les timers systemd voir ici.

chmod +x /root/apt_upgrade_daily.sh
nano /root/apt_upgrade_daily.service
/root/apt_upgrade_daily.service
[Unit]
Description=Daily apt upgrade
After=network.target

[Service]
Type=oneshot
ExecStart=/root/apt_upgrade_daily.sh

[Install]
WantedBy=multi-user.target
nano /root/apt_upgrade_daily.timer
/root/apt_upgrade_daily.timer
[Unit]
Description=Daily apt upgrade

[Timer]
OnCalendar=*-*-* 3:00:00
RandomizedDelaySec=3h
Persistent=true

[Install]
WantedBy=timers.target

Il faut enfin activer le timer. On vérifie ensuite que tout fonctionne.

systemctl enable /root/apt_upgrade_daily.service
systemctl enable /root/apt_upgrade_daily.timer
systemctl start apt_upgrade_daily.timer
systemctl status apt_upgrade_daily.timer
systemctl list-timers --all

Étape 3 : Synchronisation de l'heure

Avoir un serveur toujours à l'heure est important pour la sécurité. En particulier cela est essentiel pour les certificats TLS, la connexion par TOTP ou encore l’horodatage d’évènements enregistrés dans les journaux système.
On va s'assurer que le serveur est toujours à la bonne heure en le synchronisant avec un serveur NTP (lui même relier à une horloge atomique). Pour cela on va utiliser l'outil timedatectl fourni par systemd. Pour plus d'information voir cette documentation.
On commence par installer le paquet systemd-timesyncd.

apt install systemd-timesyncd
Les NOUVEAUX paquets suivants seront installés :
  systemd-timesyncd

On définit ensuite les serveurs NTP à utiliser.

nano /etc/systemd/timesyncd.conf
/etc/systemd/timesyncd.conf
[Time]
NTP=0.fr.pool.ntp.org 1.fr.pool.ntp.org 2.fr.pool.ntp.org 3.fr.pool.ntp.org
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

On définit ensuite la zone géographique sur laquelle on se base pour l'heure de notre serveur (c'est notamment utile pour les changements d'heure) et on active la synchronisation.

timedatectl set-timezone Europe/Paris
timedatectl set-ntp true

On redémarre alors le service systemd-timesyncd pour prendre en compte les modifications et on peut vérifie que tout est bon.

systemctl restart systemd-timesyncd
systemctl status systemd-timesyncd
timedatectl status
timedatectl timesync-status

La commande systemctl status systemd-timesyncd permet de vérifier que le service est bien lancé, la commande timedatectl status permet de vérifier que la synchronisation est bien active et qu'on est bien sur la bone zone géographique et timedatectl timesync-status permet notamment de voir à quel serveur NTP on est connecté.

Étape 4 : Gestion de l'utilisateur administrateur et de sudo

Gérer son serveur directement avec le compte root est déconseillé en terme de sécurité (voir [ANSSI R33]). Pour cela on va créer un utilisateur administrateur aliceadmin qui pourra lancer des commandes nécessitant les privilèges administrateur via sudo. Cela a deux avantages : devoir entrer un mot de passe pour lancer des commandes nécessitant les privilèges de root et toutes les commandes lancées via sudo sont journalisées.

On commence par créer le nouvel utilisateur aliceadmin. On peut, sans trop de soucis, choisir un mot de passe assez facile à retenir. En effet on n'utilisera pas ce mot de passe pour se connecter au compte aliceadmin car on utilisera une clef ssh. On peut laisser vide les champs d'information (Name, Phone, ...).

adduser aliceadmin
Adding user `aliceadmin' ...
Adding new group `aliceadmin' (1000) ...
Adding new user `aliceadmin' (1000) with group `aliceadmin (1000)' ...
Creating home directory `/home/aliceadmin' ...
Copying files from `/etc/skel' ...
Nouveau mot de passe :
Retapez le nouveau mot de passe :
passwd : mot de passe mis à jour avec succès
Modifier les informations associées à un utilisateur pour aliceadmin
Entrer la nouvelle valeur, ou appuyer sur ENTER pour la valeur par défaut
  NOM []:
  Numéro de chambre []:
  Téléphone professionnel []:
  Téléphone personnel []:
  Autre []:
Is the information correct? [Y/n] Y
Adding new user `aliceadmin' to supplemental / extra groups `users' ...
Adding user `aliceadmin' to group `users' ...

On installe ensuite sudo que l'on configure avec la commande visudo. Il faut rajouter dans le fichier la ligne aliceadmin ALL=(ALL) ALL pour autoriser l'utilisateur aliceadmin à acceder à toutes les commandes root. Pour plus d'informations voir ici.

apt install sudo
visudo
/etc/sudoers.tmp
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

Defaults        use_pty

# User privilege specification
root    ALL=(ALL:ALL) ALL
aliceadmin ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

@includedir /etc/sudoers.d

On passe sur le compte de aliceadmin et on verrouiller le mot de passe du compte root afin qu'on ne puisse plus s'y connecter directement.

su aliceadmin
sudo usermod -L -e 1 root
sudo usermod -s /bin/false root

Étape 5 : Gestion des clefs SSH

Le but de cette partie est de mettre en place l'authentification SSH par clef. Cela a pour avantage de diminuer drastiquement le risque de piratage par force brute car la longueur des clefs est par défaut très longue.

On commence par supprimer toutes les clefs du serveur, générées lors de l'installation de openssh-server, pour régénérer une clef RSA 4096 et une clef ED25519 avec l'outil ssh-keygen. Voir le manuel.

sudo rm /etc/ssh/ssh_host_*
sudo ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""

Et on en profite pour supprimer les trop petits nombres premiers, de taille inférieur à 2048 bits, du fichier /etc/ssh/moduli.

awk '$5 >= 3071' /etc/ssh/moduli | sudo tee /etc/ssh/moduli.safe > /dev/null
sudo mv /etc/ssh/moduli.safe /etc/ssh/moduli

On passa ensuite sur la machine locale avec laquelle on se connectera au serveur. On fait le choix ici d'utiliser une clef pour l'algorithme ECDSA sur la courbe elliptique Curve25519.

mkdir ~/.ssh/webalice/
ssh-keygen -a 128 -t ed25519 -f ~/.ssh/webalice/aliceadmin-ed25519
ssh-keygen -f "/home/alice/.ssh/known_hosts" -R "10.11.12.13"
ssh-copy-id -i ~/.ssh/webalice/aliceadmin-ed25519.pub aliceadmin@10.11.12.13
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/alice/.ssh/webalice/aliceadmin-ed25519.pub"
The authenticity of host '10.11.12.13 (10.11.12.13)' can't be established.
ED25519 key fingerprint is SHA256:uR+W9GKXuV/5g+1f8W6aiwW5IKuYA6tnbK/mCoId3i0.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
aliceadmin@10.11.12.13's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'aliceadmin@10.11.12.13'"
and check to make sure that only the key(s) you wanted were added.

La deuxième commande permet de générer un couple de clefs (une clef publique et une clef privée) ECDSA sur la courbe elliptique Curve25519. Le paramètre -a 128 signifie qu'on décide de chiffrer la clef privée (avec 128 tours de passe KDF) avec un mot de passe de sorte que sans ce mot de passe il soit impossible de se connecter au serveur. Cela a pour but d’empêcher quelqu'un qui déroberait le fichier de la clé privée sur notre disque puisse acceder au serveur sans le mot de passe. C'est une option facultative que l'on peut enlever.
Après l'execution de la deuxième commande, deux fichiers webaliceadmin-ed25519 et webaliceadmin-ed25519.pub ont été créés dans le répertoire ~/.ssh/vps/ : il s'agit de la clef privée (chiffrée) et de la clef publique. On envoie alors la clef publique au serveur avec la troisième commande pour qu'on puisse se connecter au compte aliceadmin avec la clef privée. Pour cela il faudra entrer le mot de passe de aliceadmin choisi lors de l'étape précédente.

On se connecte maintenant au serveur en utilisant la clef avec la commande suivant et en utilisant le mot de passe de la clef si on a décidé de chiffré la clef privée :

ssh -i ~/.ssh/webalice/aliceadmin-ed25519 aliceadmin@10.11.12.13

Étape 6 : Configuration et sécurisation de la connection SSH

On cherche dans cette partie et la suivante à configurer et sécuriser la connection SSH. Pour cela on va suivre les recommandations pour un usage sécurisé d’(Open)SSH de l'ANSSI et le guide de Mozilla.

On édite le fichier de configuration du serveur SSH.

sudo nano /etc/ssh/sshd_config
/etc/ssh/sshd_config
Port 32
# ANSSI R26 | Default : Port 22

HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key

PermitRootLogin no
# ANSSI R21 | Default : PermitRootLogin prohibit-password
StrictModes yes
# ANSSI | Default : StrictModes yes

PubkeyAuthentication yes
# Default : PubkeyAuthentication yes

PasswordAuthentication no
# Default : PasswordAuthentication yes

KbdInteractiveAuthentication yes
# Default : KbdInteractiveAuthentication yes

UsePAM yes

AllowAgentForwarding no
# Default : AllowAgentForwarding yes
AllowTcpForwarding no
# ANSSI R27 | Default : AllowTcpForwarding yes
AllowStreamLocalForwarding no
# Default : AllowStreamLocalForwarding yes
X11Forwarding no
# ANSSI R28 | Default : X11Forwarding no
PrintMotd no
# Default : PrintMotd yes
PrintLastLog yes
# ANSSI R21 | Default : PrintLastLog yes
PermitUserEnvironment no
# ANSSI R26 | Default : PermitUserEnvironment no

AcceptEnv LANG LC_*
Subsystem sftp internal-sftp
# Default : Subsystem sftp /usr/lib/openssh/sftp-server

KexAlgorithms curve25519-sha256@libssh.org,curve25519-sha256,diffie-hellman-group-exchange-sha256
# Default : KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256
# Default : HostKeyAlgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
# Default : Ciphers chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256
# Default : MACs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1

AllowUsers aliceadmin
# ANSSI R22

Pour valider les changement, on relance le service SSH. Pour vérifier que la nouvelle configuration fonctionne bien, on peut quitter la connection actuelle et essayer de se reconnecter.

sudo systemctl reload sshd
exit

On n'oublie pas d'indiquer qu'on se connecte maintenant au port 32.

ssh -i ~/.ssh/webalice/aliceadmin-ed25519 -p 32 aliceadmin@10.11.12.13

Étape 7 (fac) : Connection SSH par double authentification

On peut améliorer la sécurité de la connection SSH au compte aliceadmin en ajoutant une double authentification comme cela est fait sur certain site. Il sera alors nécessaire d'avoir une application sur un deuxième appareil (souvent un smartphone) qui générera un code temporaire toutes les 30 secondes. Il faudra donner ce code en plus de la clef privée pour se connecter au serveur. Sur smartphone on pourra utiliser l'application Aegis.

On installe pour cela le module PAM libpam-google-authenticator qui contient le module PAM Google Authenticator. Contrairement à ce qu'on pourrait croire ce logiciel n'utilise aucun "service Google" et n'envoie aucune donnée à Google. Il s'agit d'un logiciel totalement libre qui permet la double authentification par la génération d'un mot de passe à usage unique basé sur le temps (TOTP).

sudo apt install libpam-google-authenticator
Les NOUVEAUX paquets suivants seront installés :
  libpam-google-authenticator libqrencode4

On commence par créer la clef secrète permettant d'initialiser le TOPT en lançant google-authenticator.

google-authenticator
Do you want authentication tokens to be time-based (y/n) y
Warning: pasting the following URL into your browser exposes the OTP secret to Google:


Your new secret key is: 3PWWW6ZKB7JL7S4IJD5FEZNSGQ
Enter code from app (-1 to skip): 381176
Code confirmed
Your emergency scratch codes are:
  41715015
  47330205
  15559260
  82868672
  86538443

Do you want me to update your "/home/aliceadmin/.google_authenticator" file? (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n

If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y

On répond oui à la première question. Le système va alors générer une clef secrète qui vous permettra de configurer votre application OTP. Il suffit pour cela de scanner le QR code via l'application OPT. Il faut penser à stocker dans un endroit sûr les 5 codes de secours, sous peine d'être enfermé dehors si le générateur d'OTP devait faire défaut.
On répond ensuite oui, oui, non et oui aux questions. Pour plus d'info voir le github.

Il faut ensuite configurer PAM pour prendre en compte la double authentification lors de la connexion SSH.

sudo nano /etc/pam.d/sshd

Il faut commenter la ligne contenant @include common-auth et la remplacer par auth required pam_google_authenticator.so nullok de sorte à avoir un début de fichier ressemblant à :

/etc/pam.d/sshd
# PAM configuration for the Secure Shell service

# Standard Un*x authentication.
#@include common-auth
auth required pam_google_authenticator.so nullok

# Disallow non-root logins when /etc/nologin exists.
account    required     pam_nologin.so
[...]

Il faut alors modifier le fichier de configuration de ssh pour qu'il prenne en compte la double authentification.

sudo nano /etc/ssh/sshd_config

Il faut rajouter à la toute fin du fichier les lignes suivantes :

/etc/ssh/sshd_config
[...]
Match User aliceadmin
	AuthenticationMethods publickey,keyboard-interactive:pam

La double authentification est maintenant configurée. Il n'y a plus qu'à recharger le service sshd pour que les changements soient pris en compte. On peut alors se déconnecter avec exit et tenter de se reconnecter. Cette fois-ci il faudra donner le code OPT pour pouvoir acceder au serveur.

sudo systemctl reload sshd
exit
ssh -i ~/.ssh/webalice/aliceadmin-ed25519 -p 32 aliceadmin@10.11.12.13
ssh -i ~/.ssh/webalice/aliceadmin-ed25519 -p 32 aliceadmin@10.11.12.13
Enter passphrase for key '/home/alice/.ssh/vps/serveradmin-ed25519':
Verification code: 

Étape 8 : Configuration du pare-feu

Debian 10 a introduit nftables un nouvel outil pour configurer le pare-feu qui remplace iptables. Il permet une gestion plus fine des règles de filtrage. Il permet notamment de créer nativement des "Blacklist" (pouvant remplacer fail2ban) et faire du "port-knocking" sans avoir à installer de logiciel tiers.
Voici quelques références expliquant le fonctionnement de nftables : le wiki officiel, le Bulletin d'actualité du CERT-FR et le wiki de Archlinux.

On commence par installer le paquet nftables.

sudo apt install nftables
Paquets suggérés :
  firewalld
Paquets recommandés :
  netbase
Les NOUVEAUX paquets suivants seront installés :
  libjansson4 libnftables1 libnftnl11 nftables

Pour fonctionner nftables a besoin de charger des modules du noyau. Or à l'étape 1 on empêche le chargement de tels modules. Il faut donc ajouter les modules à charger dans /etc/modules.

sudo nano /etc/modules
/etc/modules
# /etc/modules: kernel modules to load at boot time.
#
# This file contains the names of kernel modules that should be loaded
# at boot time, one per line. Lines beginning with "#" are ignored.
# Parameters can be specified after the module name.

nf_tables
nft_ct
nft_limit
nft_log
nf_log_syslog

Il faut alors redémarrer le serveur pour que nftables soit opérationnel.

sudo reboot

On édite alors les règles de filtrage du pare-feu dans le fichier /etc/nftables.conf. Ici on propose un exemple simple de configuration :

  • On refuse par défaut tout traffic entrant et on accepte au cas par cas quelques exceptions.
  • On accepte les connections déjà établies.
  • On bannit pour 6 heures toutes les ip qui envoie plus de 1000 paquets TCP en moins d'une heure sur un port autre que 80 et 443 (http et https).
  • On bannit pour 30 minutes toutes les ip qui ouvrent plus de 100 connections invalides en moins d'une heure.
  • On bannit pour 15 minutes toutes les ip qui envoie un paquet non SYN lors de l'ouverture de la connection TCP.
  • On bannit pour 6 heures toutes les ip qui envoie plus de 1000 paquets UDP en moins d'une heure.
  • On limite à 10 paquets "ping" par seconde
  • On bannit pour 30 minutes toutes les ip utilisant un autre protocole que TCP UDP et ICMP.
  • On autorise les nouvelle connections sur les ports 80 et 443 (http et https).
  • On décide de mettre en place un "port-knocking" pour cacher le port de ssh (c'est une sécurité supplémentaire pour la connexion SSH). Le principe est simple : on ferme de base le port de ssh et on ne l'ouvre que pour une ip qui aura envoyée des paquets sur 4 ports qu'on aura choisi en amont (et arbitrairement). Pour filer la métaphore si une IP ne fait pas le bon "toc toc toc toc" alors le port de SSH ne s'ouvrira pas. Et a priori, à part nous, personne ne connaît les bon port sur lesquels "taper" car ils sont tous fermés par le pare-feu. A noter quand même que cette mesure n'est pas si forte que ça contre un adversaire pouvant écouter le réseau. En effet si l'adversaire peut écouter à l'entré du serveur il déterminera facilement, quand on se connectera, quelle est la séquence d'ouverture du port ssh. Cependant c'est une technique très efficace contre les bots qui testent tous les ports. Pour plus d'information voir ici et pour avoir des exemples avec nftables voir .
Il faut évidemment modifier le fichier suivant, notament en changer les ports du "port-knocking".

sudo nano /etc/nftables.conf
/etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {

    # Port-Knocking : 1111:tcp,2222:udp,3333:tcp,4444:udp
    define port1 = 1111
    define port2 = 2222
    define port3 = 3333
    define port4 = 4444

    # SSH Port
    define sshport = 32

    chain input {
        type filter hook input priority 0; policy drop;

        # Allow established/related connections
        ct state {established, related} accept

        # Allow from loopback
        iifname lo accept

        # BlackList IP
        ip saddr @blackhole drop

        # FLOODING simple blacklist
        tcp dport != @tcpaccepted meter flood { ip saddr timeout 1h limit rate over 1000/hour } add @blackhole { ip saddr timeout 6h } log prefix "NFT_LOG : FLOOD : " drop

        # Drop invalid connections
        ct state invalid tcp dport @tcpaccepted drop
        ct state invalid meter invalid { ip saddr timeout 1h limit rate over 100/hour } add @blackhole { ip saddr timeout 30m } log prefix "NFT_LOG : INVALID CONNECTION : " drop

        # If the connection is NEW and flag is not SYN then drop
        tcp flags != syn meter suspicious { ip saddr timeout 1h limit rate over 50/hour } add @blackhole { ip saddr timeout 15m } log prefix "NFT_LOG : NOTSYN : " drop

        # UDP
        ip protocol udp meter udpflood { ip saddr timeout 1h limit rate over 1000/hour } add @blackhole { ip saddr timeout 6h } log prefix "NFT_LOG : UDP : " drop

        # ICPM with ping limitation
        ip protocol icmp icmp type echo-request limit rate 10/second accept

        # Other protocol
        ip protocol != { tcp, udp, icmp } meter proto { ip saddr timeout 1h limit rate over 30/hour } add @blackhole { ip saddr timeout 30m } log prefix "NFT_LOG : OTHER PROTO : " drop

        # Allow accepted tcp port
        tcp dport @tcpaccepted tcp flags syn ct state new accept

        # Port-Knocking to ssh
        jump PortKnock
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }

    # Blacklist of ip
    set blackhole {
        type ipv4_addr;
        flags timeout;
    }

    # tcp and udp accepted port
    set tcpaccepted {
        type inet_service;
        elements = { 80, 443 }
    }

    # Port knocking
    set Knocked_1 {
        type ipv4_addr
        flags timeout
        timeout 10s
    }

    set Knocked_2 {
        type ipv4_addr
        flags timeout
        timeout 10s
    }

    set Knocked_3 {
        type ipv4_addr
        flags timeout
        timeout 10s
    }

    set Knocked_accept {
        type ipv4_addr
        flags timeout
        timeout 5m
    }

    chain Lock {
        add @blackhole { ip saddr timeout 10s }
    }
    chain Knock_1 {
        add @Knocked_1 { ip saddr }
    }
    chain Knock_2 {
        add @Knocked_2 { ip saddr }
    }
    chain Knock_3 {
        add @Knocked_3 { ip saddr }
    }
    chain Knock_a {
        add @Knocked_accept { ip saddr } log prefix "NFT_LOG : Port-Knock accepted : "
    }


    chain PortKnock {

        # Allow accepted connection by portknocking
        ip saddr @Knocked_accept tcp dport $sshport accept

        udp dport $port4 ct state new ip saddr @Knocked_3 goto Knock_a
        tcp dport $port3 ct state new ip saddr @Knocked_3 return
        ip saddr @Knocked_3 ct state new goto Lock

        tcp dport $port3 ct state new ip saddr @Knocked_2 goto Knock_3
        udp dport $port2 ct state new ip saddr @Knocked_2 return
        ip saddr @Knocked_2 ct state new goto Lock

        udp dport $port2 ct state new ip saddr @Knocked_1 goto Knock_2
        tcp dport $port1 ct state new ip saddr @Knocked_1 return
        ip saddr @Knocked_1 ct state new goto Lock

        tcp dport $port1 ct state new goto Knock_1
    }

}

Il faut maintenant démarrer le pare-feu et vérifier qu'il fonctionne.

sudo systemctl start nftables
sudo systemctl status nftables
sudo nft list table inet filter

A ce stade là, si vous avez fait des modifications et que vous n'êtes pas sûrs de vos règles, je recommande d'ouvrir un nouveau terminal et de tenter de vous connecter au serveur. [...]
Maintenant pour se connecter à ssh il faut faire la bonne séquence de "port-knocking". On utilise alors le petit logiciel knock qu'il faut installer sur la machine locale utiliser comme suit :

sudo apt install knockd
knock -d 100 10.11.12.13 1111:tcp 2222:udp 3333:tcp 4444:udp && ssh -i ~/.ssh/webalice/aliceadmin-ed25519 -p 32 aliceadmin@10.11.12.13

A noter que maintenant sans faire knock -d 100 10.11.12.13 1111:tcp 2222:udp 3333:tcp 4444:udp vous ne pouvez plus acceder au serveur via SSH car le port est fermé. Le fait de faire knock -d 100 10.11.12.13 1111:tcp 2222:udp 3333:tcp 4444:udp permet d'ouvrir le port SSH pour une durée de 5 min pour l'IP qui fait la requête.

Maintenant que tout bon on peut activer par défaut le pare-feu à chaque démarrage du serveur.

sudo systemctl enable nftables

En cas d'erreur de configuration du pare-feu il est possible que le serveur devienne inaccessible (même après redémarrage. Il faudra alors démarrer en mode rescue, modifier le fichier /etc/nftables.conf et redémarrer le serveur.

Dans la configuration proposée de nftables on journalise un certain nombre d'opération, par exemple les bannissements d'IP. Pour voir les logs de nftables on utilise

sudo journalctl | grep "NFT"

et on peut voir la liste des ip dans la blacklist avec la commande

sudo nft list table inet filter

Étape 9 : Chiffrement de la partition de stockage

Commençons rappeler l'organisation du disque du serveur.

sudo lsblk -f
NAME    FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sda
├─sda1
├─sda2  ext4   1.0         67ffeaa3-6a24-4973-9492-d84b3ed33e98
├─sda3  ext4   1.0         4d1f2b87-fdde-4dd0-b1e2-b79ea2b5d6e7    2,7G     0% /
├─sda4  ext4   1.0         eaa44e06-26ea-4ced-a0b8-0e7ed13dfaa8    2,4G     9% /var
├─sda5  ext4   1.0         0f76eb85-394f-47c2-a8db-515eba6859cd    9,2G     1% /var/log
├─sda6  ext4   1.0         d675f6af-6b1b-46f3-b200-9cb8be4fc2ac    3,7G    14% /usr
├─sda7  ext4   1.0         6dcb5bb2-af3e-417a-9bbf-bcf6dfd4eea0    1,9G     0% /opt
├─sda8  ext4   1.0         1a77e46b-5838-499d-a4f5-7c95a357d594    9,6G     0% /home
├─sda9  swap   1           4464a211-350f-460d-889f-0466468a66b6                [SWAP]
└─sda10

L'objectif de cette partie est de chiffrer la partition /dev/sda10. Pour cela on va utiliser le logiciel cryptsetup pour créer une partition LUKS. Pour plus d'informations voir ici. On commence par installer le logiciel.

sudo apt install cryptsetup
Paquets suggérés :
  cryptsetup-initramfs dosfstools keyutils liblocale-gettext-perl
Les NOUVEAUX paquets suivants seront installés :
  cryptsetup cryptsetup-bin libpopt0

Il faut également autoriser des modules noyau pour le bon fonctionnement de cryptsetup. On rajoute à la suite de /etc/modules.

sudo nano /etc/modules
/etc/modules
[...]
algif_skcipher
aesni_intel
xts
dm_crypt

Il faut alors redémarrer le serveur pour que nftables soit opérationnel.

sudo reboot

On décide ici de partir sur un bon compromis sécurité/performance en choisissant de chiffrer en aes-xts 256 ce qui équivaut en fait à un niveau de sécurité de AES en 128 bits. Pour la fonction de dérivation de clé on choisit l'algorithme Argon2id. Attention suivant la valeur du paramètre --pbkdf-force-iterations et la puissance du serveur cela peut prendre beaucoup de temps.

sudo cryptsetup --type luks2 --cipher aes-xts-plain64 --key-size 256 --hash sha512 --pbkdf argon2id --pbkdf-force-iterations 5 --pbkdf-memory 1048576 --pbkdf-parallel 4 luksFormat /dev/sda10

Il faut taper YES puis choisir un bon mot de passe.

On peut vérifier les métadonnées de la partition LUKS et sauvegarder ces métadonnées avec les commandes

sudo cryptsetup luksDump /dev/sda10
sudo cryptsetup luksHeaderBackup /dev/sda10 --header-backup-file luks-header-server.img

On récupérera plus tard le fichier luks-header-server.img qu'on sauvegardera localement.

On déchiffre la partition /dev/sda10 puis on la formate en ext4.

sudo cryptsetup luksOpen /dev/sda10 hdcrypt
sudo mkfs.ext4 -m 1 /dev/mapper/hdcrypt

On monte alors cette partition sur /home/crypt.

sudo mkdir /home/crypt
sudo mount -o rw,nosuid,nodev,noexec /dev/mapper/hdcrypt /home/crypt

On peut créer le script suivant permettant d'automatiser le déchiffrement puis le montage de la partition chiffrée. Il faudra l'exécuter à chaque démarrage du serveur pour pouvoir acceder au données chiffrée. Puisqu'il faudra le mot de passe pour le déchiffrement il est inutile de lancer automatiquement ce script.

sudo nano /root/decrypt.sh
/root/decrypt.sh
#!/bin/bash

cryptsetup luksOpen /dev/sda10 hdcrypt
mount -o rw,nosuid,nodev,noexec /dev/mapper/hdcrypt /home/crypt
sudo chmod +x /root/decrypt.sh

Étape 10 : Configuration SshFS

On va dans cette partie configurer SSH afin de pouvoir monter un espace de stockage à l'aide de sshfs. Pour plus de détails voir ici et .

On souhaite créer des utilisateurs qui pourront acceder au serveur de stockage. Pour cela, puisqu'on a fait expirer le compte root on doit modifier le fichier /etc/pam.d/chfn pour autoriser la création du nom complet et des informations associées à l'utilisateur. Il faut ajouter la ligne account sufficient pam_rootok.so.

sudo nano /etc/pam.d/chfn
/etc/pam.d/chfn
#
# The PAM configuration file for the Shadow `chfn' service
#

# This allows root to change user infomation without being
# prompted for a password
auth            sufficient      pam_rootok.so
account         sufficient      pam_rootok.so
[...]

On crée ensuite les utilisateurs qui pourront acceder au serveur de stockage. Disons qu'on a deux utilisateurs : bob-data et carol-data. On rajoute l'utilisatrice aliceweb qui gérera l'espace pour les sites web. On peut choisir un mot de passe temporaire fort qui ne sera pas utilisé pour l'accès à l'espace de stockage. Ensuite on empêche l'accès à ces compte par mot de passe.

sudo adduser bob-data
sudo adduser carol-data
sudo adduser aliceweb
sudo usermod -L -s /usr/sbin/nologin bob-data
sudo usermod -L -s /usr/sbin/nologin carol-data
sudo usermod -L -s /usr/sbin/nologin aliceweb

On créer un dossier pour chaque utilisateur sur la partition chiffrée et on restreint l'accès à ces dossier.

sudo mkdir -p /home/crypt/bob/data
sudo chown bob-data:bob-data /home/crypt/bob/data
sudo chmod 755 /home/crypt/bob/data
sudo mkdir -p /home/crypt/carol/data
sudo chown carol-data:carol-data /home/crypt/carol/data
sudo chmod 755 /home/crypt/carol/data

On créer aussi un dossier dans lequel on mettra les sites web. On gérera cet espace dans une prochaine partie mais on ne souhaite pas que ces données soient sur la partition chiffrée. En effet en cas de redémarrage du serveur on veut que les sites web soient accessibles sans avoir à déchiffrer la partition chiffrée.

sudo mkdir /home/www

On souhaite que les utilisateurs bob-data, carol-data et aliceweb se connectent à SSH avec une clef mais uniquement pour acceder au documents de leur dossier respectif. On les empêche aussi d'acceder à un terminal car ils n'ont pas à lancer de commande. Pour cela on édite le fichier /etc/ssh/sshd_config en modifiant la ligne AllowUsers aliceadmin et en ajoutant à la fin du fichier les lignes suivantes de sorte à avoir

sudo nano /etc/ssh/sshd_config
/etc/ssh/sshd_config
[...]
#AllowUsers aliceadmin
AllowUsers aliceadmin aliceweb bob-data carol-data
# ANSSI R22

Match User aliceadmin
	AuthenticationMethods publickey,keyboard-interactive:pam

Match User aliceweb
	AuthenticationMethods publickey
	ForceCommand internal-sftp
	ChrootDirectory /home/www
	PermitTTY no

Match User bob-data
	AuthenticationMethods publickey
	ForceCommand internal-sftp
	ChrootDirectory /home/crypt/bob
	PermitTTY no

Match User carol-data
	AuthenticationMethods publickey
	ForceCommand internal-sftp
	ChrootDirectory /home/crypt/carol
	PermitTTY no

On créer maintenant à partir de la machine locale une clef SSH pour chaque utilisateur. On pourra entrer un mot de passe vide.

ssh-keygen -o -t ed25519 -f ~/.ssh/webalice/aliceweb-ed25519
ssh-keygen -o -t ed25519 -f ~/.ssh/webalice/bob-ed25519
ssh-keygen -o -t ed25519 -f ~/.ssh/webalice/carol-ed25519

On décide ici d'installer à la main les clefs SSH sur le serveur plutôt que d'utiliser ssh-copy-id. Pour cela on créer dans chaque dossier personnel un dossier .ssh et un fichier authorized_keys dans lequel on mettra la clef publique précédemment générée. Par exemple pour aliceweb : on récupère d'abord la clef publique sur la machine locale

cat ~/.ssh/webalice/aliceweb-ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICR4KHdnxyc8VL8Yz9j15Vl9A8yp2Nyj1o9BzoAqLku7 alice@home

Ensuite sur le serveur on fait

sudo mkdir /home/aliceweb/.ssh
sudo nano /home/aliceweb/.ssh/authorized_keys
/home/aliceweb/.ssh/authorized_keys
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICR4KHdnxyc8VL8Yz9j15Vl9A8yp2Nyj1o9BzoAqLku7 alice@home
sudo chown -R aliceweb:aliceweb /home/aliceweb/.ssh
sudo chmod 700 /home/aliceweb/.ssh
sudo chmod 400 /home/aliceweb/.ssh/authorized_keys

Il faut ensuite faire de même pour les autres utilisateurs c'est-à-dire ici pour bob-data et carol-data.
On peut alors relancer le service sshd

sudo systemctl reload sshd

On peut désormais se connecter à l'espace de stockage du serveur, par exemple de Bob, avec la commande

knock -d 100 10.11.12.13 1111:tcp 2222:udp 3333:tcp 4444:udp && sshfs bob-data@10.11.12.13:/data /home/alice/pointDeMontage -p 32 -o IdentityFile=~/.ssh/webalice/bob-ed25519

Étape 11 (fac) : Quotas de disques

Afin d'éviter qu'un des utilisateur utilise à lui seul tout l'espace de la partition chiffrée on va mettre en place un système de quota. Pour plus de détails voiri ici. On commence par installer les paquets quota et quotatool.

sudo apt install quota quotatool
Paquets suggérés :
  libnet-ldap-perl rpcbind default-mta | mail-transport-agent
Paquets recommandés :
  libldap-common libsasl2-modules
Les NOUVEAUX paquets suivants seront installés :
  libldap-2.5-0 libnl-3-200 libnl-genl-3-200 libsasl2-2 libsasl2-modules-db quota quotatool

Il est nécessaire d'activer le module noyau quota_v2. Il faut donc l'ajouter au fichier /etc/modules.

sudo nano /etc/modules
/etc/modules
[...]
quota_v2

Puis on redémarre le serveur.

sudo reboot

Il faut ensuite démonter la partition chiffrée pour avec les options de montage permettant la mise en place des quotas. Pour plus d'informations voir ici et le manuel de ext4.

sudo tune2fs -O quota /dev/mapper/hdcrypt
sudo tune2fs -Q usrquota /dev/mapper/hdcrypt

On peut également modifier le script de montage de la partition chiffrée

sudo nano /root/decrypt.sh
/root/decrypt.sh
#!/bin/bash

cryptsetup luksOpen /dev/sda10 hdcrypt
mount -o rw,nosuid,nodev,noexec,usrquota /dev/mapper/hdcrypt /home/crypt

On créer le fichier de configuration des quota sur la partition chiffré et on active les quotas.

sudo /root/./decrypt.sh

On configure maintenant les quotas par utilisateurs. Pour bob on lui accorde jusqu'à 10 Gio (stricte) avec un avertissement à 9 Gio (souple) et pour carol on lui accorde jusqu'à 20 Gio (stricte) avec un avertissement à 15 Gio (souple).

sudo edquota bob-data
Quotas disque pour user bob-data (uid 1001) :
  Système de fichiers           blocs       souple     stricte   inodes    souple   stricte
   /dev/mapper/hdcrypt             20           9G         10G        7         0         0
sudo edquota carol-data
Quotas disque pour user carol-data (uid 1002) :
  Système de fichiers           blocs       souple     stricte   inodes    souple   stricte
    /dev/mapper/hdcrypt             4          15G         20G        1         0         0

On peut aussi configurer le temps qu'on autorise au utilisateur de dépasser les limites souples. Ici j'augmente cette limite à 30 jours.

sudo edquota -t
Sursis avant l'application des limites souples pour users :
  Unités de temps peuvent être : days (jours), hours (heures), minutes, ou seconds
    Système de fichiers  période de sursis bloc  période de sursis inode
    /dev/mapper/hdcrypt                 30days                 30days

Pour vérifier les quotas des utilisateurs on peut faire :

sudo repquota -u /home/crypt
*** Rapport pour les quotas user sur le périphérique /dev/mapper/hdcrypt
Période de sursis bloc : 30days ; période de sursis inode : 30days
                        Block limits                File limits
Utilisateur     utilisé souple stricte sursis utilisé souple stricte sursis
----------------------------------------------------------------------
root      --      36       0       0              6     0     0
bob-data  --      20 9437184 10485760              7     0     0
carol-data --       8 15728640 20971520              2     0     0  

Étape 12 (fac) : Apparmor

On installe maintenant apparmor un logiciel de sécurité qui permet d'isoler et de restreindre le pouvoir d'action des applications du système. Plus précisément c'est un modèle d’accès MAC où une autorité décide des accès d’une application sans que celle-ci puisse les altérer. Voir [ANSSI R45] et le cahier de l'administrateur Debian.

sudo apt install apparmor apparmor-utils apparmor-profiles
Paquets suggérés :
  apparmor-profiles-extra
Les NOUVEAUX paquets suivants seront installés :
  apparmor apparmor-utils apparmor-profiles

Par défaut un certain nombre de profils sont déjà présents dans le répertoire /etc/apparmor.d. Si on souhaite tous les activer on peut utiliser la commande aa-enforce /etc/apparmor.d/*. On peut voir les profils actifs avec la commande aa-status.

sudo aa-enforce /etc/apparmor.d/*
sudo aa-status

Étape 13 (fac) : Résolution DNS

On peut terminer la configuration du réseau en ajoutant un serveur DNS qui permettra de résoudre les noms de domaine Internet en des adresse IP. Cependant un serveur web n'ayant pas usuellement l'utilité de résoudre des noms de domaine (à part peut-être si des scripts utilisent des nom de domaine plutôt que des adresses IP) cette partie est facultative.
On peut choisir le serveur DNS de l’hébergeur ou pour des questions de vie privée on peut choisir des serveurs DNS supposés plus respectueux comme FDN, Quad9 ou CloudFlare.
Pour cela on va installer et configurer systemd-resolved. Plus d'info ici.

sudo apt install systemd-resolved
sudo nano /etc/systemd/resolved.conf
/etc/systemd/resolved.conf
[Resolve]
# FDN
DNS=80.67.169.12 80.67.169.40
# Quad9
FallbackDNS=9.9.9.9 149.112.112.112
Domains=~
DNSSEC=yes

On termine en relançant le service de systemd-resolved et en vérifiant que la configuration est bien prise en compte.

sudo systemctl restart systemd-resolved
sudo resolvectl status

Étape 14 (fac) : Sauvegarde de données

Maintenant qu'on accès au serveur de stockage on peut en profiter pour sauvegarde certaines données de la configuration du serveur. Par exemple on peut récupérer les métadonnées de la partition LUKS précédemment sauvegarder avec la commande cryptsetup luksHeaderBackup.

sudo mv /home/aliceadmin/luks-header-server.img /home/crypt/bob/data/luks-header-server.img
sudo chown bob-data:bob-data /home/crypt/bob/data/luks-header-server.img