كيفية تثبيت Discourse مع جدار حماية مفتوح المصدر (WAF)

لا يُسمح لي بالنشر في قسم “كيفية القيام بذلك”، لذا سأقوم بنشر هذا هنا :slight_smile:

نظرة عامة

الهدف هو إضافة جدار حماية لتطبيقات الويب (WAF) إلى الموارد الحالية مع الحفاظ على الأداء في تثبيت صغير. في هذا المثال، سنستخدم مثيل EC2 واحدًا. نظرًا لأن Discourse يعمل داخل حاويات Docker، فإننا نستطيع تثبيت NGINX على المضيف واستخدام توجيه البروكسي (proxy_pass) لتوجيه حركة المرور إلى حاوية Docker الخاصة بنا. كما أننا نستخدم RDS لقاعدة بيانات PostgreSQL.

تكوين Discourse

خطوتنا الأولى هي تكوين ملف app.yml الخاص بنا لتثبيت مخصص لـ Discourse. في هذا المثال، سنقوم بتشغيل أدوار الويب و Redis. لن نستخدم دور قاعدة البيانات (DB) أو أدوار SSL.

كما نريد التأكد من عدم تعريض منافذ الويب داخل حاوية Docker. والسبب في ذلك هو أننا سنقوم بعرض الويب عبر بروكسي NGINX على المضيف.

مثال على ملف app.yml

## هذا هو قالب حاوية Docker لـ Discourse الكل في واحد ومستقل
##
## بعد إجراء تغييرات على هذا الملف، يجب عليك إعادة البناء
## /var/discourse/launcher rebuild app
##
## كن حذرًا جدًا عند التعديل!
## ملفات YAML حساسة للغاية للأخطاء في المسافات البيضاء أو المحاذاة!
## قم بزيارة http://www.yamllint.com/ للتحقق من صحة هذا الملف حسب الحاجة
templates:
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  - "templates/web.ratelimited.template.yml"
  - "templates/web.socketed.template.yml"
## قم بإلغاء التعليق عن هذين السطرين إذا كنت ترغب في إضافة Lets Encrypt (https)
  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"


## ما هي منافذ TCP/IP التي يجب أن تعرضها هذه الحاوية؟
## إذا كنت ترغب في مشاركة Discourse لمنفذ مع خادم ويب آخر مثل Apache أو nginx،
## راجع https://meta.discourse.org/t/17247 للحصول على التفاصيل
expose:
#  - "80:80"   # http
#  - "443:443" # https


params:
  db_default_text_search_config: "pg_catalog.english"
  ## قم بتعيين db_shared_buffers إلى الحد الأقصى 25% من إجمالي الذاكرة.
  ## سيتم تعيينها تلقائيًا بواسطة bootstrap بناءً على ذاكرة الوصول العشوائي المكتشفة، أو يمكنك تجاوزها
  db_shared_buffers: "768MB"
  ## يمكن أن يحسن أداء الفرز، لكنه يضيف استخدام الذاكرة لكل اتصال
  #db_work_mem: "40MB"
  ## أي إصدار Git يجب أن تستخدمه هذه الحاوية؟ (الافتراضي: tests-passed)
  #version: tests-passed

env:
  LANG: en_US.UTF-8
  # DISCOURSE_DEFAULT_LOCALE: en
  ## كم عدد طلبات الويب المتزامنة المدعومة؟ يعتمد على الذاكرة ونوى المعالج.
  ## سيتم تعيينها تلقائيًا بواسطة bootstrap بناءً على المعالجات المكتشفة، أو يمكنك تجاوزها
  UNICORN_WORKERS: 2

  ## TODO: اسم النطاق الذي ستستجيب له هذه المثيلة من Discourse
  ## مطلوب. لن يعمل Discourse مع عنوان IP مجرد.
  DISCOURSE_HOSTNAME: cloudforums.net

  ## قم بإلغاء التعليق إذا كنت تريد بدء الحاوية بنفس
  ## اسم المضيف (خيار -h) المحدد أعلاه (الافتراضي "$hostname-$config")
  #DOCKER_USE_HOSTNAME: true

  ## TODO: قائمة عناوين البريد الإلكتروني المفصولة بفواصل والتي سيتم تعيينها كمسؤول ومطور
  ## عند التسجيل الأولي، على سبيل المثال 'user1@example.com,user2@example.com'
  DISCOURSE_DEVELOPER_EMAILS: 'youremail@domain.com'

  ## TODO: خادم البريد SMTP المستخدم للتحقق من الحسابات الجديدة وإرسال الإشعارات
  ## عنوان SMTP واسم المستخدم وكلمة المرور مطلوبة
  ## تحذير: قد يتسبب حرف '#' في كلمة مرور SMTP في مشاكل!
  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           # (اختياري، الافتراضي true)

  ## إذا أضفت قالب Lets Encrypt، قم بإلغاء التعليق أدناه للحصول على شهادة SSL مجانية
  #LETSENCRYPT_ACCOUNT_EMAIL: me@example.com

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

  ## عنوان CDN http أو https لهذه المثيلة من Discourse (مُهيأ للسحب)
  ## راجع https://meta.discourse.org/t/14857 للحصول على التفاصيل
  #DISCOURSE_CDN_URL: https://discourse-cdn.example.com
## حاوية Docker غير حالة؛ يتم تخزين جميع البيانات في /shared
volumes:
  - volume:
      host: /var/discourse/shared/standalone
      guest: /shared
  - volume:
      host: /var/discourse/shared/standalone/log/var-log
      guest: /var/log

## تذهب الإضافات هنا
## راجع https://meta.discourse.org/t/19157 للحصول على التفاصيل
hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - git clone https://github.com/discourse/docker_manager.git

## أي أوامر مخصصة للتشغيل بعد البناء
run:
  - exec: echo "Beginning of custom commands"
  ## إذا كنت ترغب في تعيين عنوان البريد الإلكتروني 'من' لتسجيلك الأول، قم بإلغاء التعليق وتغيير:
  ## بعد الحصول على بريد التسجيل الأول، قم بإعادة التعليق على السطر. يجب تشغيله مرة واحدة فقط.
  - exec: rails c "SiteSetting.enable_badge_sql = true"
  - exec: echo "End of custom commands"

بدء تثبيت Discourse

استنساخ مستودع Discourse

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

نسخ app.yml المخصص للتثبيت

يمكنك تشغيل الأمر أدناه للقيام بذلك نيابةً عنك. فقط استبدل النص “PASTE YOUR FULL APP.YML HERE” بملف app.yml الكامل الخاص بك.

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

تشغيل إعداد Discourse

ابدأ إعداد Discourse دون مطالبات باستخدام الأمر أدناه.

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

يجب أن يستغرق تثبيت Discourse من 10 إلى 15 دقيقة.

تثبيت NGINX و WAF و GEOIP

قم بتشغيل التحديثات وتثبيت المتطلبات المسبقة

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

قم بتثبيت قاعدة بيانات MaxMind DB حتى تتمكن من تشغيل قواعد GEO IP بناءً على الموقع. يمكنك استخدام هذه القاعدة لتوجيه أو حظر حركة المرور بناءً على الموقع. ستظهر سجلات NGINX لدينا الآن بلد المستخدم حتى لو لم تقم بتفعيل الحظر الجغرافي.

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

تنزيل واستخراج NGINX و NAXSI WAF

ملاحظة: استبدل NGINX بأحدث إصدار

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/

استنساخ وحدة GEO IP2 لـ NGINX عبر Git

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

تجميع NGINX

سنقوم بإنشاء سكريبت للقيام بذلك نيابةً عنا وتشغيله أدناه


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

إنشاء قواعد الجدار الناري

سيؤدي الافتراضي أدناه إلى تفعيل WAF وحظر الطلبات الخبيثة. إذا كنت ترغب في تشغيل WAF في وضع التعلم، يمكنك فعل ذلك عن طريق إضافة Learning Mode إلى ملف القاعدة أدناه.

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



cat > /etc/nginx/naxsi.rules <<\EOF
SecRulesEnabled;
DeniedUrl "/RequestDenied";
## التحقق من قواعد Naxsi
CheckRule "$SQL >= 8" BLOCK;
CheckRule "$RFI >= 8" BLOCK;
CheckRule "$TRAVERSAL >= 4" BLOCK;
CheckRule "$EVADE >= 4" BLOCK;
CheckRule "$XSS >= 8" BLOCK;
EOF

إنشاء ملف تكوين NGINX

بشكل افتراضي في ملف التكوين هذا، يتم التعليق على الحظر الجغرافي، ولكن يمكنك إلغاء التعليق وتفعيله إذا كنت ترغب في ذلك.

ملاحظة: يجب تشغيل NGINX بدون SSL في البداية. لأننا نحتاج إلى أن يرى certbot نطاقًا نشطًا. سنحصل على SSL في خطوة لاحقة وسنقوم فعليًا بنسخ ملف NGINX جديد لاستبدال هذا الملف

ملاحظة: استبدل cloudforums.net باسم نطاقك

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

إنشاء سكريبت upstart لـ NGINX

نحتاج إلى إنشاء سكريبت لخدمة 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 
 # قم بتضمين قيم nginx الافتراضية إذا كانت متاحة 
 if [ -f /etc/nginx ] ; then 
         . /etc/nginx 
 fi
 
 set -e
 
 case "$1" in 
     start)
         echo -n "Starting $DESC: " 
         start-stop-daemon --start --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON -- $DAEMON_OPTS 
         echo "$NAME." 
         ;; 
     stop) 
         echo -n "Stopping $DESC: " 
         start-stop-daemon --stop --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     restart|force-reload) 
         echo -n "Restarting $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 "Reloading $DESC configuration: " 
         start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/nginx.pid \ 
             --exec $DAEMON 
         echo "$NAME." 
         ;; 
     *) 
         N=/etc/init.d/$NAME 
         echo "Usage: $N {start|stop|restart|force-reload}" >&2 
         exit 1 
         ;; 
 esac
 
 exit 0
EOF

systemctl daemon-reload

إنشاء ملف خدمة 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

تنزيل قاعدة بيانات Geo IP للدول

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/

تفعيل وبدء تشغيل NGINX

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

إضافة شهادة SSL لنطاقك

لا تنس استبدال cloudforums.net بنطاقك.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

إنشاء ملف NGINX جديد بقواعد SSL

قم بإنشاء ملف NGINX جديد بشهادة SSL التي قمت بتنزيلها للتو.

ملاحظة: غيّر cloudforums.net إلى نطاقك


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';
    
    #***********************************************************
    #قم بإلغاء التعليق للقيام بالحظر الجغرافي. الافتراضي هو الولايات المتحدة فقط في الإعدادات أدناه
    #***********************************************************
    #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;
       }
    }
    # HTTPS server
    #
    #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

إعادة تشغيل NGINX

systemctl restart nginx

الآن لديك جدار حماية مفتوح المصدر رائع!! :slight_smile:

إعلان غير خجول

تأكد من الانضمام إلينا في cloudforums.net إذا استمتعت بهذا الدليل. سنكون سعداء جدًا بوجودك معنا هنا. نحن لا نبيع أي شيء ولا نضع إعلانات. فقط نبحث عن منشورات تقنية جيدة :slight_smile:

تثبيت NGINX / WAF آلي بنقرة واحدة بعد تثبيت Discourse. لا تنسَ تغيير اسم النطاق من cloudforums.net إلى نطاقك.

فقط قم بتشغيل

sh discourse_install.sh