Sécuriser une installation NextCloud avec Crowdsec

Sommaire
Nextcloud est un outil de drive collaboratif extensible permettant de remplacer les suites bureautiques et drives traditionnel. (GSuite et Microsoft 365). L’accent est mis sur la confidentialité avec un outil facilement auto-hébergeable.
Crowdsec est une solution de sécurité communautaire reposant sur le principe d’analyse et la corrélation de logs applicatifs. Toutes les machines exécutant CrowdSec permettent de collecter des signaux d’intrus, de les bloquer et de les signaler au reste de la communauté. Ainsi l’ensemble des utilisateurs bénéfient d’une protection de qualité et tout ça gratuitement.
Architecture #
Voici comment est architecture notre stack nextcloud
Schéma simplifié de l’architecture
Le conteneur CrowdSec est relié aux logs provenant du reverse-proxy ainsi qu’aux logs de Nextcloud. Ainsi, il obtient la capacité de détecter les attaques.
Le conteneur Openresty à pour objectif d’acheminer les requêtes des utilisateurs vers le conteneur Nextcloud. Il échange avec CrowdSec via l’API locale et est en mesure de bloquer les requêtes malveillantes en les bannissant ou en leur faisant faire un challenge captcha.
Le conteneur certbot permet d’obtenir des certificats gratuitement avec le service let’s encrypt. Il gère également le système de challenges permettant leur obtention.
Pour finir, les conteneurs Nextcloud et Database assurent le fonctionnement du service Nextcloud.
Si vous prêtez attention au fichier
docker compose.yml
, vous remarquerez deux conteneurs inconnus. (Redis, Cron). Redis est une base de données en mémoire utilisée par Nextcloud pour stocker rapidement de l’information. Et Cron est un outil pour automatiser les tâches de maintenance.
Des volumes sont également montés pour permettre l’échange de données entre conteneurs à travers le système de fichiers.
Prérequis #
Vous aurez également besoin d’une clef d’api reCAPTCHA V2 à demander sur le site de Google. Ainsi qu’un nom de domaine qui pointe déjà vers votre VPS.
Dans notre cas, nous utiliserons une machine virtuelle sous Debian 11 avec les caractéristiques suivantes :
- 2 vCPU
- 2 Go de RAM
- 100 Go d’espace disque (à adapter selon votre utilisation de nextcloud)
Une fois notre machine fraîchement installée, nous nous connectons dessus en SSH sur le compte root.
Création de l’utilisateur #
Avant toute chose, nous créons un utilisateur bob
.
# Création de l'utilisateur
adduser <username> --disabled-password
passwd <username>
# Installation de l'outil sudo
apt-get -y install sudo
Nous lui donnons ensuite des droits.
nano /etc/sudoers
# User privilege specification
root ALL=(ALL:ALL) ALL
bob ALL=(ALL:ALL) ALL
Puis nous passons à notre utilisateur
su <username>
Et pour finir, nous renforçons la sécurité du serveur SSH en modifiant vers la configuration suivante.
# Edit sshd config file
sudo nano /etc/ssh/sshd_config
# Force ssh v2
Protocol 2
# Disable root login
PermitRootLogin no
# Enable ssh logs
SyslogFacility AUTH
LogLevel INFO
# Limit maximum failed authentifications
MaxAuthTries 5
# Limit maximum concurrent sessions
MaxSessions 2
# Disable empty passwords
PermitEmptyPasswords no
sudo systemctl restart sshd.service
Mise à jour #
Nous mettons ensuite la VM à jours.
sudo apt-get update
sudo apt-get -y dist-upgrade
Puis nous automatisons le processus des mises à jour de sécurité avec le paquet unattended-upgrade
.
sudo apt-get install -y apt-listchanges unattended-upgrades
Pare-feu #
Pour se protéger des menaces extérieures, il est important de fermer tous les ports inutiles sur notre serveur. Pour cela, nous dédions cette tâche iptables
et nous l’administrerons avec ufw
.
Installation du pare-feu
sudo apt-get install -y iptables iptables-persistent ufw
Uen fois installé, nous ouvrons les ports suivants :
- 22 pour autoriser les connexions SSH
- 80 pour autoriser les connexions HTTP
- 443 pour autoriser les connexions HTTPS
Ouverture des ports
sudo ufw allow ssh http https
La configuration finie, nous activons le pare-feu
sudo ufw enable
Redis #
Redis demande juste une petite modification du kernel
sudo sysctl -w vm.overcommit_memory=1
sudo sysctl -p /etc/sysctl.conf
Installation de Docker #
Notre système d’exploitation maintenant à jour, nous installons docker
et docker-compose
pour être capable de gérer les conteneurs qui vont exécuter Nextcloud.
# Installation de docker-ce
sudo apt-get install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# Installation de docker-compose
mkdir -p ~/.docker/cli-plugins/
curl -SL https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-linux-x86_64 -o ~/.docker/cli-plugins/docker-compose
chmod +x ~/.docker/cli-plugins/docker-compose
Démarrage de la stack #
Avant de cloner le dépôt d’exemple, nous installons git.
sudo apt install -y git
Nous clonons le dépôt contenant les fichiers de configuration.
git clone https://github.com/Sykursen/crowdsec-nextcloud.git
Il faut désormais les éditer afin qu’ils correspondent à notre configuration. Nous remplaçons tous les champs example.org
par notre domaine, ainsi que l’ajout de mots de passe robustes dans le fichier .env
.
Une fois tous les fichiers crées et édités, nous lançons le déploiement de notre stack.
sudo docker compose up -d
Ajout du bouncer #
Une fois la stack démarrée avec succès, nous générons un token pour notre bouncer.
sudo docker exec nextcloud-crowdsec-1 cscli bouncers add openresty-nextcloud
Ajouter la clef obtenue dans le fichier /crowdsec/crowdsec-openresty-bouncer.conf.yaml
nano /crowdsec/crowdsec-openresty-bouncer.conf.yaml
API_KEY=<token>
Redémarrer le bouncer
sudo docker compose restart openresty
Support de HTTPS #
Pour plus de sécurité il est recommandé de passer sur le protocole HTTPS en ajoutant un nom de domaine ainsi qu’un certificat TLS.
Une fois notre stack démarré, nous pouvons effectuer une demande de certificat avec la commande suivante.
Attention à bien remplacer example.com votre nom de domaine
sudo docker compose run --rm certbot certonly --webroot --webroot-path /var/www/certbot/ -d <domain>
Une fois le certificat obtenu, nous éditons le fichier .conf/
en rajoutant ces lignes à la fin du fichier.
Attention à bien remplacer example.com par votre nom de domaine
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_tokens off;
proxy_hide_header X-Powered-By;
client_max_body_size 512M;
client_body_timeout 300s;
fastcgi_buffers 64 4K;
ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;
access_log /var/log/nginx/access.log;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=15552000" always;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
location /.well-known/carddav {
return 301 $scheme://example.com/remote.php/dav;
}
location /.well-known/caldav {
return 301 $scheme://example.com/remote.php/dav;
}
location / {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Nous redémarrons une dernière fois notre reverse-proxy
sudo docker compose restart openresty
Tests #
Blockage manuel #
On peut ensuite tester le blockage de façon manuel crowdsec en ajoutant une règle de ban ou de captcha.
sudo docker exec -it nextcloud-crowdsec-1 /bin/bash
cscli decisions add --ip <ip> --duration 1m --type <captcha/ban>
exit
Résultat du blockage par captcha
Résultat du blockage par ban
Détection automatique #
Pour la détection automatique, nous allons tenter d’énumérer 5 utilisateurs différents. Dès la 5 ème tentative détecter notre tentative d’énumération est détecté et bloquer par une décision de blocage immédiat.
Conclusion #
Ainsi, notre instance Nextcloud est désormais disponible en se rendant sur notre nom de domaine. Il est protégé par le réseau CrowdSec et bloque les tentatives d’intrusion automatiquement.
Attention, les certificats SSL expirent tout les 3 mois, voici la procédure pour les régénérer.
sudo docker compose run --rm certbot renew
Sources #
Quelques ressources qui m’ont permis de rédiger cet article.