Comment installer Discourse avec un WAF open source

Je ne suis pas autorisé à publier dans la section « How-Tos », je vais donc poster ceci ici :slight_smile:

Aperçu

L’objectif est d’ajouter un WAF (pare-feu d’application web) aux ressources existantes tout en maintenant des performances adéquates sur une petite installation. Dans cet exemple, nous utiliserons une seule instance EC2. Comme Discourse est exécuté dans Docker, nous pouvons installer NGINX sur l’hôte et utiliser la fonction de proxy pass pour acheminer le trafic vers notre conteneur Docker. Nous utilisons également RDS pour PostgreSQL.

Configuration de Discourse

Notre première étape consiste à configurer notre fichier app.yml pour une installation personnalisée de Discourse. Dans cet exemple, nous exécuterons les rôles web et redis. Nous n’utiliserons pas les rôles DB ou SSL.

Nous devons également nous assurer de ne pas exposer les ports web du conteneur Docker. La raison en est que nous exposerons notre interface web via le proxy NGINX sur l’hôte.

Exemple de fichier app.yml

## Ceci est le modèle de conteneur Docker Discourse tout-en-un, autonome
##
## Après avoir apporté des modifications à ce fichier, vous DEVEZ reconstruire
## /var/discourse/launcher rebuild app
##
## SOYEZ TRÈS PRUDENT EN ÉDITANT !
## LES FICHIERS YAML SONT EXTRÊMEMENT SENSIBLES AUX ERREURS D'ESPACEMENT OU D'ALIGNEMENT !
## visitez http://www.yamllint.com/ pour valider ce fichier si nécessaire
templates:
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
## Décommentez ces deux lignes si vous souhaitez ajouter Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"


## Quels ports TCP/IP ce conteneur doit-il exposer ?
## Si vous souhaitez que Discourse partage un port avec un autre serveur web comme Apache ou nginx,
## consultez https://meta.discourse.org/t/17247 pour plus de détails
expose:
#  - "80:80"   # http
#  - "443:443" # https


params:
  db_default_text_search_config: "pg_catalog.english"
  ## Définir db_shared_buffers à un maximum de 25 % de la mémoire totale.
  ## sera défini automatiquement par bootstrap en fonction de la RAM détectée, ou vous pouvez le remplacer
  db_shared_buffers: "768MB"
  ## peut améliorer les performances de tri, mais ajoute une utilisation de la mémoire par connexion
  #db_work_mem: "40MB"
  ## Quelle révision Git ce conteneur doit-il utiliser ? (par défaut : tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en
  ## Combien de requêtes web simultanées sont prises en charge ? Dépend de la mémoire et des cœurs CPU.
  ## sera défini automatiquement par bootstrap en fonction des CPU détectés, ou vous pouvez le remplacer
  UNICORN_WORKERS: 2

  ## TODO : Le nom de domaine auquel cette instance Discourse répondra
  ## Requis. Discourse ne fonctionnera pas avec une adresse IP brute.
  DISCOURSE_HOSTNAME: cloudforums.net

  ## Décommentez si vous souhaitez que le conteneur soit démarré avec le même
  ## nom d'hôte (option -h) que spécifié ci-dessus (par défaut « $hostname-$config »)
  #DOCKER_USE_HOSTNAME: true

  ## TODO : Liste d'e-mails séparés par des virgules qui seront administrateurs et développeurs
  ## lors de l'inscription initiale, par exemple « user1@example.com,user2@example.com »
  DISCOURSE_DEVELOPER_EMAILS: 'votreemail@domain.com'

  ## TODO : Le serveur de messagerie SMTP utilisé pour valider les nouveaux comptes et envoyer des notifications
  # L'adresse SMTP, le nom d'utilisateur et le mot de passe sont requis
  # ATTENTION : le caractère '#' dans le mot de passe SMTP peut causer des problèmes !
  DISCOURSE_SMTP_ADDRESS: VOTRE_SMTP
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: VOTRE_NOM_UTILISATEUR_SMTP
  DISCOURSE_SMTP_PASSWORD: VOTRE_MOT_DE_PASSE_SMTP
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (optionnel, par défaut true)

  ## Si vous avez ajouté le modèle Lets Encrypt, décommentez ci-dessous pour obtenir un certificat SSL gratuit
  #LETSENCRYPT_ACCOUNT_EMAIL: moi@example.com

  DISCOURSE_DB_SOCKET: ''
  DISCOURSE_DB_USERNAME: VOTRE_NOM_UTILISATEUR
  DISCOURSE_DB_PASSWORD: VOTRE_MOT_DE_PASSE
  DISCOURSE_DB_HOST: VOTRE_HOTE

  ## L'adresse HTTP ou HTTPS du CDN pour cette instance Discourse (configurée pour récupérer)
  ## voir https://meta.discourse.org/t/14857 pour plus de détails
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com
## Le conteneur Docker est sans état ; toutes les données sont stockées dans /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## Les plugins vont ici
## voir https://meta.discourse.org/t/19157 pour plus de détails
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Toute commande personnalisée à exécuter après la construction
run:
  - exec: echo "Début des commandes personnalisées"
  ## Si vous souhaitez définir l'adresse e-mail « De » pour votre première inscription, décommentez et modifiez :
  ## Après avoir reçu le premier e-mail d'inscription, re-commentez la ligne. Elle ne doit être exécutée qu'une seule fois.
  - exec: rails c "SiteSetting.enable_badge_sql = true"
  - exec: echo "Fin des commandes personnalisées"

Démarrage de l’installation de Discourse

Cloner le dépôt Discourse

sudo -u root git clone https://github.com/discourse/discourse_docker.git /var/discours

Copier le fichier app.yml personnalisé pour l’installation

Vous pouvez exécuter la commande ci-dessous pour le faire pour vous. Remplacez simplement le test « COLLEZ VOTRE FICHIER APP.YML COMPLET ICI » par votre fichier app.yml complet.

sudo -u root cat > /var/discourse/containers/app.yml <<\EOF
COLLEZ VOTRE FICHIER APP.YML COMPLET ICI
EOF

Exécuter la configuration de Discourse

Démarrez la configuration de Discourse sans invites en utilisant la commande ci-dessous.

sudo -u yes "" | /var/discourse/./discourse-setup

Discourse devrait prendre 10 à 15 minutes pour s’installer.

Installation de NGINX, WAF et GEOIP

Exécutez les mises à jour et installez les prérequis

apt update -y
apt upgrade -y
apt -y install libpcre3-dev libssl-dev unzip build-essential daemon libxml2-dev libxslt1-dev libgd-dev libgeoip-dev zlib1g-dev libpcre3

Installez la base de données MindMax pour pouvoir exécuter des règles GEO IP basées sur la localisation. Vous pouvez utiliser cette base de données pour acheminer ou bloquer le trafic en fonction de la localisation. Nos journaux NGINX afficheront désormais le pays de l’utilisateur, même si vous n’activez pas le blocage géographique. I

sudo add-apt-repository -y ppa:maxmind/ppa
apt update -y
apt install -y libmaxminddb0 libmaxminddb-dev mmdb-bin

Télécharger et extraire NGINX et NAXSI WAF

Remarque : Remplacez NGINX par la dernière version

mkdir ~/nginx-waf
wget https://nginx.org/download/nginx-1.16.1.tar.gz -O ~/nginx-waf/nginx.tar.gz
tar xzf ~/nginx-waf/nginx.tar.gz -C ~/nginx-waf
wget https://github.com/nbs-system/naxsi/archive/master.zip -O ~/nginx-waf/waf.zip
unzip ~/nginx-waf/waf.zip -d ~/nginx-waf/

Cloner le module GEO IP2 pour NGINX via Git

apt install -y git
git clone https://github.com/leev/ngx_http_geoip2_module.git /etc/ngx_http_geoip2_module

Compiler NGINX

Nous allons créer un script pour le faire pour nous et l’exécuter ci-dessous.


cat > ~/nginx-waf/nginx-1.16.1/install.sh <<\EOF
cd ~/nginx-waf/nginx-1.16.1/
./configure --conf-path=/etc/nginx/nginx.conf --add-module=../naxsi-master/naxsi_src/ --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --user=www-data --group=www-data --with-http_ssl_module --without-mail_pop3_module --without-mail_smtp_module --without-mail_imap_module --without-http_uwsgi_module --add-dynamic-module=/etc/ngx_http_geoip2_module --without-http_scgi_module --prefix=/usr
make
make install
EOF

sh ~/nginx-waf/nginx-1.16.1/install.sh

Créer des règles de pare-feu

La configuration par défaut ci-dessous activera le WAF et bloquera les requêtes malveillantes. Si vous souhaitez exécuter le WAF en mode apprentissage, vous pouvez le faire en ajoutant Learning Mode au fichier de règles ci-dessous.

cp ~/nginx-waf/naxsi-master/naxsi_config/naxsi_core.rules /etc/nginx/



cat > /etc/nginx/naxsi.rules <<\EOF
SecRulesEnabled;
DeniedUrl "/RequestDenied";
## Vérifier les règles Naxsi
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
EOF

Créer le fichier de configuration NGINX

Par défaut, dans ce fichier de configuration, le blocage géographique est commenté, mais vous pouvez le décommenter et l’activer si vous le souhaitez.

Remarque : Nous devons exécuter NGINX SANS SSL au départ. Parce que nous avons besoin que certbot voie un domaine actif. Nous obtiendrons SSL à une étape ultérieure et copierons en réalité un nouveau fichier de configuration NGINX pour le remplacer.

Remarque : Remplacez cloudforums.net par votre nom de domaine

cat > /etc/nginx/nginx.conf <<\EOF
#user  nobody;
worker_processes  1;
load_module modules/ngx_http_geoip2_module.so;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    include       /etc/nginx/naxsi_core.rules;
        include     /etc/nginx/conf.d/*.conf;
        include     /etc/nginx/sites-enabled/*;
    
    geoip2 /etc/geo_ip/GeoLite2-Country.mmdb {
        $geoip2_data_country_code source=$remote_addr country iso_code;
        $geoip2_data_country_name source=$remote_addr country names en;
    }  
    log_format  main_geo  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          '$geoip2_data_country_code $geoip2_data_country_name';
    
    access_log /var/log/nginx/access.log main_geo;
   
    default_type  application/octet-stream;
    error_log /var/log/nginx/error.log;
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    server {
        listen       80;
        server_name cloudforums.net;
        root /;
 
        location /.well-known/acme-challenge/ {
                root /var/www;
        }
        location / {
            return 301 https://$host$request_uri;
            include /etc/nginx/naxsi.rules;
                root   html;
                index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}
EOF

Créer un script upstart pour NGINX

Nous devons créer un script pour démarrer le service NGINX.

cat > /etc/init.d/nginx <<\EOF
#! /bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 
 DAEMON=/usr/sbin/nginx 
 NAME=nginx 
 DESC=nginx
 
 test -x $DAEMON || exit 0 
 # Inclure les valeurs par défaut de nginx si disponibles 
 if [ -f /etc/nginx ] ; then 
         . /etc/nginx 
 fi
 
 set -e
 
 case "$1" in 
     start)
         echo -n "Démarrage de $DESC: " 
         start-stop-daemon --start --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON -- $DAEMON_OPTS 
         echo "$NAME." 
         ;; 
     stop) 
         echo -n "Arrêt de $DESC: " 
         start-stop-daemon --stop --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     restart|force-reload) 
         echo -n "Redémarrage de $DESC: " 
         start-stop-daemon --stop --quiet --pidfile \ 
             /var/run/nginx.pid --exec $DAEMON 
         sleep 1 start-stop-daemon --start --quiet --pidfile \ 
             /var/run/nginx.pid --exec $DAEMON -- $DAEMON_OPTS 
         echo "$NAME." 
         ;; 
     reload) 
         echo -n "Rechargement de la configuration $DESC: " 
         start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     *) 
         N=/etc/init.d/$NAME 
         echo "Utilisation : $N {start|stop|restart|force-reload}" >&2 
         exit 1 
         ;; 
 esac
 
 exit 0
EOF

systemctl daemon-reload

Créer le fichier de service NGINX

cat > /lib/systemd/system/nginx.service <<\EOF
[Unit]
Description=Un serveur web haute performance et un serveur proxy inverse
Documentation=man:nginx(8)
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/usr/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF

Télécharger la base de données Geo IP par pays

mkdir /etc/geo_ip
wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
gzip -d GeoLite2-Country.mmdb.gz
mv GeoLite2-Country.mmdb /etc/geo_ip/

Activer et démarrer NGINX

systemctl stop apache2
systemctl daemon-reload
systemctl enable nginx
systemctl start nginx

Ajouter un certificat SSL pour votre domaine

N’oubliez pas de remplacer cloudforums.net par votre domaine.com.

mkdir /var/www
apt-get -y update
apt-get -y install letsencrypt
yes "n" | letsencrypt certonly --webroot --agree-tos -w /var/www -d cloudforums.net -m votreemail@domain.com

Créer un nouveau fichier NGINX avec des règles SSL

Créez un nouveau fichier NGINX avec votre certificat SSL que vous venez de télécharger.

Remarque : Remplacez cloudforums.net par votre domaine


cat > /etc/nginx/nginx.conf <<\EOF
#user  nobody;
worker_processes  1;
load_module modules/ngx_http_geoip2_module.so;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    include       /etc/nginx/naxsi_core.rules;
        include     /etc/nginx/conf.d/*.conf;
        include     /etc/nginx/sites-enabled/*;
    
    geoip2 /etc/geo_ip/GeoLite2-Country.mmdb {
        $geoip2_data_country_code source=$remote_addr country iso_code;
        $geoip2_data_country_name source=$remote_addr country names en;
    }  
    log_format  main_geo  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" '
                          '$geoip2_data_country_code $geoip2_data_country_name';
    
    #***********************************************************
    #Décommentez pour activer le blocage géographique. Par défaut, seul les États-Unis est autorisé dans les paramètres ci-dessous
    #***********************************************************
    #map $geoip2_data_country_code $allowed_country {
    #    default no;
    #    US yes;
    # }
    access_log /var/log/nginx/access.log main_geo;
   
    default_type  application/octet-stream;
    error_log /var/log/nginx/error.log;
    #access_log  logs/access.log  main;
    sendfile        on;
    #tcp_nopush     on;
    #keepalive_timeout  0;
    keepalive_timeout  65;
    #gzip  on;
    server {
        listen       80;
        server_name  cloudforums.net;
        root /;
 
        location /.well-known/acme-challenge/ {
                root /var/www;
        }
        location / {
            return 301 https://$server_name$request_uri;
            include /etc/nginx/naxsi.rules;
                root   html;
                index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    server {
      listen 443 ssl;  listen [::]:443 ssl;
      server_name cloudforums.net;  
      ssl on;
      ssl_certificate      /etc/letsencrypt/live/cloudforums.net/fullchain.pem;
      ssl_certificate_key  /etc/letsencrypt/live/cloudforums.net/privkey.pem;
      ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
      ssl_protocols TLSv1.2;
      ssl_prefer_server_ciphers on;
      ssl_session_cache shared:SSL:10m;
      add_header Strict-Transport-Security "max-age=63072000;";
      ssl_stapling on;
      ssl_stapling_verify on;
      client_max_body_size 0;
      location / {
        proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
        proxy_set_header Host $http_host;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Real-IP $remote_addr;
       }
    }
    # Serveur HTTPS
    #
    #server {
    #    listen       443 ssl;
    #    server_name  localhost;
    #    ssl_certificate      cert.pem;
    #    ssl_certificate_key  cert.key;
    #    ssl_session_cache    shared:SSL:1m;
    #    ssl_session_timeout  5m;
    #    ssl_ciphers  HIGH:!aNULL:!MD5;
    #    ssl_prefer_server_ciphers  on;
    #    location / {
    #        root   html;
    #        index  index.html index.htm;
    #    }
    #}
}
EOF

Redémarrer NGINX

systemctl restart nginx

Vous avez maintenant un WAF open source formidable !! :slight_smile:

Publicité sans vergogne

Assurez-vous de nous rejoindre sur cloudforums.net si vous avez apprécié ce guide. Nous serions ravis de vous avoir parmi nous. Nous ne vendons rien et n’avons pas de publicités. Nous cherchons simplement de bons posts IT :slight_smile:

Installation automatisée en un clic de NGINX / WAF après l’installation de Discourse. N’oubliez pas de modifier le nom de domaine de cloudforums.net pour le vôtre.

Il suffit d’exécuter

sh discourse_install.sh