Scripts de Sécurité
Outils d'automatisation et de protection
Clés & quotas
Ajoute X-API-Key à chaque requête. Respecte les limites, gère les codes 429 et utilise un backoff.
Mises à jour
Planifie des tâches récurrentes. Utilise ETag/If-None-Match si exposé par l’API pour éviter les re-téléchargements.
Durcissement
Applique des listes sur ipset/nftables, pf et Windows Firewall. Sauvegarde et journalise chaque action.
API – Exemples avancés
Filtrage, pagination, gestion 429 et ETag si disponible
HOST="https://blacklistip.com"
API_KEY="VOTRE_CLE_API"
curl -sS -H "X-API-Key: $API_KEY" \
"$HOST/api/v1/bans?limit=200&offset=0&country=FR&action=BLOCK" \
-D - | tee /tmp/resp.headers | jq .
ETAG=$(grep -i '^ETag:' /tmp/resp.headers | awk '{print $2}' | tr -d '\r')
[ -n "$ETAG" ] && curl -sS -H "X-API-Key: $API_KEY" -H "If-None-Match: $ETAG" "$HOST/api/v1/bans?limit=200" -w "\nHTTP %{http_code}\n"
import time, os, requests
HOST = "https://blacklistip.com"
API_KEY = os.getenv("API_KEY","VOTRE_CLE_API")
H = {"X-API-Key": API_KEY}
def get(path, params=None, etag=None, retries=5):
headers = H.copy()
if etag: headers["If-None-Match"] = etag
url = f"{HOST}{path}"
for i in range(retries):
r = requests.get(url, params=params or {}, headers=headers, timeout=30)
if r.status_code == 429:
wait = int(r.headers.get("Retry-After","2"))
time.sleep(max(wait, 2*(i+1))); continue
if r.status_code == 304: return {"not_modified": True}
r.raise_for_status()
return {"json": r.json(), "etag": r.headers.get("ETag")}
raise RuntimeError("Echec après backoff")
data = get("/api/v1/bans", {"limit":200, "country":"FR", "action":"BLOCK"})
print(bool(data.get("json")), data.get("etag"))
const HOST="https://blacklistip.com";
const API_KEY=process.env.API_KEY||"VOTRE_CLE_API";
async function api(path, params={}, etag){
const u=new URL(path, HOST); Object.entries(params).forEach(([k,v])=>u.searchParams.set(k,v));
for(let i=0;i<5;i++){
const r=await fetch(u, {headers:{"X-API-Key":API_KEY, ...(etag?{"If-None-Match":etag}:{})}});
if(r.status===429){const ra=Number(r.headers.get("Retry-After"))||2*(i+1); await new Promise(s=>setTimeout(s,ra*1000)); continue;}
if(r.status===304) return {not_modified:true};
if(!r.ok) throw new Error("HTTP "+r.status);
return {json:await r.json(), etag:r.headers.get("ETag")};
}
throw new Error("Echec après backoff");
}
api("/api/v1/bans",{limit:200, country:"FR", action:"BLOCK"}).then(console.log).catch(console.error);
package main
import ("fmt"; "net/http"; "io"; "os"; "time")
func main(){
host := "https://blacklistip.com"
req, _ := http.NewRequest("GET", host+"/api/v1/bans?limit=200&action=BLOCK", nil)
req.Header.Set("X-API-Key", os.Getenv("API_KEY"))
client := &http.Client{ Timeout: 30 * time.Second }
for i:=0;i<5;i++{
res, err := client.Do(req); if err!=nil { time.Sleep(time.Duration(2*(i+1))*time.Second); continue }
if res.StatusCode==429 { time.Sleep(2*time.Second); continue }
defer res.Body.Close(); b,_ := io.ReadAll(res.Body)
if res.StatusCode==200 { fmt.Println(string(b)); return }
if res.StatusCode==304 { fmt.Println("Not modified"); return }
fmt.Println("HTTP", res.StatusCode); os.Exit(1)
}
}
$HostUrl = "https://blacklistip.com"; $ApiKey = $env:API_KEY
$Headers = @{ "X-API-Key" = $ApiKey }
$Resp = Invoke-WebRequest -Uri "$HostUrl/api/v1/bans?limit=200&action=BLOCK" -Headers $Headers -UseBasicParsing
$ETag = $Resp.Headers.ETag
if ($ETag) { Invoke-WebRequest -Uri "$HostUrl/api/v1/bans?limit=200" -Headers (@{ "X-API-Key"=$ApiKey; "If-None-Match"=$ETag }) -UseBasicParsing }
Téléchargements – Direct & via API
Par année/mois ou endpoint de listing
Bash – jour courant
HOST="https://blacklistip.com"
Y=$(date +%Y); M=$(date +%m); D=$(date +%F)
F="blacklistip-$D.json"
URL="$HOST/front/files/$Y/$M/$F"
curl -fSLo "$F" "$URL" && jq -r '.[0] | keys_unsorted' "$F" || echo "Introuvable: $URL"
Python – liste & download
import os, requests
H="https://blacklistip.com"; A=os.getenv("API_KEY","VOTRE_CLE_API")
r=requests.get(f"{H}/api/v1/files", params={"year":"2025","month":"08"}, headers={"X-API-Key":A}, timeout=30); r.raise_for_status()
files=r.json(); name=files[0]["filename"]; d=requests.get(f"{H}/front/files/2025/08/{name}", timeout=60); open(name,"wb").write(d.content); print(name)
PowerShell – direct
$h="https://blacklistip.com"; $y=(Get-Date -UFormat %Y); $m=(Get-Date -UFormat %m); $d=(Get-Date -UFormat %Y-%m-%d)
$f="blacklistip-$d.json"; Invoke-WebRequest -Uri "$h/front/files/$y/$m/$f" -OutFile $f -UseBasicParsing
Linux – ipset/iptables & nftables
Listes volumineuses, mise à jour incrémentale
ipset + iptables (diff incrémental)
set -euo pipefail
SET="blkip"; HOST="https://blacklistip.com"
ipset list "$SET" >/dev/null 2>&1 || ipset create "$SET" hash:ip family inet hashsize 1024 maxelem 100000
iptables -C INPUT -m set --match-set "$SET" src -j DROP 2>/dev/null || iptables -I INPUT -m set --match-set "$SET" src -j DROP
Y=$(date +%Y); M=$(date +%m); D=$(date +%F); F="blacklistip-$D.json"
curl -fSLo "$F" "$HOST/front/files/$Y/$M/$F" || exit 0
jq -r '.[].ip // .ip' "$F" | sort -u > /tmp/blkip.new
ipset save "$SET" | awk '/^add/ {print $3}' | sort -u > /tmp/blkip.cur || true
comm -13 /tmp/blkip.cur /tmp/blkip.new | while read -r ip; do ipset add "$SET" "$ip" -!; done
comm -23 /tmp/blkip.cur /tmp/blkip.new | while read -r ip; do ipset del "$SET" "$ip" -!; done
echo "MAJ ipset OK"
nftables (set + règle)
set -e
TABLE="inet filter"; SET="blkip"; HOST="https://blacklistip.com"
nft list table $TABLE >/dev/null 2>&1 || nft add table $TABLE
nft list set $TABLE $SET >/dev/null 2>&1 || nft add set $TABLE $SET { type ipv4_addr; flags interval; }
nft list chain $TABLE input >/dev/null 2>&1 || nft add chain $TABLE input { type filter hook input priority 0\; }
nft list ruleset | grep -q "@$SET drop" || nft add rule $TABLE input ip saddr @$SET drop
Y=$(date +%Y); M=$(date +%m); D=$(date +%F); F="blacklistip-$D.json"
curl -fSLo "$F" "$HOST/front/files/$Y/$M/$F" || exit 0
jq -r '.[].ip // .ip' "$F" | sort -u | sed 's/^/add element inet filter blkip { /; s/$/ }/' | nft -f -
FreeBSD/OpenBSD – pf
Table pf persistante + ancre dédiée
pf – table & ancre
LIST="/etc/pf.blacklist"; ANCHOR="/etc/pf.anchors/blacklist"
fetch -o /tmp/blk.json "https://blacklistip.com/front/files/$(date +%Y)/$(date +%m)/blacklistip-$(date +%F).json" || exit 0
jq -r '.[].ip // .ip' /tmp/blk.json > "$LIST"
cat > "$ANCHOR" <<'EOF'
table <blkip> persist file "/etc/pf.blacklist"
block in quick from <blkip> to any
EOF
pfctl -t blkip -T replace -f "$LIST"
pfctl -f /etc/pf.conf
Windows – Defender Firewall
Règles par IP, idempotentes
PowerShell – création/update
$h="https://blacklistip.com"; $y=(Get-Date).ToString("yyyy"); $m=(Get-Date).ToString("MM"); $d=(Get-Date).ToString("yyyy-MM-dd")
$f="blacklistip-$d.json"; Invoke-WebRequest -Uri "$h/front/files/$y/$m/$f" -OutFile $f -UseBasicParsing
$data = Get-Content $f | ConvertFrom-Json
foreach ($row in $data) {
$ip = $row.ip; if (-not $ip) { continue }
$name = "BLK-$ip"
if (-not (Get-NetFirewallRule -DisplayName $name -ErrorAction SilentlyContinue)) {
New-NetFirewallRule -DisplayName $name -Direction Inbound -Action Block -RemoteAddress $ip -Enabled True
}
}
Réseaux – Mikrotik, Cisco/ASA, Cloudflare
Exemples d’intégration
MikroTik RouterOS
:local host "https://blacklistip.com"
:local y [/system clock get date]
:local yyyy [:pick $y 7 11]; :local mm [:pick $y 0 3]
:local mapMonth do={:local m $"0"; :if ($m="jan") do={:return "01"}; :if ($m="feb") do={:return "02"}; :if ($m="mar") do={:return "03"}; :if ($m="apr") do={:return "04"}; :if ($m="may") do={:return "05"}; :if ($m="jun") do={:return "06"}; :if ($m="jul") do={:return "07"}; :if ($m="aug") do={:return "08"}; :if ($m="sep") do={:return "09"}; :if ($m="oct") do={:return "10"}; :if ($m="nov") do={:return "11"}; :if ($m="dec") do={:return "12"}}
:local M [$mapMonth m=$mm]
:local d [/system clock get date]; :local day [:pick $d 4 6]
:local file ("blacklistip-" . $yyyy . "-" . $M . "-" . $day . ".json")
/tool fetch url=($host . "/front/files/" . $yyyy . "/" . $M . "/" . $file) dst-path="/blkip.json"
/ip firewall address-list remove [find list="blkip"]
:foreach line in=[/file get [/file find name="blkip.json"] contents] do={
# Adapter: parse JSON via script externe; RouterOS ne gère pas JSON nativement
}
Cisco ASA (ACL)
! Générer une ACL à partir d'un JSON (coté Linux) puis la pousser sur ASA
jq -r '.[].ip // .ip | "access-list OUTSIDE-IN extended deny ip host \(.) any"' blacklistip-$(date +%F).json > asa-blk.acl
! Copier ensuite sur l'équipement et appliquer via "access-group"
Cloudflare API (WAF IP list)
# Exemple: créer/mettre à jour une liste IP Cloudflare depuis Linux
CF_TOKEN="CF_API_TOKEN"; CF_ACC="ACCOUNT_ID"; LIST="blacklistip"
curl -sS -H "Authorization: Bearer $CF_TOKEN" "https://api.cloudflare.com/client/v4/accounts/$CF_ACC/rules/lists" | jq .
# Ensuite: pousser les IP avec l'endpoint de batch (voir docs Cloudflare)
Planification & Observabilité
cron
0 * * * * /usr/local/sbin/update-blacklist.sh >> /var/log/update-blacklist.log 2>&1
systemd service
[Unit]
Description=MAJ blocage IP BlacklistIP
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/update-blacklist.sh
systemd timer
[Unit]
Description=Timer MAJ blocage IP BlacklistIP
[Timer]
OnBootSec=2m
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
Infra-as-Code & Déploiements
Ansible, Docker, Kubernetes
Ansible – téléchargement + ipset
- name: Télécharger la liste du jour
get_url:
url: "https://blacklistip.com/front/files/2025/08/blacklistip-2025-08-26.json"
dest: /tmp/blacklist.json
- name: Appliquer ipset
shell: |
ipset list blkip >/dev/null 2>&1 || ipset create blkip hash:ip
jq -r '.[].ip // .ip' /tmp/blacklist.json | xargs -r -n1 -I{} ipset add blkip {} -!
Docker – updater
# Dockerfile
FROM alpine:3.20
RUN apk add --no-cache curl jq ipset iptables bash
ENV HOST="https://blacklistip.com"
COPY update.sh /usr/local/bin/update.sh
CMD ["/usr/local/bin/update.sh"]
Kubernetes – CronJob (download)
apiVersion: batch/v1
kind: CronJob
metadata:
name: blacklistip-download
spec:
schedule: "0 * * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: fetch
image: curlimages/curl
args:
- sh
- -c
- >
h=https://blacklistip.com;
y=$(date +%Y); m=$(date +%m); d=$(date +%F);
curl -fSLo /data/blacklistip-$d.json $h/front/files/$y/$m/blacklistip-$d.json
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
emptyDir: {}