Filtrage IP automatisé

Msappdem

Nouveau membre
5 Novembre 2022
13
2
8
Bonjour,

J'utilise Unifi OS Server hébergé sur une VM de mon Synology

Tuto ici :

actuellement en version 5.0.6 (Network 10.2.105). Avant j'utilisais une image Docker (linuxserver/unifi-network-application) mais de ce que j'ai compris ce n'est plus supporté.

Je me suis aperçus que je subissais beaucoup de tentative d'intrusion malgré mes petites règles et je suis tombé sur un article de Korben (https://korben.info/data-shield-ipv4-blocklist.html) au sujet de Data-Shield (https://github.com/duggytuxy/Data-Shield_IPv4_Blocklist).
J'aurais bien voulu pouvoir automatiser l'intégration directement dans le routeur, mais ce n'est pas prévu donc j'ai fait plusieurs essaie et j'ai fini par regarder comment fonctionnait l'API.

Je ne sais pas si ça peut servir, mais j'ai fait un petit script (aidé par l'IA) pour mettre à jour mes règles de par feu pour blacklister des adresses ip.

Ce que fait le script :

Le script va télécharger un fichier de référence plein d'adresse IP, va le découper en plusieurs fichiers texte si besoin puis va créer deux règles de filtrage (blocage) nommées : IP Ban 01 - Ext-Gat et IP Ban 01 - Ext-Int
Le chiffre est incrémental en fonction du nombre de fichier.
Le but étant de bloquer une liste d'adresse IP de la zone Externe vers Gateway et Interne.

Prés-requis :
  • Créer une API Key (https://ip: Port/network/default/integrations)
  • Avoir un serveur pour programmer l'execution du script (moi j'ai du Synology)
Limite(s) :
  • Une règle de parfeu est limitée à 10000 entrées
Utilisation :

Le script requiert trois paramètres obligatoires :

Il est également possible de préciser :

Pour lancer le script :

Bash:
script.sh <IP> <PORT> <APIKEY> [<URL_FICHIER> <MAX_LIGNES> <LIMIT>]

Programmation :

Mon script est placé dans un dossier sur le NAS : /NetBackup/NAS/Scripts/ubiquiti_blocklist_ip/ip.sh
puis executé dans une tâche planifiée en tant qu'utilisateur root (mais ça doit fonctionner en tant qu'utilisateur) avec la commande :

Bash:
bash /volume1/NetBackup/NAS/Scripts/ubiquiti_blocklist_ip/ip.sh 192.168.0.10 443 qsdsqdxwcdcqsdqsdsqdsqd

Le script :

Bash:
#!/bin/sh

IP="$1"
PORT="$2"
APIKEY="$3"
IPS="${4:-"https://cdn.jsdelivr.net/gh/duggytuxy/Data-Shield_IPv4_Blocklist@refs/heads/main/prod_critical_data-shield_ipv4_blocklist.txt"}"
IPMAX="${5:-9998}"
LIMIT="${6:-20}"

if [ -z "$IP" ] || [ -z "$PORT" ] || [ -z "$APIKEY" ] || [ -z "$IPS" ] || [ -z "$IPMAX" ] || [ -z "$LIMIT" ]; then
    echo "Usage: $0 <IP> <PORT> <APIKEY> [<URL_FICHIER> <MAX_LIGNES> <LIMIT>]"
    exit 1
fi

echo "Verification de l'acces API..."

INFO_JSON=$(curl -sk "https://$IP:$PORT/proxy/network/integration/v1/info" \
  -H "Accept: application/json" \
  -H "X-API-Key: $APIKEY")

if [ $? -ne 0 ] || [ -z "$INFO_JSON" ]; then
    echo "Erreur : impossible de joindre l'API"
    exit 1
fi

echo "$INFO_JSON" | grep -q "applicationVersion"
if [ $? -ne 0 ]; then
    echo "Erreur : reponse API inattendue"
    exit 1
fi

echo "Connexion API OK"

echo "Recuperation de la liste des sites..."
SITE_JSON=$(curl -sk "https://$IP:$PORT/proxy/network/integration/v1/sites" \
  -H "Accept: application/json" \
  -H "X-API-Key: $APIKEY")

if [ $? -ne 0 ] || [ -z "$SITE_JSON" ]; then
    echo "Erreur : impossible de recuperer les sites"
    exit 1
fi

SITE_ID=$(echo "$SITE_JSON" | sed -n 's/.*"id"[ ]*:[ ]*"\([^"]*\)".*/\1/p' | head -n 1)

if [ -z "$SITE_ID" ]; then
    echo "Erreur : aucun site ID detecte"
    exit 1
fi

echo "Site ID detecte : $SITE_ID"

echo "Suppression des fichiers .txt existants..."
rm -f *.txt

echo "Telechargement du fichier source..."
curl -sk "$IPS" -o filtreperso.txt
if [ $? -ne 0 ] || [ ! -s filtreperso.txt ]; then
    echo "Erreur : impossible de telecharger le fichier"
    exit 1
fi

echo "Decoupage du fichier en blocs de $IPMAX lignes..."
split -l "$IPMAX" filtreperso.txt filtreperso_

echo "Nettoyage des fichiers generes..."
for f in filtreperso_*; do
    # Supprimer la dernière ligne si elle est vide
    sed -i '${/^$/d;}' "$f"

    # Ajouter extension .txt
    mv "$f" "$f.txt"
done

echo "Suppression du fichier source filtreperso.txt..."
rm -f filtreperso.txt

echo "Comptage des fichiers generes..."
NB_FICHIERS=$(ls filtreperso_*.txt 2>/dev/null | wc -l)

echo "Nombre de fichiers generes : $NB_FICHIERS"

echo "Recuperation des zones firewall..."

ZONES=$(curl -sk "https://$IP:$PORT/proxy/network/integration/v1/sites/$SITE_ID/firewall/zones" \
  -H "Accept: application/json" \
  -H "X-API-Key: $APIKEY")

if [ -z "$ZONES" ]; then
    echo "Erreur : impossible de recuperer les zones firewall"
    exit 1
fi

ZONE_EXTERNAL=$(echo "$ZONES" | grep -o '{"id":"[^"]*","name":"External"[^}]*}' | sed 's/.*"id":"\([^"]*\)".*/\1/')
ZONE_INTERNAL=$(echo "$ZONES" | grep -o '{"id":"[^"]*","name":"Internal"[^}]*}' | sed 's/.*"id":"\([^"]*\)".*/\1/')
ZONE_GATEWAY=$(echo "$ZONES" | grep -o '{"id":"[^"]*","name":"Gateway"[^}]*}' | sed 's/.*"id":"\([^"]*\)".*/\1/')

echo "Zone External : $ZONE_EXTERNAL"
echo "Zone Internal : $ZONE_INTERNAL"
echo "Zone Gateway  : $ZONE_GATEWAY"

echo "Verification des regles de pare-feu..."

# On récupère toutes les policies une seule fois
POLICIES=$(curl -sk "https://$IP:$PORT/proxy/network/integration/v1/sites/$SITE_ID/firewall/policies?filter=name.like('IP%20Ban*')&limit=$LIMIT" \
  -H "Accept: application/json" \
  -H "X-API-Key: $APIKEY")

echo "Suppression des anciennes policies :"

echo "$POLICIES" | jq -c '.data[]' | while read -r POLICY; do
    ID=$(echo "$POLICY" | jq -r '.id')
    NAME=$(echo "$POLICY" | jq -r '.name')

    echo " - $NAME  (ID: $ID)"

    POLICIEDELETE=$(curl -sk -X DELETE "https://$IP:$PORT/proxy/network/integration/v1/sites/$SITE_ID/firewall/policies/$ID" \
      -H "Accept: application/json" \
      -H "X-API-Key: $APIKEY")

done

echo "Création des policies :"

i=1
for f in filtreperso_*.txt; do
    # echo "Traitement du fichier : $f"

    SUFFIX=$(printf "%02d" "$i")

    RULE1="IP Ban $SUFFIX - Ext-Gat"
    RULE2="IP Ban $SUFFIX - Ext-Int"

    ITEMS=$(awk 'NF {printf "{\"type\":\"IP_ADDRESS\",\"value\":\"%s\"},", $0}' "$f")
    ITEMS="[${ITEMS%,}]"

BODY1=$(cat <<EOF
{
  "enabled": true,
  "name": "$RULE1",
  "description": "Auto-generated $(date +"%Y-%m-%d %H:%M:%S")",
  "action": { "type": "BLOCK" },
  "source": {
    "zoneId": "$ZONE_EXTERNAL",
    "trafficFilter": {
      "type": "IP_ADDRESS",
      "ipAddressFilter": {
        "type": "IP_ADDRESSES",
        "matchOpposite": false,
        "items": $ITEMS
      }
    }
  },
  "destination": {
    "zoneId": "$ZONE_GATEWAY"
  },
  "ipProtocolScope": {
    "ipVersion": "IPV4"
  },
  "loggingEnabled": false
}
EOF
)

BODY2=$(cat <<EOF
{
  "enabled": true,
  "name": "$RULE2",
  "description": "Auto-generated $(date +"%Y-%m-%d %H:%M:%S")",
  "action": { "type": "BLOCK" },
  "source": {
    "zoneId": "$ZONE_EXTERNAL",
    "trafficFilter": {
      "type": "IP_ADDRESS",
      "ipAddressFilter": {
        "type": "IP_ADDRESSES",
        "matchOpposite": false,
        "items": $ITEMS
      }
    }
  },
  "destination": {
    "zoneId": "$ZONE_INTERNAL"
  },
  "ipProtocolScope": {
    "ipVersion": "IPV4"
  },
  "loggingEnabled": false
}
EOF
)

    echo " - $RULE1"
    echo "$BODY1" > body1.json

    POLICIECREATE1=$(curl -sk -X POST \
      "https://$IP:$PORT/proxy/network/integration/v1/sites/$SITE_ID/firewall/policies" \
      -H "Accept: application/json" \
      -H "Content-Type: application/json" \
      -H "X-API-Key: $APIKEY" \
      --data @body1.json)

    rm -f body1.json

    echo " - $RULE2"
    echo "$BODY2" > body2.json

    POLICIECREATE2=$(curl -sk -X POST \
      "https://$IP:$PORT/proxy/network/integration/v1/sites/$SITE_ID/firewall/policies" \
      -H "Accept: application/json" \
      -H "Content-Type: application/json" \
      -H "X-API-Key: $APIKEY" \
      --data @body2.json)

    rm -f body2.json

    i=$((i+1))
done

echo "Operations terminees."

Voila je voulais juste partager quelque chose qui fonctionne chez moi et qui pourrait servir.
 
Dernière édition:
  • J'aime
Réactions: morgyann
Bonjour,

Merci pour l'info et le script.

J'utilise Unifi OS Server hébergé sur une VM
Questions (je n'ai jamais utilisé Unifi et complètement néophite) :

1. Faut-il avoir du matériel Unifi ?
2. Si non, l'OS est-il libre d'utilisation ?
3. Et peut-on l'installer sur une machine en début de réseau (pour tout le réseau et machines) ?
 
Bonjour,

Merci pour l'info et le script.


Questions (je n'ai jamais utilisé Unifi et complètement néophite) :

1. Faut-il avoir du matériel Unifi ?
2. Si non, l'OS est-il libre d'utilisation ?
3. Et peut-on l'installer sur une machine en début de réseau (pour tout le réseau et machines) ?
Bonjour,

oui, il faut du matériel Unifi. J'ai un UXG Fiber sans firmware, cet OS permet l'administration.
 
Je pense qu'il y a confusion : l'UXG Fiber pour être gérer à besoin d'Unifi Network cette appli est native sur les UDM mais peut-être installée sur PC Windows ; en Docker .
Unifi OS est le logiciel des UDM et qui regroupe plusieurs applis Unifi : Network , Protect ,Access
Pour moi d'après la Doc tu devrais avoir le pare-feu Unifi intégrer ;
Note : a mes débuts sur le matériel Unifi j'avais installé Unifi Network en Docker pour gérer un AP et un Switch , maintenant je suis passé sur UDM_SE.
Un des pb est que Unifi Network à souvent des mises à jour donc surveiller la version chez moi je suis en 10.2.105
 
Je pense qu'il y a confusion : l'UXG Fiber pour être gérer à besoin d'Unifi Network cette appli est native sur les UDM mais peut-être installée sur PC Windows ; en Docker .
Unifi OS est le logiciel des UDM et qui regroupe plusieurs applis Unifi : Network , Protect ,Access
Pour moi d'après la Doc tu devrais avoir le pare-feu Unifi intégrer ;
Note : a mes débuts sur le matériel Unifi j'avais installé Unifi Network en Docker pour gérer un AP et un Switch , maintenant je suis passé sur UDM_SE.
Un des pb est que Unifi Network à souvent des mises à jour donc surveiller la version chez moi je suis en 10.2.105
Bonjour,
Je confirme, avant j'avais un container docker avec Unifi Network et j'avais des avertissements indiquant qu'il serait obligatoire de passer par Unifi OS. Je sais pas si c'est toujours d'actualité, mais Unifi OS ne fonctionne pas en docker.
 
J'utilise au boulot les règles (e)DROP de SpamHaus depuis une bonne quinzaine d'années et sont utilisées sur tous nos équipements (firewalls applicatifs, border, etc.). Elles restent une référence dans la sécurité IT. Point positif pour les homelabs, elles sont gratuites ;)

Ci-après un script shell (POSIX donc portable pour un shell de type Bourne). Il est adapté pour ipfw(8), un firewall sur *BSD donc suivant le pare-feu, adapter les fonctions flush_ipfw_tables(), reload_ipfw_tables() et les options de sed, cut, fetch, grep, etc. qui ne proviennent pas des outils GNU œuf corse.

Bash:
#!/bin/zsh
# Generates an IPFW-format deny list based on
# the Spamhaus IPv4/IPv6 DROP list
# https://www.spamhaus.org/drop/

generate_ipv4() {
    IPFWSCRIPT_IPV4=/etc/ipfw.DROPlist-ipv4
    DROPLIST=https://www.spamhaus.org/drop/drop.lasso
    LOCALNEWFILE=/tmp/droplist-ipv4.new
    LOCALOLDFILE=/tmp/droplist-ipv4.old
    TMP=`mktemp -t tmpdroplist-ipv4`
    TMP1=`mktemp -t tmpdroplist-ipv4`
    TMP2=`mktemp -t tmpdroplist-ipv4`
    
    # Fetch the list
    fetch -q -o $LOCALNEWFILE $DROPLIST
    if [ $? -ne 0 ] ; then
            echo "`date`: makedroplist: failed to fetch $DROPLIST. Aborting..."
            exit 1
    fi
    
    # Compare the old and new file
    # No need to generate new rules if nothing have changed
    #
    # If old DROP list have disappeared, create a dummy
    if [ ! -f $LOCALOLDFILE ] ; then
            echo "Spamhaus IPv4 DROP List" > $LOCALOLDFILE
    fi
    
    # Remove first line from both files to ensure a real comparison
    # Spamhaus seems to change the first line every day, even if there are no changes
    grep -v "Spamhaus IPv4 DROP List" $LOCALOLDFILE > $TMP
    cp $TMP $LOCALOLDFILE
    grep -v "Spamhaus IPv4 DROP List" $LOCALNEWFILE > $TMP         
    cp $TMP $LOCALNEWFILE
    
    #
    # Use diff to see if they are different. We don't need to know WHAT has been changed
    #
    /usr/bin/diff -q $LOCALNEWFILE $LOCALOLDFILE > /dev/null
    if [ $? -eq 0 ] ; then
            # Be quiet when nothing has changed. Uncomment next line if you need to know
            echo "`date`: makedroplist: IPv4 DROP list hasn't changed. Exiting..."
            rm -f $TMP $TMP1 $TMP2 $LOCALNEWFILE
            exit 0
    fi
    
    # Clean up the file. We only want the CIDR blocks for ipfw
    cat -s $LOCALNEWFILE | cut -f 1 -d ";" | tr -d " " > $TMP
    
    # Get rid of blank lines in file
    sed '/./,$!d' $TMP > $TMP1
    sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' $TMP1 > $TMP2
    
    # Write a comment header etc, to the script
    echo "#!/bin/sh" > $IPFWSCRIPT_IPV4
    echo "# The Spamhaus IPv4 DROP list in IPFW format" >> $IPFWSCRIPT_IPV4
    echo "# See https://www.spamhaus.org/drop/ for details" >> $IPFWSCRIPT_IPV4
    echo "# This file was generated on `hostname`" >> $IPFWSCRIPT_IPV4
    echo "# Update: `date`" >> $IPFWSCRIPT_IPV4
    echo "" >> $IPFWSCRIPT_IPV4
    
    # Generate the ipv4 ipfw rules
    while read line ;do
       echo "/sbin/ipfw -q table spamhaus-ipv4 add $line" >> $IPFWSCRIPT_IPV4
    done < $TMP2
        
    # Clean up dust files
    rm -f $TMP $TMP1 $TMP2 $LOCALOLDFILE
    mv -f $LOCALNEWFILE $LOCALOLDFILE
    echo "`date`: IPv4 ipfw rules successfully generated from $DROPLIST"
}
    
generate_ipv6() {
    IPFWSCRIPT_IPV6=/etc/ipfw.DROPlist-ipv6
    DROPLIST=https://www.spamhaus.org/drop/dropv6.txt
    LOCALNEWFILE=/tmp/droplist-ipv6.new
    LOCALOLDFILE=/tmp/droplist-ipv6.old
    TMP=`mktemp -t tmpdroplist-ipv6`
    TMP1=`mktemp -t tmpdroplist-ipv6`
    TMP2=`mktemp -t tmpdroplist-ipv6`

    # fetch the list
    fetch -q -o $LOCALNEWFILE $DROPLIST
    if [ $? -ne 0 ] ; then
            echo "`date`: makedroplist: failed to fetch $DROPLIST. Aborting..."
            exit 1
    fi

    # Compare the old and new file
    # No need to generate new rules if nothing have changed
    #
    # If old DROP list have disappeared, create a dummy
    if [ ! -f $LOCALOLDFILE ] ; then
            echo "Spamhaus IPv6 DROP List" > $LOCALOLDFILE
    fi
    
    # Remove first line from both files to ensure a real comparison
    # Spamhaus seems to change the first line every day, even if there are no changes
    grep -v "Spamhaus IPv6 DROP List" $LOCALOLDFILE > $TMP
    cp $TMP $LOCALOLDFILE
    grep -v "Spamhaus IPv6 DROP List" $LOCALNEWFILE > $TMP         
    cp $TMP $LOCALNEWFILE

    #
    # Use diff to see if they are different. We don't need to know WHAT has been changed
    #
    /usr/bin/diff -q $LOCALNEWFILE $LOCALOLDFILE > /dev/null
    if [ $? -eq 0 ] ; then
            # Be quiet when nothing has changed. Uncomment next line if you need to know
            echo "`date`: makedroplist: IPv6 DROP list hasn't changed. Exiting..."
            rm -f $TMP $TMP1 $TMP2 $LOCALNEWFILE
            exit 0
    fi

    # Clean up the file. We only want the CIDR blocks for ipfw
    #echo "cleaning up"
    cat -s $LOCALNEWFILE | cut -f 1 -d ";" | tr -d " " > $TMP
    
    # Get rid of blank lines in file
    sed '/./,$!d' $TMP > $TMP1
    sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' $TMP1 > $TMP2

    # Write a comment header etc, to the script
    echo "#!/bin/sh" > $IPFWSCRIPT_IPV6
    echo "# The Spamhaus IPv6 DROP list in IPFW format" >> $IPFWSCRIPT_IPV6
    echo "# See https://www.spamhaus.org/drop/ for details" >> $IPFWSCRIPT_IPV6
    echo "# This file was generated on `hostname`" >> $IPFWSCRIPT_IPV6
    echo "# Update: `date`" >> $IPFWSCRIPT_IPV6
    echo "" >> $IPFWSCRIPT_IPV6

    # Generate the ipv6 ipfw rules
    while read line ;do
       echo "/sbin/ipfw -q table spamhaus-ipv6 add $line" >> $IPFWSCRIPT_IPV6
    done < $TMP2

    # Clean up dust files
    rm -f $TMP $TMP1 $TMP2 $LOCALOLDFILE
    mv -f $LOCALNEWFILE $LOCALOLDFILE
    echo "`date`: IPv6 ipfw rules successfully generated from $DROPLIST"

}

flush_ipfw_tables() {
    #First we flush 'table(spamhaus-ipv4)' and 'table(spamhaus-ipv6)' from old entries
    /sbin/ipfw -q table spamhaus-ipv4 flush
    /sbin/ipfw -q table spamhaus-ipv6 flush
}

reload_ipfw_tables() {
    # Load the rules
    sh $IPFWSCRIPT_IPV4 && sh $IPFWSCRIPT_IPV6
}

# Running each functions

generate_ipv4 && generate_ipv6 && flush_ipfw_tables && reload_ipfw_tables

exit 0
 
j'avais des avertissements indiquant qu'il serait obligatoire de passer par Unifi OS
Unifi Network existe tjrs en version Docker ainsi que Unifi Protect ; et peuvent donc fonctionner indépendamment mais cela t'oblige à mettre l'image à jour . Pour Unifi OS , a ma connaissance cela n'est gérer que sur les routeur Unifi ( UDM ; Cloud Gatexay ; Dream Router )
A noter que les version Docker n'exploite pas toutes les possibilités ( Gestion des VLAN )