Come installare un Discourse con un WAF open source

Non mi è consentito pubblicare nella sezione How-To, quindi pubblicherò qui :slight_smile:

Panoramica

L’obiettivo è aggiungere un WAF alle risorse esistenti mantenendo le prestazioni su un’installazione di piccole dimensioni. In questo esempio, utilizzeremo una singola istanza EC2. Poiché Discourse viene eseguito in Docker, siamo in grado di installare NGINX sull’host e utilizzare proxy_pass per instradare il traffico nel nostro contenitore Docker. Stiamo inoltre utilizzando RDS per PostgreSQL.

Configurazione di Discourse

Il nostro primo passo è configurare il file app.yml per un’installazione personalizzata di Discourse. In questo esempio, eseguiremo i ruoli web e redis. Non utilizzeremo il ruolo DB né i ruoli SSL.

Inoltre, vogliamo assicurarci di non esporre le porte web nel contenitore Docker. Il motivo è che esporremo il web tramite il proxy NGINX sull’host.

Esempio di file app.yml

## questo è il template del contenitore Docker Discourse all-in-one, standalone
##
## Dopo aver apportato modifiche a questo file, DEVI ricostruire
## /var/discourse/launcher rebuild app
##
## FAI *MOLTA* ATTENZIONE DURANTE LA MODIFICA!
## I FILE YAML SONO SUPER SUPER SENSIBILI A ERRORI NEGLI SPAZI O NELL'ALLINEAMENTO!
## visita http://www.yamllint.com/ per convalidare questo file se necessario
templates:
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
## Scommenta queste due righe se desideri aggiungere Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"


## quali porte TCP/IP deve esporre questo contenitore?
## Se desideri che Discourse condivida una porta con un altro server web come Apache o nginx,
## consulta https://meta.discourse.org/t/17247 per i dettagli
expose:
#  - "80:80"   # http
#  - "443:443" # https


params:
  db_default_text_search_config: "pg_catalog.english"
  ## Imposta db_shared_buffers al massimo al 25% della memoria totale.
  ## verrà impostato automaticamente da bootstrap in base alla RAM rilevata, oppure puoi sovrascriverlo
  db_shared_buffers: "768MB"
  ## può migliorare le prestazioni di ordinamento, ma aumenta l'utilizzo della memoria per connessione
  #db_work_mem: "40MB"
  ## Quale revisione Git deve utilizzare questo contenitore? (default: tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en
  ## Quanti richieste web simultanee sono supportate? Dipende dalla memoria e dai core della CPU.
  ## verrà impostato automaticamente da bootstrap in base alle CPU rilevate, oppure puoi sovrascriverlo
  UNICORN_WORKERS: 2

  ## TODO: Il nome di dominio a cui risponderà questa istanza di Discourse
  ## Obbligatorio. Discourse non funzionerà con un semplice indirizzo IP.
  DISCOURSE_HOSTNAME: cloudforums.net

  ## Scommenta se desideri che il contenitore venga avviato con lo stesso
  ## hostname (opzione -h) specificato sopra (default "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: Elenco di email separate da virgola che diventeranno amministratori e sviluppatori
  ## all'iscrizione iniziale, ad esempio 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'youremail@domain.com'

  ## TODO: Il server SMTP utilizzato per validare nuovi account e inviare notifiche
  ## INDIRIZZO SMTP, nome utente e password sono obbligatori
  ## ATTENZIONE: il carattere '#' nella password SMTP può causare problemi!
  DISCOURSE_SMTP_ADDRESS: YOUR_SMTP
  DISCOURSE_SMTP_PORT: 587
  DISCOURSE_SMTP_USER_NAME: YOUR_SMTP_USERNAME
  DISCOURSE_SMTP_PASSWORD: YOUR_SMTP_PASSWORD
  #DISCOURSE_SMTP_ENABLE_START_TLS: true           # (opzionale, default true)

  ## Se hai aggiunto il template Lets Encrypt, scommenta qui sotto per ottenere un certificato SSL gratuito
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

  DISCOURSE_DB_SOCKET: ''
  DISCOURSE_DB_USERNAME: YOUR_USERNAME
  DISCOURSE_DB_PASSWORD: YOUR_PASSWORD
  DISCOURSE_DB_HOST: YOUR_HOST

  ## L'indirizzo CDN http o https per questa istanza di Discourse (configurato per il recupero)
  ## consulta https://meta.discourse.org/t/14857 per i dettagli
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com
## Il contenitore Docker è senza stato; tutti i dati sono archiviati in /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## I plugin vanno qui
## consulta https://meta.discourse.org/t/19157 per i dettagli
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## Qualsiasi comando personalizzato da eseguire dopo la compilazione
run:
  - exec: echo "Inizio dei comandi personalizzati"
  ## Se desideri impostare l'indirizzo email 'From' per la tua prima registrazione, scommenta e modifica:
  ## Dopo aver ricevuto la prima email di iscrizione, rimetti in commento la riga. Deve essere eseguita solo una volta.
  - exec: rails c "SiteSetting.enable_badge_sql = true"
  - exec: echo "Fine dei comandi personalizzati"

Inizia l’installazione di Discourse

Clona il repository Discourse

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

Copia il file app.yml personalizzato per l’installazione

Puoi eseguire il comando seguente per farlo per te. Sostituisci semplicemente il testo “PASTE YOUR FULL APP.YML HERE” con il tuo intero file app.yml.

sudo -u root cat > /var/discourse/containers/app.yml <<\EOF
PASTE YOUR FULL APP.YML HERE
EOF

Esegui la configurazione di Discourse

Avvia la configurazione di Discourse senza prompt utilizzando il comando seguente.

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

Discourse dovrebbe impiegare 10-15 minuti per l’installazione.

Installazione di NGINX, WAF e GEOIP

Esegui gli aggiornamenti e installa i prerequisiti

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

Installa il database MindMax in modo da poter eseguire regole GEO IP in base alla posizione. Puoi utilizzare questo database per instradare o bloccare il traffico in base alla posizione. I nostri log NGINX mostreranno ora il paese dell’utente anche se non attivi il blocco geografico.

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

Scarica ed estrai NGINX e NAXSI WAF

Nota: Sostituisci NGINX con l’ultima versione

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/

Clona Git il modulo GEO IP2 per NGINX

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

Compila NGINX

Costruiremo uno script per farlo per noi e lo eseguiremo qui sotto


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

Crea le regole del firewall

Di seguito, l’impostazione predefinita attiverà il WAF e bloccherà le richieste dannose. Se desideri eseguire il WAF in modalità di apprendimento, puoi farlo aggiungendo Learning Mode al file di regole qui sotto.

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



cat > /etc/nginx/naxsi.rules <<\EOF
SecRulesEnabled;
DeniedUrl "/RequestDenied";
## Controlla le regole Naxsi
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
EOF

Crea il file di configurazione NGINX

Di default in questo file di configurazione, il blocco geografico è commentato, ma puoi scommentarlo e attivarlo se lo desideri.

Nota: Dobbiamo eseguire NGINX SENZA SSL all’inizio. Perché abbiamo bisogno che certbot veda un dominio attivo. Otterremo SSL in un passaggio successivo e in realtà copieremo un nuovo file di configurazione NGINX per sostituire questo

Nota: Sostituisci cloudforums.net con il tuo nome di dominio

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

Crea lo script upstart per NGINX

Dobbiamo creare uno script per avviare il servizio 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 
 # Includi i valori predefiniti di nginx se disponibili 
 if [ -f /etc/nginx ] ; then 
         . /etc/nginx 
 fi
 
 set -e
 
 case "$1" in 
     start)
         echo -n "Avvio $DESC: " 
         start-stop-daemon --start --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON -- $DAEMON_OPTS 
         echo "$NAME." 
         ;; 
     stop) 
         echo -n "Arresto $DESC: " 
         start-stop-daemon --stop --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     restart|force-reload) 
         echo -n "Riavvio $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 "Ricaricamento configurazione $DESC: " 
         start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     *) 
         N=/etc/init.d/$NAME 
         echo "Utilizzo: $N {start|stop|restart|force-reload}" >&2 
         exit 1 
         ;; 
 esac
 
 exit 0
EOF

systemctl daemon-reload

Crea il file di servizio NGINX

cat > /lib/systemd/system/nginx.service <<\EOF
[Unit]
Description=A high performance web server and a reverse proxy server
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

Scarica il database Geo IP Country

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/

Abilita e avvia NGINX

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

Aggiungi il certificato SSL per il tuo dominio

Non dimenticare di sostituire cloudforums.net con il tuo dominio.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 youremail@domain.com

Crea un nuovo file NGINX con le regole SSL

Crea un nuovo file NGINX con il certificato SSL appena scaricato.

Nota: cambia cloudforums.net con il tuo dominio


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';
    
    #***********************************************************
    #Scommenta per fare il blocco geografico. Di default è impostato solo sugli USA nelle impostazioni sottostanti
    #***********************************************************
    #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;
       }
    }
    # Server 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

Riavvia NGINX

systemctl restart nginx

Ora hai un fantastico WAF open source!! :slight_smile:

Pubblicità senza vergogna

Assicurati di unirti a noi su cloudforums.net se ti è piaciuta questa guida. Saremmo davvero felici di averti qui. Non vendiamo nulla né abbiamo pubblicità. Cerchiamo solo buoni post IT :slight_smile:

Installazione automatizzata in un solo click di NGINX / WAF dopo aver installato Discourse. Non dimenticare di modificare il nome di dominio da cloudforums.net al tuo dominio.

Basta eseguire

sh discourse_install.sh