Acheter un nom de domaine et ouvrir nos services à l’extérieur

Nou allons aujourd’hui poser les fondations de notre service. Cela va consister en deux étapes. La première va nous permettre de disposer d’un nom de domaine, c’est-à-dire de nous référer à un nom tel que k-sper.fr au lieu de notre adresse IP publique lorsque nous voulons accéder à nos services.

La deuxième va consister à installer aujourd’hui les infrastructures de sécurité nécessaires à l’ouverture de notre installation vers le Big Bad Web. Ces infrastructures de composent :

  • d’un proxy inverse (reverse proxy) : nous avons choisi Swag pour sa simplicité d’utilisation, mais aussi pour l’ensemble de fonctionnalités qu’il embarque,
  • d’un système de détection d’intrusion : Crowdsec nous permettra de bénéficier de l’expérience acquise en la matière par l’ensemble des utilisateurs de ce système.

Nous avons installé Docker dans un précédent épisode, et nous allons maintenant installer ces deux applications sous forme de conteneurs Docker, tout en nous assurant qu’ils seront configurés pour protéger l’ensemble de notre système.

Le nom de domaine

L’acquisition du nom de domaine s’effectue auprès d’un registraire de nom de domaine. Ce sont les entités qui vont indiquer quels sont les domaines disponibles et en organiser la vente. Chaque registraire peut attribuer des noms auprès d’un certain nombre de registres (qui correspondent aux extensions de noms, .fr dans le cas de k-sper.fr).

On pourra citer parmi les registraires qui permettent l’attribution de domaines en .fr, ainsi que d’adresses courriel associées :

Sachez qu’il existe un grand nombre d’autres registraires. Je ne vais pas vous en recommander un en particulier. Précisons également que ces organismes proposent souvent aussi des services d’hébergement de vos systèmes. Il s’agit d’une solution qui vous permet de disposer de votre propre système sans avoir le serveur à votre domicile (notre Raspberry Pi). Ce n’est pas la solution décrite dans ce blog mais vous pouvez arriver à des résultats très similaires avec ces prestations.

Une fois le nom de domaine acheté, il convient de le relier à votre adresse IP. Pour cela, nous devons à nouveau considérer votre adressage IP.

S’il est statique, vous allez sur le site de votre registrar, et sous votre nom de domaine, vous recherchez la zone DNS. Vous allez maintenant ajouter une entrée A à cette zone. Je vais vous guider sur la base de copies d’écrans prises auprès d’OVH, qui est le registraire de k-sper.fr. Je vais sélectionner « Ajouter une entrée », puis « A ».

La zone de saisie me demande un sous-domaine et une cible. Je vais commencer sans indiquer de sous-domaine, et en saisissant mon adresse IP statique.

Je valide. Même si le registrar indique que la propagation peut prendre 24h, j’ai constaté personnellement que cela prenait généralement quelques secondes pour ce qui me concerne.

Vous pouvez procéder à nouveau de même avec le sous-domaine www par exemple. Nous verrons plus tard l’intérêt des sous-domaines. A chaque fois que vous créerez un nouveau sous-domaine, vous devrez créer un nouvel enregistrement A pour celui-ci.

Si votre adresse IP est dynamique, vous pouvez effectuer le lien entre votre nom de domaine via l’opérateur de DDNS que vous avez mobilisé pour la mise en œuvre du VPN.

Swag

Swag est un proxy inverse maintenu par l’équipe de linuxserver.io. Cette communauté met à la disposition de ses utilisateurs des logiciels open source sous forme de conteneurs Docker, de manière à les rendre accessibles au plus grand nombre. Leurs contributions portent sur un grand nombre de logiciels différents, du plus simple au plus complexe. Swag est une contribution dont la valeur ajoutée est très significative puisqu’il embarque :

Mais revenons concrètement à notre installation.

Nous nous connectons à notre Raspberry Pi :

ssh -p 2345 k-sper.fr@raspberrypi.local

Nous le mettons à jour :

sudo apt update
sudo apt upgrade

Pour installer Swag, nous allons commencer par créer un nouveau répertoire à son nom à l’intérieur de notre répertoire home :

cd ~
sudo mkdir swag

La commande cd nous permet de nous déplacer dans le système de fichiers, et le symbole ~ correspond à notre répertoire home (en l’occurrence /home/k-sper.fr).

Nous allons dans le répertoire nouvellement créé et ouvrons un nouveau fichier texte que nous appelons docker-compose.yml.

cd swag
sudo nano docker-compose.yml

Le fichier docker-compose.yml est celui qui sera recherché par l’utilitaire Docker compose lorsqu’il cherchera à créer un nouveau conteneur ou à mettre à jour celui qui existe déjà. Le suffixe yml signifie que ce fichier est un document au format yaml (yet another markup language). Ce format est assez simple à s’approprier, je vous rassure. Il est par contre essentiel de respecter sa norme, incluant notamment les indentations et les tirets suivis d’un espace.

Nous allons saisir dans ce fichier le contenu suivant :

---
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    environment:
      - PUID=1000
      - PGID=1000
      - TZ="Europe/Paris"
      - URL=yourdomain.url
      - VALIDATION=http
      - SUBDOMAINS=www, #optionnel
      - CERTPROVIDER= #optionnel
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80 #optionnel
    cap_add:
      - NET_ADMIN
    restart: unless-stopped

Nous voilà plongés brutalement dans le bain gelé de Docker compose. Décortiquons maintenant ce nouveau fichier. Nous y indiquons :

  • que nous allons créer un nouveau service appelé swag,
  • basé sur l’image Docker swag située sur le serveur lscr.io/linuxserver, que nous voulons la dernière version stable (latest) ; une image Docker est un fichier binaire qui contient un sous-système quasi-autonome permettant – en l’occurrence – de faire fonctionner Swag,
  • que nous souhaitons que le nom du conteneur soit swag,
  • que ses variables d’environnement (les métadonnées qui vont constituer les paramètres majeurs de notre conteneur) soient :
    • l’utilisateur dont le numéro d’identifiant (PUID) est 1000, et le groupe d’utilisateurs dont le numéro d’identifiant (PGID) est 1000 ; je reviendrai sur ce point critique plus loin,
    • le fuseau horaire est « Europe/Paris » ; la liste des paramètres de fuseaux horaires peut être trouvée ici ; cela permettra à notre conteneur de disposer de l’horaire de notre zone,
    • notre URL (nom de domaine, sans www ou tout autre sous-domaine),
    • validation : http indique à l’autorité de certification qu’elle va pouvoir établir se certification sur un échange en protocole http,
    • la liste des sous-domaines que nous souhaitons déclarer (à ce stade, nous nous limitons à www),
    • l’autorité de certification que nous souhaitons utiliser ; en laissant ce champ vierge, nous utilisons Let’s Encrypt,
  • le volume est un lieu de stockage des données du conteneur au sein du serveur hôte ; il permet de rendre pérennes des données du conteneur, qui sinon seraient perdues en cas d’arrêt de ce dernier ; il s’agit ici des données de configuration de Swag, qui seront donc disponibles dans le répertoire ~/swag/config (le point désignant le répertoire courant),
  • les ports par lesquels notre conteneur va communiquer vers son environnement (ici les ports 80, spécialisé dans le http, et 443, spécialisé dans le https),
  • les capacités additionnelles (à n’utiliser que lorsque c’est indispensable, et c’est ici le cas), qui vont permettre au conteneur de disposer de prérogatives exceptionnelles ; ici il s’agit de permettre au conteneur de réaliser des opérations d’administration de réseau,
  • la capacité de redémarrer systématiquement en cas d’échec, sauf si l’utilisateur a interrompu volontairement le conteneur.

Avant d’aller plus loin nous devons ouvrir les ports 80 et 443 sur notre routeur, et les orienter vers notre Raspberry Pi. Nous allons donc sur l’interface de notre routeur, sur la page Réseau v4/NAT. Nous saisissons :

Nous pouvons maintenant mettre en route ce conteneur.

sudo docker compose pull

Cette commande permet d’aller récupérer l’image de Swag sur le serveur de linuxserver.io. Puis

sudo docker compose up -d

Là, nous lançons le conteneur à proprement parler. Ce lancement peut prendre un certain temps. Afin de nous assurer du succès de l’opération, nous allons consulter le journal (log) de notre conteneur après deux minutes (le temps que l’ensemble des opérations de lancement soient terminées) :

sudo docker compose logs

Si tout s’est bien passé, il devrait afficher parmi les dernières lignes : Server ready. Le proxy inversé est maintenant disponible.

Pour finaliser cette installation, il nous faudra rediriger les ports de notre routeur (ou box internet). Mais nous ferons cela plus loin, lorsque nous aurons protégé notre système avec Crowdsec.

Crowdsec

Crowdsec est un système de détection d’intrusion (IDS). Cette catégorie regroupe différents types de logiciels dédiés à la sécurité. Crowdsec analyse les journaux (logs) de votre système et de vos applications à la recherche de tentatives d’attaques. Lorsqu’il identifie une telle tentative, il va « bannir » l’attaquant, c’est-à-dire qu’il va lui interdire l’accès à votre système. Mais en complément de cela, Crowdsec met en commun entre tous ses utilisateurs la connaissance de la liste des principaux attaquants. Ainsi, une adresse IP ayant attaqué plusieurs utilisateurs sera positionnée sur une liste noire diffusée à l’ensemble des utilisateurs et bannie a priori.

Maintenant passons à l’installation de Crowdsec.

Nous nous connectons à notre Raspberry Pi :

ssh -p 2345 k-sper.fr@raspberrypi.local

Nous le mettons à jour (en fait ce n’est pas indispensable mais c’est une habitude à prendre) :

sudo apt update
sudo apt upgrade

Nous allons créer dans Docker un volume « logs » vers lequel nous enverrons tous les journaux des applications que nous voudrons protéger via Crowdsec (en particulier Swag, comme nous le verrons plus loin). Ce volume permettra à Crowdsec de lire les journaux des applications à protéger.

sudo docker volume create logs

Nous allons maintenant créer un répertoire Crowdsec et nous positionner dans ce répertoire :

sudo mkdir crowdsec
cd crowdsec

A l’intérieur, nous allons créer le fichier docker-compose.yml :

sudo nano docker-compose.yml

Et nous allons saisir les éléments suivants dans le fichier :

services:
  crowdsec:
    image: crowdsecurity/crowdsec:latest
    restart: unless-stopped
    container_name: crowdsec
    environment:
      COLLECTIONS: "crowdsecurity/nginx"
      GID: "${GID-1000}"
      TZ: Europe/Paris
    volumes:
      - logs:/var/log
      - ./data:/var/lib/crowdsec/data/
      - ./crowdsec:/etc/crowdsec/
    ports:
      - 8080:8080

volumes:
  logs:
    external: true

Ce fichier n’introduit pas vraiment de notion nouvelle si ce n’est la référence à un volume externe (logs) que nous retrouvons en fin de fichier, et que nous avons déjà évoqué.

Nous lançons donc notre conteneur :

sudo docker compose up -d

Crowdsec est maintenant fonctionnel mais il ne lit aucun journal et n’a pas de « videur » pour bannir les attaquants.

Nous allons entrer dans le conteneur pour lui demander de créer un identifiant pour le videur de Swag :

sudo docker exec -ti crowdsec /bin/sh

L’ordre docker exec -ti nous permet en effet d’entrer dans le conteneur en mode interactif. La commande /bin/sh indique que nous voulons exécuter un shell, c’est-à-dire pouvoir exécuter des commandes. Nous allons donc exécuter, à l’intérieur du conteneur, la commande qui crée un lien avec un videur :

cscli bouncer add nginx

Nous devrions alors avoir une réponse du type de celle-ci :

Nous allons copier cette clé pour la reporter dans notre conteneur Swag. Puis nous sortons de notre conteneur :

exit

Protéger Swag avec Crowdsec

Nous allons maintenant orienter le journal de Swag vers notre volume « logs » et installer le videur de Crowdsec dans notre proxy inverse. Pour cela, retournons vers notre conteneur Swag.

cd ~/swag

Nous arrêtons le conteneur.

sudo docker compose down

Nous allons modifier notre conteneur afin de lui indiquer de travailler avec Crowdsec. Les modifications apportées apparaîtront ici en couleur.

---
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    environment:
      - PUID=1000
      - PGID=1000
      - TZ="Europe/Paris"
      - URL=yourdomain.url
      - VALIDATION=http
      - SUBDOMAINS=www, #optionnel
      - CERTPROVIDER= #optionnel
      - DOCKER_MODS=linuxserver/mods:swag-crowdsec
      - CROWDSEC_API_KEY=v/7oenS0pZUJq5ZPxgViDDu4KkhJXXnqnUbrSLHK4aA
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80 #optionnel
      - logs:/config/log
    cap_add:
      - NET_ADMIN
    restart: unless-stopped

volumes:
  logs:
    external: true

Nous avons apporté trois modifications à notre conteneur. La première consiste à utiliser une personnalisation de Swag (DOCKER_MODS) qui vise à l’intégrer à Crowdsec et plus précisément à activer en son sein un videur (bouncer) de Crowdsec. La deuxième indique la clé du videur que nous avons créé dans Crowdsec ; nous devons reporter là la clé que nous avions copiée à la fin de la création du conteneur Crowdsec. La troisième précise que les journaux de Swag devront être écrits dans le volume « logs » que nous avons créé pour Crowdsec. Ainsi, ce journal pourra être lu par Crowdsec.

Nous relançons notre conteneur Swag :

sudo docker compose up -d

Maintenant il nous faut indiquer à Crowdsec comment lire le journal de Swag et au videur de Swag comment écouter les messages de Crowdsec.

Nous allons donc retourner dans le répertoire de Crowdsec :

cd ~/crowdsec

Nous allons ouvrir le fichier dans lequel Crowdsec stocke les modalités de lecture des journaux des applications :

sudo nano crowdsec/acquis.yaml

Et dans ce fichier nous allons saisir les informations suivantes :

---
filenames:
  - /var/log/nginx/*.log
labels:
  type: nginx

Ces quelques lignes indiquent que Crowdsec doit aller chercher le journal à l’emplacement /var/log/nginx/*.log dans le conteneur Crowdsec, ce qui correspond à un emplacement au sein du volume « logs ».

Afin qu’il prenne en compte notre modification, nous relançons le conteneur Crowdsec :

sudo docker compose down
sudo docker compose up -d

Maintenant nous allons vérifier que notre conteneur Crowdsec fonctionne bien et que le lien a bien été fait avec son videur Swag :

sudo docker exec -ti crowdsec cscli bouncers list

Nous devrions avoir un retour du type suivant :

Il est important de contrôler l’heure indiquée en dessous de « Last API pull ». Le dernier appel doit avoir eu lieu dans la minute ou les deux minutes précédant notre commande (attention : l’heure est indiquée dans Crowdsec en temps universel et non calculée sur votre fuseau horaire ; il faut donc prendre en compte le décalage horaire).

Limiter les accès géographiquement

Selon l’usage que nous avons de nos services, nous pouvons estimer que seuls certains pays devraient y accéder. Ou au contraire qu’ils devraient être interdits à quelques pays. L’un des intérêts de Swag est qu’il permet de mettre en oeuvre ce type de protection, via l’utilisation de l’extension Maxmind.

Maxmind gère une base de données dont la fonction est d’indiquer la localisation des adresses IP. Ainsi, via cette base, Swag sera capable d’identifier de quel pays viennent les tentatives de connexion à votre système, et ainsi de les bloquer ou de les accepter selon le paramétrage que vous aurez choisi.

Voyons donc comment utiliser cette extension.

Nous commençons par acquérir une licence gratuite auprès de Maxmind à cette adresse. Puis nous nous positionnons sur notre répertoire swag et ouvrons le fichier docker-compose.yml pour lui apporter les modifications ci-dessous :

---
services:
  swag:
    image: lscr.io/linuxserver/swag:latest
    container_name: swag
    environment:
      - PUID=1000
      - PGID=1000
      - TZ="Europe/Paris"
      - URL=yourdomain.url
      - VALIDATION=http
      - SUBDOMAINS=www, #optionnel
      - CERTPROVIDER= #optionnel
      - DOCKER_MODS=linuxserver/mods:swag-crowdsec|linuxserver/mods:swag-maxmind
      - CROWDSEC_API_KEY=v/7oenS0pZUJq5ZPxgViDDu4KkhJXXnqnUbrSLHK4aA
      - MAXMINDDB_LICENSE_KEY=<notre-licence>
    volumes:
      - ./config:/config
    ports:
      - 443:443
      - 80:80 #optionnel
      - logs:/config/log
    cap_add:
      - NET_ADMIN
    restart: unless-stopped

volumes:
  logs:
    external: true

Ces modifications indiquent que nous souhaitons utiliser l’extension Maxmind et renseignent la clé de licence pour y accéder. Vous aurez noté le caractère « | » (généralement sur les claviers AltGr+6) utilisé dans ce script pour séparer les différentes extensions Swag que nous utilisons.

Nous relançons le conteneur pour appliquer les modifications :

sudo docker compose down && sudo docker compose up -d

Puis nous ouvrons le fichier ~/swag/config/nginx/nginx.conf et ajoutons la ligne suivante dans la section http :

include /config/nginx/maxmind.conf;

Cette ligne indique que la configuration de Nginx doit prendre en compte le fichier maxmind.conf, que nous allons maintenant ouvrir pour le faire évoluer ainsi :

geoip2 /config/geoip2db/GeoLite2-City.mmdb {
    auto_reload 1w;
    $geoip2_data_city_name   city names en;
    $geoip2_data_postal_code postal code;
    $geoip2_data_latitude    location latitude;
    $geoip2_data_longitude   location longitude;
    $geoip2_data_state_name  subdivisions 0 names en;
    $geoip2_data_state_code  subdivisions 0 iso_code;
    $geoip2_data_continent_code   continent code;
    $geoip2_data_country_iso_code country iso_code;
}

# Country Codes: https://en.wikipedia.org/wiki/ISO_3166-2

map $geoip2_data_country_iso_code $geo-whitelist {
    default yes;
    # Example for whitelisting a country, comment out 'default yes;' above and uncomment 'default no;' and the whitelisted country below
    # default no;
    # UK yes;
    FR yes;
    IT yes;
}

map $geoip2_data_country_iso_code $geo-blacklist {
    default yes;
    # Example for blacklisting a country, uncomment the blacklisted country below
    # UK no;
    VN no;
 }

geo $lan-ip {
    default no;
    10.0.0.0/8 yes;
    172.16.0.0/12 yes;
    192.168.0.0/16 yes;
    127.0.0.1 yes;
}

Nous avons ici laissé passer tous les pays par défaut (default yes), avons spécifié que les connexions entrantes de la France (FR) et de l’Italie (IT) devaient être acceptées, et que celles venant du Vietnam (VN) devaient être rejetées. Nous aurions pu rejeter tous les pays par défaut en remplaçant les deux occurrences de default yes par default no.

Le dernier bloc, que nous laissons tel quel, indique que nous acceptons la connexion depuis toutes les adresses de notre réseau local. Il est à noter que ces adresses ne sont pas associées à un pays. Ainsi, dans certains cas, nous pouvons accepter les connexions du réseau local tous en refusant celles de notre propre pays de résidence.

Lorsque nous ouvrirons l’accès à des services à l’avenir, nous devrons insérer dans les configurations du proxy inverse de chacune de ces applications les lignes suivantes, à l’intérieur du bloc server :

 server {
listen 443 ssl;
listen [::]:443 ssl;

server_name some-app.*;
include /config/nginx/ssl.conf;
client_max_body_size 0;

if ($lan-ip = yes) { set $geo-whitelist yes; }
if ($geo-whitelist = no) { return 404; }


location / {

Nous pouvons maintenant redémarrer notre conteneur afin qu’il prenne en compte les changements, et nous allons pouvoir ouvrir notre système à l’extérieur :

sudo docker compose down && sudo docker compose up -d

Router les ports de notre box

Notre dernière action en vue d’être opérationnels consiste à router les ports de notre box. Par cette action, nous allons indiquer à notre box que lorsqu’elle est appelée en mode http (port 80) ou en mode https (http sécurisé, port 443), elle doit orienter les échanges vers notre Raspberry Pi.

Pour cela, nous quittons la ligne de commande et nous dirigeons vers notre navigateur internet. Nous allons sur l’interface de notre box (généralement l’IP 192.168.1.1 ou 192.168.0.1) et recherchons les règles NAT v4.

Là nous saisissons les éléments suivants :

  • pour le port 80/TCP en entrée, nous le routons vers le port 80 sur l’adresse IP de notre Pi,
  • de même pour le port 443/TCP.

Notre système est maintenant pleinement opérationnel. Nous avons construit la coquille qui nous permettra d’héberger des services … mais nous n’avons pour l’instant aucun service !


Si vous avez apprécié cet article, n’hésitez pas à laisser à son auteur un petit commentaire : il l’encouragera à le maintenir et à le développer.


Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *