Tuto: Bloquer les requêtes selon le pays

Bonjour,

Mise à jour du tuto le 14/07/22

  1. Créer le hook suivant:

/etc/yunohost/hooks.d/conf_regen/18-nginx_geoip

#!/bin/bash

# if not present, add package geoip-database with apt install geoip-database

##############################################
#Beware, i'm not sure this command is universal. You must test it before. If not, you can find the result with the command ip route, this one must be like 192.168.0.0/24 or similar. Then replace the variable $network_address with it.
#Attention, je ne suis pas certain que cette commande est universelle. Vous devez la tester au préalable. Si ce n'est pas le cas, vous pouvez trouver la réponse avec la commande ip route, celle-ci doit être de la forme 192.168.0.0/24 ou similaire. Remplacez alors la variable $network_address par celle-ci.
network_address=$(awk '{print $1}' <(grep src <(ip route)))
################################################
action=$1
pending_dir=$4
nginx_dir=$pending_dir/../nginx/etc/nginx
nginx_security_conf=$nginx_dir/conf.d/security.conf.inc
nginx_country_conf=$nginx_dir/conf.d/country.conf

[[ $action == "pre" ]] || exit 0
[[ -d $nginx_dir ]] || exit 0
[[ -e $nginx_security_conf ]] || exit 0

#Now we create the Nginx country conf file 
echo "# GeoIP databases
geoip_country /usr/share/GeoIP/GeoIP.dat;

map \$geoip_country_code \$allowed_country {
  default no;
  # France
  FR yes;
  # Italie
  #IT yes;
}

geo \$lan-ip {
  default no;
  $network_address yes;
  127.0.0.1 yes;
}" > $nginx_country_conf

#Next ,we modify Nginx security.conf.inc file
echo '

# allow local ip
if ($lan-ip = yes) {
  set $allowed_country yes;
}
# block the country
if ($allowed_country = no) {
  return 444;
}' >> $nginx_security_conf

#And then, we create the cron job.
echo "#!/bin/bash

maj_geoip () {
    cd /usr/share/GeoIP
    if grep 'inet6 [23]' <(ip -6 addr);then #test if the server can be join with ipv4+ipv6 or only ipv4
        wget https://dl.miyuru.lk/geoip/maxmind/country/maxmind.dat.gz -O GeoIP.dat.gz #Download IPv4+IPv6 database

    else
        wget https://mailfud.org/geoip-legacy/GeoIP.dat.gz #Download IPv4 database
    fi 
    gunzip -f GeoIP.dat.gz #unzip database
}
last=/usr/local/etc/last #it is a file to write the last date update. It is use to compare if there is a new update when the cron job is execute. If not, there's nothing to do.
if grep 'inet6 [23]' <(ip -6 addr);then  
      update=\$(sed -n 's,.*<li>Maxmind : \(.*\)</li>,\1,p' <(curl -s https://www.miyuru.lk/geoiplegacy | grep 'Maxmind : ')) #Retrieve the last update of database ipv4+ipv6 date on miuru.lk
else
      update=\$(sed -n 's,.*<b>\(.*\)</b>,\1,p' <(curl -s https://mailfud.org/geoip-legacy/ | grep 'Latest update')) #retrieve the last update date on mailfud.org
fi
[[ \"\$(<\$last)\" != \"\$update\" ]] && (echo \"\$update\" > \"\$last\";maj_geoip) #Test if there is an update. If yes, write the date in /usr/local/etc/last and execute the function maj_geoip.
exit 0" > /etc/cron.weekly/Geoipupdate
chmod 500 /etc/cron.weekly/Geoipupdate # give execution right to the cron job.
##############################################################

Regénérer la configuration de Nginx dans Yunohost:

yunohost tools regen-conf nginx --force

Voilà, c’est fini.
Pour autoriser un pays, il suffit de l’ajouter dans le hook. Pour en supprimer un, soit passer la valeur yes à no, soit commenter la ligne avec un dièse. Ne pas oublier de régénérer la conf nginx de Yunohost avec regen-conf pour que ce soit pris en compte.


Important: Ce qui est ci-dessous est partiellement obsolète, je l’ai laissé pour info et pour ne pas “casser” e fil de discussion. Il faut désormais suivre ce qui est ci-dessus.

Selon l’usage que vous faîtes de Yunohost, il n’est pas forcément nécessaire de laisser un accès de n’importe où. Par exemple pour un usage personnel de Nextcloud, l’accès est suffisant uniquement de France par exemple et parfois de l’étranger quand on se déplace. Dans ce cas il est possible d’interdire l’accès de l’étranger avec les listes Ip de géolocalisation disponible ici et en cas de déplacement à l’étranger, activer un accès à partir du pays concerné rapidement avec son code iso disponible sur cette page.

Il suffit d’utiliser le script suivant à placer dans /etc/cron.monthly pour une vérification une fois par mois et de le rendre exécutable avec sudo chmod +x /etc/cron.monthly :

#!/bin/bash

maj_geoip () {
    cd /usr/share/GeoIP
    wget https://dl.miyuru.lk/geoip/maxmind/country/maxmind.dat.gz
    gunzip maxmind.dat.gz
    mv maxmind.dat GeoIP.dat
}

month=/usr/local/etc/maxmind
update=$(curl -s https://www.miyuru.lk/geoiplegacy | grep -oP "(?<=Maxmind : ).*(?=</li>)")
[[ "$(<$month)" != "$update" ]] && (echo "$update" > "$month";maj_geoip)
exit 0

Ensuite créer le fichier /etc/nginx/country:

map $geoip_country_code $allowed_country {
   default no;
   # France
   FR yes;
   # Italie
   #IT yes;
   #Etc........
   }

Ajoutez les pays de votre choix, il suffit de dé-commenter le code iso 3166 des pays pour lesquelles on souhaite un accès comme ici dans l’exemple avec la France .

Ajouter à nginx.conf au début du bloc http

# GeoIP databases
    geoip_country /usr/share/GeoIP/GeoIP.dat;

ainsi que la ligne suivante en dessous de include /etc/nginx/sites-enabled/*; :

include /etc/nginx/country;

Ajouter au début de /etc/nginx/conf.d/yunohost_admin.conf en adaptant l’adresse de réseau local:

geo $lan-ip {
default no;
192.168.0.0/24 yes;
} 

Puis ajouter à la fin du fichier /etc/nginx/conf.d/security.conf.inc:

if ($lan-ip = yes) {
set $allowed_country yes;
}
    # block the country
    if ($allowed_country = no) {
        return 444;
    }

J’ai eu l’occasion de tester de l’étranger, l’accès est bien bloqué. Il a suffit de dé-commenter la ligne du pays concerné dans le fichier /etc/nginx/country pour avoir à nouveau accès au serveur à partir d’un navigateur.

8 Likes

Bonjour et merci pour ce tutoriel explicatif.

De mon côté, j’utilise une technique radicalement différente mais au résultat similaire : mon serveur Yunohost est placé dans une DMZ derrière un bastion IPfire sur lequel le blocage GeoIP est actif.
Cela permet d’avoir en sus une détection des évènements anormaux via le NIDS Suricata (que pour ma part je configure en mode NIPS) sur une plage de ports plus étendue, de mesurer/surveiller les flux, …

Sangokuss :slight_smile:

1 Like

Je ne connais pas les outils que tu utilises, merci pour la découverte.
Par contre, si je comprends bien, Ipfire tourne sur un autre serveur en amont de Yunohost? Est-ce bien ça?

Oui, c’est bien un second serveur qui se place en coupure (comme on dit) et qui par conséquent filtre l’ensemble des flux.

1 Like

Bonjour @metyun ,
pourquoi se limiter à nginx?
Si j’ai bien compris (attention je suis un noob total en administration serveur! :sweat_smile: ), ce tuto permet de filtrer par géolocalisation IP toutes les tentatives de connexion via le serveur web.
Or, si un attaquant essaie de se connecter en ssh sur mon serveur, il ne sera pas pris dans les mailles de ce filtre… J’ai bon?

Ne serait-il pas préférable de filtrer au niveau de fail2ban directement? Un peu comme dans cet article: https://www.webfoobar.com/node/54
Il me semble que l’approche via fail2ban englobe absolument tous les types de connexion, et pas seulement web.

Merci de me corriger si besoin!

Bonjour @polochon ,

Tout simplement parce que lors de mes recherches je n’étais pas tombé sur ton lien et que je ne savais même pas que fail2ban pouvais le faire.

J’ai recueilli des infos ici et là, et je voulais simplement partager les scripts que j’ai mis en place et qui sont fonctionnels. On voit bien les réponses 444 dans les logs de Nginx pour les Ip étrangères.

De ce que j’avais trouvé, la database GeoIP n’était plus maintenu et il fallait passer à GeoIP2, ce qui n’était pas possible nativement sur yunohost. Aussi j’ai fais le script de mise à jour de la database qui est vraisemblablement nécessaire.

Ce qui me chagrine dans la solution fail2ban si je comprends le dernier commentaire de ton lien, c’est que lorsque la banaction geohostsdeny est utilisée, on ne pourrait pas en utiliser une autre. Donc si une attaque provient d’un pays mis en liste blanche, l’IP de celle-ci ne serait jamais bannie, ce qui n’est pas vraiment l’idéal.
Avec la méthode que j’indique, le serveur ne retourne aucune information et ferme la connexion, le filtrage n’est pas fait après coup comme avec fail2ban. Néanmoins tu as raison de préciser que ça ne concerne que Nginx.

Pour SSH je n’ai pas ouvert le port car je ne m’y connecte qu’en local. Si je devais ouvrir celui-ci, je préfère de toute façon une connexion par échange de clefs qui me semble beaucoup plus sécurisée qu’une connexion par mot de passe.
Pour d’autres services comme le mail, je ne crois pas que bloquer les requêtes venant de l’étranger soit une bonne idée car on ne sait pas à l’avance la localisation des serveurs d’où partent les mails. Je fais peut-être erreur dans mon interprétation, idem, ne pas hésiter à me corriger ou y apporter des précisions.

Je n’ai peut-être pas bien tout compris, tout comme toi je n’ai pas de connaissance en administration serveur, yunohost est justement une formidable solution qui nous permet de s’auto-héberger, je ne saurais trop remercier toutes les personnes qui participent à cette aventure.

merci pour ta réponse complète!

c’est que lorsque la banaction geohostsdeny est utilisée, on ne pourrait pas en utiliser une autre. Donc si une attaque provient d’un pays mis en liste blanche, l’IP de celle-ci ne serait jamais bannie, ce qui n’est pas vraiment l’idéal.

Je n’ai lu que superficiellement la page que j’ai donné en lien, je voulais juste avoir un exemple à montrer. Effectivement ce n’est pas top :confused:
je vais essayer de chercher sérieusement quelque chose de plus abouti, mais sinon je n’avais pas pensé que fermer le port SSH sur ma box peut aussi être une solution (vu que je n’administre pas en dehors de chez moi).

Thanks :slight_smile:

Si tu te connectes en SSH que de chez toi, effectivement ça n’a aucun intérêt d’ouvrir le port dans ta box. Dans la box tu n’ouvres que les ports dont tu as besoin d’un accès de l’extérieur.

Je n’ai pas creusé la question mais si il n’y a pas moyen de mettre plusieurs banaction pour la même jail de fail2ban, il doit avoir moyen dans ce cas de dupliquer le filtre et de faire 2 jails.
Par exemple 2 filtres identiques [sshd] et [sshd-bis], dans le second on filtre par le pays en ajoutant la banaction geohostsdeny et un maxretry inférieur au 1er filtre.
Comme ça si le pays est en liste blanche mais tente une attaque, il est quand même bloqué par le filtre 1.

Une annonce qui pourrait t’intéresser @Sango

1 Like

Oui, lu (je suis de près les avancées d’IPfire :wink: )
Et j’aime bien cette idée de développement d’un outil indépendant !

Merci à toi d’avoir pensé à moi :slight_smile:

Encore une question, j’allais me lancer dans les modifs mais est ce que ça ne sera pas perdu à la prochaine mise à jour de yunohost? Je pense surtout au fichier /etc/nginx/conf.d/yunohost_admin.conf ?

Non, les modifications sont conservées.
Pour voir les différences avec le fichier que tu devrais avoir si tu avais laissé par défaut:

yunohost tools regen-conf -n -d

Ça te permet de jeter un coup d’œil pour voir si tout est conforme lors de mise à jour.

Ce sont les fichiers d’applications qui sont généralement remplacées et une copie de ceux-ci avant mise à jour se trouve dans /home/yunohost.conf/backup. C’est utile par exemple pour des applications comme jirafeau si tu as personnalisé la page d’accueil, il suffit de récupérer le fichier.

Bonjour,

Tuto mis à jour avec les modifications indiquées ci-dessous. Toujours fonctionnel avec Buster (yunohost 4)

Pour ceux qui aurait apporté ces modifications, afin de bénéficier des dernières configurations de Nginx apportées par la version 3.8 de Yunohost, je conseille de faire:

yunohost tools regen-conf nginx --force

Si vous aviez apporté d’autres modifications à ces fichiers, il sera nécessaire de les refaire car elles seront également écrasées. Une sauvegarde de /etc/nginx/* sera utile avant de passer cette commande afin de les retrouver facilement.

Pour remettre en place le filtrage de géolocalisation, le tuto est toujours valable sauf la fin, il suffit de placer maintenant la partie ci-dessous à la fin du fichier /etc/nginx/conf.d/security.conf.inc

if ($lan-ip = yes) {
set $allowed_country yes;
}
    # block the country
    if ($allowed_country = no) {
        return 444;
    }

Pour ceux qui utilisaient déjà la restriction geoip, les scripts ne sont pas écrasés avec le regen-conf. Seuls les fichiers nginx.conf, yunohost_admin.conf et security.conf.inc seront à modifier à nouveau.

Pour info, il est possible de créer un “hook” (en gros un script) qui se déclenche lors de la post regenconf de nginx afin de ne pas avoir à refaire à chaque fois le boulot.

1 Like

Certainement @ljf, cependant je n’ai pas la moindre idée de comment le créer et la seule fois ou j’ai essayé d’en faire un, je m’y suis cassé les dents, il y avait un bug.
Il y a déjà un “hook” nginx, c’est possible d’en utiliser un second pour ajouter les modifications ou faut-il modifier celui existant? Et où le mettre, dans /etc/yunohost/hooks.d ou faut-il le laisser dans /usr/share/yunohost/hooks/conf_regen?
En même temps, quand il y a des changements comme ici l’ajout du fichier security.conf.inc avec la version 3.8, je ne vois pas vraiment comment le prévoir.
Pourtant ça serait le plus simple pour partager, le hook s’occupant des modifications, l’idée me plaît bien :slightly_smiling_face:

Dans mailman_ynh il y a un exemple de hook pour la regenconf de postfix

Le fichier est placé par l’app dans /usr/share/yunohost/hooks/conf_regen/ mais normalement la bonne pratique c’est de le mettre dans /etc/yunohost/hooks.d/conf_regen/ .

Dans le hook il y a une fonction pour le pre et une fonction pour le post. Après réflexion mon intuition me dit qu’en fait la modif doit être faite à coup de sed dans le pre_regenconf.

1 Like

Merci pour ce tuto et l’enrichissante discussion qui s’en est suivi.

@metyun : je reçois quotidiennement par Logwatch des alertes de tentatives de connexion via mod_proxy :

Connection attempts using mod_proxy:
124.225.45.203 -> 110.242.68.4:443: 1 Time(s)
149.129.50.37 -> [www.proxylists.net:443](http://www.proxylists.net:443): 1 Time(s)
223.166.75.23 -> 110.242.68.4:443: 1 Time(s)
63.143.61.42 -> example.com:443: 3 Time(s)

Dans cet exemple on voit 3 IP chinoises et une américaine.

En soi ce n’est pas gravissime, mais puis-je bloquer ces tentatives en amont via ton script, ne serait-ce que pour alléger mes logs ?

Et puis mes utilisateurs étant tous en France, fermer les portes aux autres pays n’est pas un problème. :stuck_out_tongue:

L’idée de passer par fail2ban est intéressante, ne pourrait-on pas contourner le problème de pays en whitelist en blacklistant à la place le reste des pays (quelques centaines d’occurrences tout au plus) ?

Non, ça n’empêche pas les requêtes d’apparaître dans les logs. Ces tentatives de connexion utilisent sans doute une méthode CONNECT, et elles apparaissent dès la 1ère tentative dans le rapport logwatch avec une erreur 400. Regarde les logs pour le confirmer.
Pour alléger tes logs, tu peux ajouter un filtre Fail2ban qui filtre les lignes de logs avec une erreur en 4xx. J’utilise la règle suivante conjointement à ce script pour alléger les logs:

failregex = <HOST>.*"(GET|POST|HEAD|CONNECT).* 4(0[0-9]|1[0-5]|44) .*$

Pour info, la source des fichiers .dat ne permet plus qu’une connexion en Ipv6 depuis peu. L’ayant désactivé sur le serveur, j’utilise désormais une nouvelle source avec ce script:

#!/bin/bash

maj_geoip () {
    cd /usr/share/GeoIP
    wget https://mailfud.org/geoip-legacy/GeoIP.dat.gz
    gunzip -f GeoIP.dat.gz
    wget https://mailfud.org/geoip-legacy/GeoIPv6.dat.gz
    gunzip -f GeoIPv6.dat.gz
}

last=/usr/local/etc/last
update=$(sed -n 's,.*<b>\(.*\)</b>,\1,p' <(curl -s https://mailfud.org/geoip-legacy/ | grep 'Latest update'))
[[ "$(<$last)" != "$update" ]] && (echo "$update" > "$last";maj_geoip)
exit 0

Plop

J’ai mis en place ce blocage geoip il y a quelques jours et… ça semble marcher. Un peu trop bien, puisque j’ai posté une image hébergée sur nextcloud (lien direct suffixé du /preview pour un affichage via balise) sur un forum américain, et mes interlocuteurs n’ont pas pu voir mon image :upside_down_face:

J’ai dans l’idée qu’il faudrait ajouter un filtre dans /etc/nginx/conf.d/security.conf.inc pour ne pas filtrer les pages commençant par https://monbeauserveur.noho.st/nextcloud/s/

Super.
J’ai vu ce post hier soir. Du coup je me suis posé beaucoup de questions : y aurait il quelqu’un d’autre qui pourrait accéder à mon serveur ? Comment je peux savoir qui a tenté de se connecter ? Comment restreindre l’accès au maximum ?
Je suis pas super calé en sécurité. J’ai lu pas mal mais je pense que c’est pas assez.
L’idée de bloquer l’accès avec le firewall avec geoip est séduisante.
Voici les articles que j’ai laissé ouvert pour les relire et mieux comprendre :

https://inai.de/projects/xtables-addons/