Il y a 389 articles et 19 utilisateurs inscrits (ce bandeau est défini dans MediaWiki:Sitenotice)

WAF

De AJLSM
Aller à la navigation Aller à la recherche

Voir aussi WAF 2026

[modifier | modifier le wikicode]
Important :
En partie obsolète
  1. Voir : http://mw.ajlsm.com/wiki/WAF_2026.

Documentation externe

[modifier | modifier le wikicode]

https://www.zenetys.com/comment-proteger-un-site-web-avec-haproxy-et-modsecurity-via-spoe/ WAF : Web Application Firewall.

Description de l'infrastructure

[modifier | modifier le wikicode]

Schéma de l'infra

[modifier | modifier le wikicode]

Voir le schéma : Media:Haproxy-spoa.png

Dans notre infra (aujourd'hui Saturne) et sur Proxmox, on a installé 4 VM.

2 VM Haproxy (en cluster) récupèrent le flux et le renvoi vers 2 VM modsecurity (en cluster) qui analysent et traitent les requêtes et renvoient vers les VM HAPROXY qui renvoient, ou non, vers les VM clients.

  • Mise en place de deux nœuds Haproxy redondés via VRRP
  1. IP 1 : 10.0.0.30,
  2. IP 2 : 10.0.0.31,
  3. IP redondée/balancée : 10.0.0.32, IP externe redondée : 51.83.117.120)
  • Mise en place de deux nœuds d'analyse de requête (modsec SPOA) :
  1. IP 1 : 10.0.0.33 (Docker)
  2. IP 2 : 10.0.0.34 (Docker)

Pour savoir si les Docker tournent, lancer la commande :

$ sudo docker ps
Important :
Pré-requis pour les étapes suivantes :
  1. Accès par VPN Wireguard, ou,
  2. Accès par OpenVPN,
  3. Utilisateur root.

Ajouts des certificats SSL (manuelle)

[modifier | modifier le wikicode]

Rajouter un domaine (un certificat SSL) dans le reverse proxy HAPROXY

[modifier | modifier le wikicode]

1 -> Copier le certificat complet (voir où se trouve les certificats dans la conf apache du domaine ...-le-ssl.conf, puis réunir la clé publique et la clé privée en 1 fichier.

Depuis la VM (voir celui de portdunkerque pour exemple) vers les 2 VM HAPROXY, dans /etc/haproxy/certs/ en prenant soin de respecter le nom de domaine, par exemple :

port-dunkerque.pleade.com.pem

Une fois les certificats SSL en place, relancez le service haproxy :

service haproxy reload
Notice :
Contrairement à la recommandation qui suit, l'intégration du contenu des fichiers .crt, .key et .pem fonctionne très bien : Attention, ne pas ajouter le contenu du fichier .pem s'il existe !.

Certificats automatiques (SSL Lets Encrypt) via Haproxy

[modifier | modifier le wikicode]
Important :
Utiliser le projet Ansible : WAF#Ansible.
Important :
Le domaine doit être géré par Haproxy. Il doit être "derrière" le pare-feu, c'est-à-dire : Firewall: NAT: One-to-One : IP interne 10.0.0.32. Ne fonctionne pas avec l'IP native du serveur..

Modification du fichier haproxy.yml d'Ansible

[modifier | modifier le wikicode]
  1. Le chemin des certificats change de /etc/haproxy/ssl à /etc/haproxy/certs
  2. Ajout de la clé ACCOUNT_THUMBPRINT qui gère les challenge acme-challenge

Ajout de certificat sur un nouveau domaine

[modifier | modifier le wikicode]

L'exemple ici porte sur port-dunkerque.pleade.com

Important :
A exécuter séquentiellement (un après l'autre, pas en parallèle) sur HAPROXY-01 et HAPROXY-02.

S'authentifier en tant qu'acme

[modifier | modifier le wikicode]
$ sudo -u acme -s                       /!\ IMPORTANT d'être authentifié en tant qu'acme

Génération des certificats via LetsEncrypt

[modifier | modifier le wikicode]
$ acme.sh --issue -d port-dunkerque.pleade.com --stateless --server letsencrypt

Déploiement dans le dossier Haproxy grâce au hook correspondant qui met à jour à chaud les certs

[modifier | modifier le wikicode]
$ DEPLOY_HAPROXY_HOT_UPDATE=yes DEPLOY_HAPROXY_STATS_SOCKET=/var/run/haproxy/admin.sock DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs  acme.sh --deploy -d port-dunkerque.pleade.com --deploy-hook haproxy
Notice :
Si, en tant qu'acme, on fait un crontab -l, on peut voir qu'une tâche de renouvellement de certificats tourne régulièrement..

Configuration de HaProxy - Méthode 1

[modifier | modifier le wikicode]

On utilise ansible pour générer les fichiers de configuration dans Haproxy, les appliquer, redémarrer les Haproxy, etc.

Voir la section : Ansible

La configuration du fichier est la même, avec la possibilité de

  1. variabiliser : voir section : http://mw.ajlsm.com/wiki/WAF#Variabilisation
  2. déployer la configuration sur les 2 Haproxy en une seule action.

Pour utiliser cette méthode, récupérer le projet ansible-ajlsm-haproxy en SVN.

La création des règles est la même que celle décrite en méthode 2.

La différence est que l'on ne travaille pas "directement" dans le fichier /etc/haproxy/haproxy.cfg, mais dans le fichier /ansible-ajlsm-haproxy/inventory/group_vars/ajlsm_loadbalancer/haproxy.yml qui est déployé en ligne de commande avec Ansible.

Cette méthode est celle utilisée en production.

Configuration de HaProxy - Méthode 2

[modifier | modifier le wikicode]

Voir aussi projet ansible en SVN : WAF#Ansible.

Modifier directement le fichier de configuration configuration /etc/haproxy/haproxy.cfg.

Rajouter les lignes suivantes dans la configuration /etc/haproxy/haproxy.cfg (dans les blocs frontend front_web et backend ) :

acl host_leclientvoulu hdr(host) -i lenomdedomaineduclientvoulu.com
use_backend back_leclientvoulu if host_leclientvoulu

et rajouter un nouveau bloc backend comme ceci :

  1. Configuration avec certificat SSL :
backend back_leclientvoulu
   server LECLIENTVOULU ipduclientvoulu:443 check ssl verify none
  1. Configuration sans certificat :
backend back_leclientvoulu
   server LECLIENTVOULU ipduclientvoulu:80 check

Par exemple, pour archives.lamayenne.fr :

        acl host_ssl_archiveslamayenne  hdr(host)               -i          archives.lamayenne.fr
        use_backend back_ssl_archiveslamayenne                  if          host_ssl_archiveslamayenne

        acl host_salledelecturearchiveslamayenne  hdr(host)       -i      salle-de-lecture-archives.lamayenne.fr
        use_backend back_salledelecturearchiveslamayenne          if      host_salledelecturearchiveslamayenne

...

backend back_ssl_archiveslamayenne
        server ARCHIVESLAMAYENNE                ad53.ajlsm.lan:443 check ssl verify none

backend back_salledelecturearchiveslamayenne
        server SALLEDELECTUREARCHIVESLAMAYENNE  ad53.ajlsm.lan:80 check

Traitement des blocages par IP

[modifier | modifier le wikicode]

La règle apache pour le blocage des IP de chaque site :

RewriteCond %{REMOTE_ADDR}

renvoie sur le Haproxy.

Le blocage d'IP ne se fait plus.

Lorsque toutes les IP doivent être bloquées sauf les accès prévus : pour le client, AJLSM, etc, (Par exemple, pour une application intranet) la gestion des accès par IP est à configurer dans Haproxy.

De plus, les configurations apache doivent être modifiées sur chaque VM.

Blocage des IP dans Haproxy

[modifier | modifier le wikicode]

Créez les IP allowed dans la configuration Haproxy en ajoutant, dans le frontend front_web et avant les autres acl, l'instruction suivante :

        acl allowed_ajlsm src xx.xx.xx.xx xx.xx.xx.xx xx.xx.xx.xx/xx
        acl allowed_xx_client src xx.xx.xx.xx

(Par exemple, pour les IP AJLSM et le client ad53)

        acl allowed_ajlsm src 82.65.94.250 109.190.129.96 91.174.149.42 10.0.0.0/24
        acl allowed_ad53_client src 85.115.60.201

Association des règles aux acl du client

[modifier | modifier le wikicode]

Enfin, modifier les acl des domaines qui utilisent ces règles de blocage d'IP en ajoutant l'instruction suivante :

        http-request deny                                         if      host_[le-host] !allowed_ajlsm !allowed_xx_client

Par exemple :

        acl host_salledelecturearchiveslamayenne  hdr(host)       -i      salle-de-lecture-archives.lamayenne.fr
        use_backend back_salledelecturearchiveslamayenne          if      host_salledelecturearchiveslamayenne
        http-request deny                                         if      host_salledelecturearchiveslamayenne !allowed_ajlsm !allowed_ad53_client

Forcer le HTTPS sur un domaine

[modifier | modifier le wikicode]

On souhaite que le WAF prenne bien en compte l'acl pour un domaine avec SSL.

Ajouter la commande suivante dans l'acl concerné.

http-request redirect scheme https                      if          { hdr(Host) -i [le-domaine] } !{ ssl_fc }

Par exemple, pour ad53 :

        acl host_ssl_archiveslamayenne  hdr(host)               -i          archives.lamayenne.fr
        use_backend back_ssl_archiveslamayenne                  if          host_ssl_archiveslamayenne
        http-request redirect scheme https                      if          { hdr(Host) -i archives.lamayenne.fr } !{ ssl_fc }

Vérifier la configuration haproxy avec la commande :

haproxy -f /etc/haproxy/haproxy.cfg -c -V

Modification de la configuration apache des domaines

[modifier | modifier le wikicode]

Dans la configuration apache de chaque site, il faut ajouter les IP privées des 2 Haproxy.

        RewriteCond %{REMOTE_ADDR} !10.0.0.30
        RewriteCond %{REMOTE_ADDR} !10.0.0.31

Puis, commenter la ligne :

#RewriteRule .* /403.html [R=403,L]

Modification des logs pour le suivi des utilisateurs

[modifier | modifier le wikicode]

Lorsque l'on bascule le flux vers le WAF, c'est l'IP de Haproxy qui est la seule à communiquer avec la VM.

Pour avoir des statistiques du nombre d'utilisateurs, il faut modifier les configuration apache et ajoutant les instructions suivantes (conserver la configuration hors WAF en cas de suppression du WAF) :

Activer le module Apache remoteip :

a2enmod remoteip

Modifier la configuration Apache du site :

       # Configuration des logs pour suivi par IP utilisateurs
        RemoteIPHeader X-Forwarded-For
        LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" forwarded

        ErrorLog /var/log/apache2/archives.lamayenne.fr.errors.log
        CustomLog /var/log/apache2/archives.lamayenne.fr.log forwarded
        # Fin configuration des logs pour suivi par IP utilisateurs


        #### Configuration log hors WAF 
        #ErrorLog /var/log/apache2/archives.lamayenne.fr.errors.log
        #CustomLog /var/log/apache2/archives.lamayenne.fr.log combined
        #### Fin configuration log hors WAF

Application des règles HaProxy

[modifier | modifier le wikicode]

Une fois les modifications terminées, on applique la configuration en relançant le service.

Redémarrer HAPROXY

[modifier | modifier le wikicode]
service haproxy restart

Vérifier que le HAPROXY est en vie

[modifier | modifier le wikicode]
service haproxy status

Logrotate Haproxy

[modifier | modifier le wikicode]

Le logrotate est configuré et fait une rotation journalière (configuration dans /etc/logrotate.d/haproxy), Il y a un palier maximum de 500M par fichier de log (on peut adapter).

Le log est compressé (en .1, .2.gz ...) soit lorsqu'il atteint 500M, soit au terme de la journée. Les plus anciens logs compressés sont supprimés graduellement et automatiquement.

La conf est la suivante :

/var/log/haproxy.log {
   daily
   rotate 15
   maxsize 500M

Problèmes et solutions

[modifier | modifier le wikicode]

En cas d'erreur de configuration, on peut sortir un état plus détaillé du service haproxy avec la commande :

$ sudo systemctl status haproxy.service -l --no-pager
● haproxy.service - HAProxy Load Balancer
     Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2022-05-19 10:36:36 UTC; 9s ago
       Docs: man:haproxy(1)
             file:/usr/share/doc/haproxy/configuration.txt.gz
    Process: 88895 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS (code=exited, status=0/SUCCESS)
   Main PID: 88906 (haproxy)
      Tasks: 5 (limit: 9327)
     Memory: 9.4M
     CGroup: /system.slice/haproxy.service
             ├─88906 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock
             └─88916 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock

May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53goaccess started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53goaccess started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_salledelecturearchiveslamayenne started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_salledelecturearchiveslamayenne started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53internetpublicationajlsm started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53internetpublicationajlsm started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53preprodajlsm started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53preprodajlsm started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53slecturepublicationajlsm started.
May 19 10:36:36 haproxy-01 haproxy[88906]: Proxy back_ad53slecturepublicationajlsm started.

Tester un domaine en passant par le reverse proxy HAPROXY

[modifier | modifier le wikicode]

(sans avoir à faire une migration PROD / sans impact CLIENT).

Les tests sont lancés depuis votre poste de travail en suivant les instructions ci-dessous.

Exemple : Tests sur la VM Port Dunkerque

Rajouter cette ligne dans le fichier hosts du poste de travail d'où seront lancés les tests :

51.83.117.120 port-dunkerque.pleade.com

Remarques : Pour info, cette IP 51.83.117.120 pointe sur l'IP redondée du HAPROXY soit 10.0.0.32..

Il faut bien tester pour détecter d'éventuels faux positifs (logs : /var/log/haproxy.log)

Réplication de la configuration dans les 2 Haproxy

[modifier | modifier le wikicode]

Une fois la configuration /etc/haproxy/haproxy.cfg testée et validée dans le Haproxy01, répliquez cette configuration dans Haproxy02.

Réplication avec ansile

[modifier | modifier le wikicode]
Important :
Source V1 pour l'ansibelisation du déploiement Haproxy dans le SVN sous le nom : ansible-ajlsm/trunk/ansible-ajlsm-haproxy. Après chaque modification, pensez à commiter !!!.


Notice :
Ne pas oublier de changer les mots de passes dans inventory/ajlsm_loadbalancer.inventory (à terme il faudra utiliser des clés publiques ainsi qu'Ansible-Vault).

Installation de Ansible

[modifier | modifier le wikicode]

Il faut installer Ansible au préalable ( https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#installing-ansible-on-ubuntu )

Paramétrage de Ansible

[modifier | modifier le wikicode]
  1. Ajouter la variable d'environnement.
  2. Modifier les clés dans le fichier ansible-ajlsm-haproxy/inventory/ajlsm_loadbalancer.inventory

Variabilisation

[modifier | modifier le wikicode]

Par exemple, les IP pour le traitement des accès par IP et blocage, on variabilise la configuration dans Haproxy en ajoutant, Dans le fichier /ansible-ajlsm-haproxy/inventory/group_vars/ajlsm_loadbalancer/haproxy.yml et juste avant frontend front_web :

---
haproxy_allowed_ips_ajlsm: " xx.xx.xx.xx xx.xx.xx.xx xx.xx.xx.xx/xx"
haproxy_allowed_ips_[le-client]_client: "xx.xx.xx.xx"
haproxy_custom_configuration: |

Puis en ajoutant, dans le frontend front_web et avant les autres acl, l'instruction suivante :

        acl allowed_ajlsm src {{ haproxy_allowed_ips_ajlsm }}
        acl allowed_[le-client]_client src {{ haproxy_allowed_ips_[le-client]_client }}

Par exemple, pour AJLSM et ad53 :

---
haproxy_allowed_ips_ajlsm: "82.65.94.250 109.190.129.96 91.174.149.42 10.0.0.0/24"
haproxy_allowed_ips_ad53_client: "85.115.60.201"
haproxy_custom_configuration: |

...

        acl allowed_ajlsm src {{ haproxy_allowed_ips_ajlsm }}
        acl allowed_ad53_client src {{ haproxy_allowed_ips_ad53_client }}
  1. Il faut être sous VPN.
  2. Dans /etc/host, ajouter les 2 hosts : 10.0.0.30 haproxy01.ajlsm.lan, et : 10.0.0.31 haproxy02.ajlsm.lan
  3. Après avoir testé la configuration dans Haproxy01, créer le fichier Haproxy.yml (copie du fichier Haproxy01 en retirant les sections "global" et "default") et ajoutant les variables, si nécessaire.
  4. Le fichier Haproxy.yml doit être placé dans le répertoire /ansible-ajlsm-haproxy/inventory/group_vars/ajlsm_loadbalancer/haproxy.yml
  5. Lancer la commande depuis la racine du dossier décompressé (il faut avoir ansible.cfg en visu lorqu'on fait un ls).
ansible-playbook ./plays/deploy.haproxy.yml -l ajlsm_loadbalancer
Notice :
Il faut un environnement python. Penser à lancer
source .venv/bin/activate
si vous travaillez dans un environnement spécifique.
source .venv/bin/activate && ansible-playbook ./plays/deploy.haproxy.yml -l ajlsm_loadbalancer

Si tout va bien :

ansible-ajlsm-haproxy ~$ ansible-playbook ./plays/deploy.haproxy.yml -l ajlsm_loadbalancer

PLAY [ajlsm_loadbalancer] ****************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [haproxy01.ajlsm.lan]
ok: [haproxy02.ajlsm.lan]

TASK [haproxy : Install packages] ********************************************************************************************************************************************************************************************************
ok: [haproxy02.ajlsm.lan]
ok: [haproxy01.ajlsm.lan]

TASK [haproxy : Deploy configuration and validate it] ************************************************************************************************************************************************************************************
changed: [haproxy02.ajlsm.lan]
changed: [haproxy01.ajlsm.lan]

TASK [haproxy : Restore configuration (validation failed)] *******************************************************************************************************************************************************************************
skipping: [haproxy01.ajlsm.lan]
skipping: [haproxy02.ajlsm.lan]

TASK [haproxy : Remove restored backup file] *********************************************************************************************************************************************************************************************
skipping: [haproxy01.ajlsm.lan]
skipping: [haproxy02.ajlsm.lan]

TASK [haproxy : Start service and enable it on boot] *************************************************************************************************************************************************************************************
ok: [haproxy01.ajlsm.lan]
ok: [haproxy02.ajlsm.lan]

TASK [haproxy : Failure] *****************************************************************************************************************************************************************************************************************
skipping: [haproxy01.ajlsm.lan]
skipping: [haproxy02.ajlsm.lan]

RUNNING HANDLER [haproxy : restart haproxy] **********************************************************************************************************************************************************************************************
changed: [haproxy02.ajlsm.lan]
changed: [haproxy01.ajlsm.lan]

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
haproxy01.ajlsm.lan        : ok=5    changed=2    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0   
haproxy02.ajlsm.lan        : ok=5    changed=2    unreachable=0    failed=0    skipped=3    rescued=0    ignored=0

En cas d'erreur après le lancement de la commande

[modifier | modifier le wikicode]
Essayer la commande
[modifier | modifier le wikicode]
ANSIBLE_CONFIG=ansible.cfg ansible-playbook ./plays/deploy.haproxy.yml -l ajlsm_loadbalancer

Cette partie est à compléter selon l'évolution du script de déploiement.

1 - Si la commande renvoie :
[modifier | modifier le wikicode]
-bash: ansible-playbook: command not found

Voir tuto : https://stackoverflow.com/questions/63177609/zsh-command-not-found-ansible-after-pip-installing

2 - Si la commande renvoie :
[modifier | modifier le wikicode]
ansible-playbook ./plays/deploy.haproxy.yml -l ajlsm_loadbalancer

PLAY [ajlsm_loadbalancer] *****************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ********************************************************************************************************************************************************************************************************************************
fatal: [haproxy01.ajlsm.lan]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname haproxy01.ajlsm.lan: nodename nor servname provided, or not known", "unreachable": true}
fatal: [haproxy02.ajlsm.lan]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: Could not resolve hostname haproxy02.ajlsm.lan: nodename nor servname provided, or not known", "unreachable": true}

PLAY RECAP ********************************************************************************************************************************************************************************************************************************************
haproxy01.ajlsm.lan        : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   
haproxy02.ajlsm.lan        : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0

Dans /etc/host, ajouter les 2 hosts :

  1. 10.0.0.30 haproxy01.ajlsm.lan
  2. 10.0.0.31 haproxy02.ajlsm.lan

Reste à faire

[modifier | modifier le wikicode]

Fine-tuning pour une maintenance simplifiée + le vault&clé publique pour la sécurité des mots de passes

  1. Il faudrait vaulter des choses,
  2. améliorer le playbook & le rôle
  3. ainsi que variabiliser plus de choses;

Migrer un site vers HAPROXY (mise en production)

[modifier | modifier le wikicode]

(après avoir testé)

  • Soit changer le DNS vers 51.83.117.120 (le client doit faire pointer son DNS sur cette IP)
  • Soit aller dans les NAT One-to-One (1:1) d'OPNSENSE, et changer l'IP du client vers 10.0.0.32 (sur la correspondance IP externe client <-> IP interne client de la page)

Lors d'une mise en production, pour éviter faire changer aux clients le DNS tout de suite et pour garder un débrayage facile : depuis le pare-feu OPNSense -> firewall -> NAT -> one-to-one, on fait pointer l'IP initiale de la VM vers 10.0.0.32.

Dans l'exemple pour PORT DUNKERQUE, ça voudrait dire faire pointer 46.105.188.170 sur 10.0.0.32 au lieu de 10.0.0.24.

Voir la copie d'écran de OPNSense : media:Nat.png

Arrêter le WAF

[modifier | modifier le wikicode]

En cas de problème avec une VM client (un blocage, ralentissement, faux positifs, etc.)

  • Modifier les configuration apache en décommentant
    #RewriteRule .* /403.html [R,L]
    

et en remettant les règles pour les logs (commenter la section et décommenter l'autre) :

       # Configuration des logs pour suivi par IP utilisateurs
        #RemoteIPHeader X-Forwarded-For
        #LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" forwarded

        #ErrorLog /var/log/apache2/chercher-archives.lamayenne.fr.errors.log
        #CustomLog /var/log/apache2/chercher-archives.lamayenne.fr.log forwarded
        # Fin configuration des logs pour suivi par IP utilisateurs


        #### Configuration log hors WAF 
        ErrorLog /var/log/apache2/archives.lamayenne.fr.errors.log
        CustomLog /var/log/apache2/archives.lamayenne.fr.log combined
        #### Fin configuration log hors WAF
  • Remettre l'IP interne du client (et pas celle du HAPROXY) dans le NAT 1:1 d'OPNSENSE.
  • Ou bien, éteindre les deux machines VM modsec-spoe (Proxmox Saturne) pour désactiver le WAF de force.

On parle de "faux positif " lorsque un paquet anodin (ne constituant aucune menace réelle) est détecté comme tentative d'intrusion.

On parle de "faux négatif" lorsqu'une tentative d'intrusion n'est pas détectée.

Identifier la cause du blocage Modsec

[modifier | modifier le wikicode]

Voir dans les logs du container Docker sur chacune des VM Modsec via la commande suivante :

$ docker exec -it modsec tail -n 100 -f /var/log/modsec_audit.log

Exemple d'erreurs

[modifier | modifier le wikicode]

Log de Modsec

L'erreur suivante apparait :

Message Multipart parssins error: Multipart: Final boundary missing.

Souvent, la cause inhérente est un tronquage de la requête à cause d'un maximum en bytes trop petit.

Pour tenter de corriger l'erreur ci-dessus :

  1. Agrandir dans /opt/modsecurity/modsecurity.conf les champs "SecRequestBodyLimit" et "SecRequestBodyNoFilesLimit". Sans effet et annulation du changement.
  2. Remplacer "SecRequestBodyLimitAction Reject" par "SecRequestBodyLimitAction ProcessPartial" ce qui désactive la limite de taille d'une requête en bytes, mais continue à la logguer pour analyse côté MODSEC et la refuse si attaque détectée.


Important :
Cela n'est pas conseillé, mais cela permet d'éviter de potentiels faux positifs que ce soit côté upload de fichier, et/ou requêtes grosses..

Le problème rencontré est corrigé par l'ajout de nouvelles règles dans Modsec pour Drupal en infra.

Modifier les règles OWASP (en cas de faux positifs)

[modifier | modifier le wikicode]

Lors de la mise en place de modsec, un ensemble de règles sont téléchargées.

Toutes ne sont pas activés.

Pour modifier et activer les règles, changez d'utilisateur :

$ sudo su

Activation de règles pour Drupal

[modifier | modifier le wikicode]

Lors de la mise en place de modsec, les règles destinées à Drupal ont bien été chargées mais elles n'étaient pas activées.

Cela a généré des faux positifs dans Drupal : 403 en cas d'accès à certaines pages, comme "enregistrer un contenu", dont le motif d'URL est pris pour une tentative d'attaque par modsec.

Pour activer les règles Drupal, changez d'utilisateur : sudo -su, puis modifiez /opt/modsecurity/owasp-modsecurity-crs/crs-setup.conf (sur les 2 VMs MODSEC) en ajoutant de ce paragraphe =>

SecAction \
"id:900130,\
 phase:1,\
 nolog,\
 pass,\
 t:none,\
 setvar:tx.crs_exclusions_drupal=1"

Ajout de nouvelles règles pour Drupal

[modifier | modifier le wikicode]

Après un fonctionnement "normal" du WAF et des règles ci-dessus, certains faux positifs sont réapparus, notamment lors de l'enregistrement de contenus. Le motif d'URL qui est bloquée par le WAF est de type :

[DOMAINE]/node/[ID-DU-NOEUD]/edit?destination=/admin/content

Pour corriger, modifier /opt/modsecurity/modsecurity.conf comme suit :

SecRule REQUEST_FILENAME "@endsWith /node/add/article" \
    "id:123456,\
    phase:1,\
    nolog,\
    ctl:requestBodyAccess=Off"

SecRule REQUEST_FILENAME "@endsWith /node/add/page" \
    "id:123457,\
    phase:1,\
    nolog,\
    ctl:requestBodyAccess=Off"

SecRule REQUEST_FILENAME "@rx /node/[0-9]+/edit.*$" \
    "id:123458,\
    phase:1,\
    nolog,\
    ctl:requestBodyAccess=Off"

Ajout de règles pour Pleade

[modifier | modifier le wikicode]

Éditez le fichier /opt/modsecurity/modsecurity.conf.

nano /opt/modsecurity/modsecurity.conf

Ajoutez la règle :

SecRule REQUEST_FILENAME "@rx /ead-admin/.*\.html.*$" \
    "id:123460,\
    phase:1,\
    nolog,\
    ctl:requestBodyAccess=Off"

Activer et relancer le service ModSec

[modifier | modifier le wikicode]
$ sudo -su                                  # Changez d'utilisateur #
$ nano /opt/modsecurity/modsecurity.conf    # Modifiez le fichier de configuration #
$ docker ps                                 # Vérifiez si Docker tourne #
$ docker stop modsec && docker rm modsec    # Arrêt et suppression de la conf #
$ cd /home/modsec/                          # Allez dans l'utilisateur modsec #
$ ./launch_modsec_spoa.sh                   # Relance de Modsec #
Important :
Faire la même opération sur les 2 modsec.

Espace disque saturé

[modifier | modifier le wikicode]

Le container Docker de modsec écrit beaucoup de logs.

Le dossier overlay2 (dans /var/lib) de Docker peut se remplir rapidement.

Pour corriger les problèmes rencontrés, vérifier le logrotate modSec -> /etc/docker/daemon.json ou vider les logs selon la procédure ci-dessous :

Nettoyage overlay2

[modifier | modifier le wikicode]
$ docker stop modsec
$ docker system prune
$ cd /home/modsec && ./launch_modsec_spoa.sh
$ docker ps (pour vérifier que c'est allumé)