@liberstudio/cloudflare-list 2.3.9 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,183 +1,184 @@
1
- # @liberstudio/cloudflare-list
2
-
3
- Modulo NestJS per il rilevamento e blocco automatico di attacchi tramite Cloudflare IP List.
4
-
5
- Ogni richiesta che produce un `404` viene considerata sospetta (tipicamente bot, scanner, enumeratori). L'IP viene risolto tramite ASN lookup per ottenere l'intero range di subnet e bloccarlo direttamente sulla lista Cloudflare WAF. Se il lookup fallisce, viene bloccato il singolo IP.
6
-
7
- ## Come funziona
8
-
9
- ```
10
- Request → 404 rilevato dal middleware
11
- └─ IP estratto (cf-connecting-ip / x-real-ip / x-forwarded-for)
12
- └─ throttle 5s per IP
13
- └─ log su file (se path non è silentPath)
14
- └─ allowedIps? → skip (log only)
15
- └─ IP Cloudflare? → skip (log only)
16
- └─ ASN lookup su api.hackertarget.com
17
- ├─ range trovato → blocca intera subnet su Cloudflare
18
- └─ fallback → blocca singolo IP /32 o /128
19
- ```
20
-
21
- ### Filtraggio automatico
22
-
23
- Il modulo ignora automaticamente:
24
- - IP nella whitelist `allowedIps` (configurazione statica)
25
- - IP appartenenti ai range ufficiali Cloudflare (scaricati e cachati 24h)
26
- - IP privati (10.x, 192.168.x, 172.16–31.x)
27
- - Loopback (127.0.0.1, ::1)
28
- - IPv4-mapped IPv6 (::ffff:x.x.x.x → convertiti in IPv4)
29
-
30
- ### Gestione errori e log
31
-
32
- - `AllExceptionsFilter` globale cattura tutte le eccezioni non gestite e le logga a console con IP, metodo, path e status code
33
- - Con `verbose: true` il log include anche body, query, params e stack trace
34
-
35
- ---
36
-
37
- ## Prima di iniziare su Cloudflare
38
-
39
- 1. Creare un account Cloudflare
40
- 2. Scegliere il dominio da utilizzare
41
- 3. **Manage Account > Configuration > Lists > Create List** (tipo: IP)
42
- 4. **Account API tokens > Create Token > Create Custom Token**
43
- - Permission: `Account > Account WAF > Edit`
44
- - Permission: `Account > Account Filter Lists > Edit`
45
- 5. **Security > WAF > Custom Rules > Create Rule**
46
- - Field: `IP Source Address` — `is in` — scegli la lista creata
47
- - Action: `Block`
48
- 6. Salvare la regola
49
-
50
- ---
51
-
52
- ## Installazione
53
-
54
- ```bash
55
- npm install @liberstudio/cloudflare-list
56
- ```
57
-
58
- ---
59
-
60
- ## Configurazione
61
-
62
- ### Statica (`forRoot`)
63
-
64
- ```typescript
65
- import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
66
-
67
- @Module({
68
- imports: [
69
- CloudflareAttacksModule.forRoot({
70
- accountId: "abc123",
71
- listId: "def456",
72
- apiToken: "your-cloudflare-api-token",
73
- comment: "Blocked by liberstudio/cloudflare-list",
74
- logPath: "/var/log/nestjs-attacks.log",
75
- allowedIps: ["1.2.3.4", "10.0.0.0/8"],
76
- excludedPaths: ["/api/health", "/api/webhook"],
77
- silentPaths: ["/auth/me", "/auth/refresh"],
78
- verbose: false,
79
- whatsappNotify: { phone: "393331234567" },
80
- }),
81
- ],
82
- })
83
- export class AppModule {}
84
- ```
85
-
86
- ### Asincrona (`forRootAsync`) con `ConfigModule`
87
-
88
- ```typescript
89
- import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
90
-
91
- @Module({
92
- imports: [
93
- CloudflareAttacksModule.forRootAsync({
94
- imports: [ConfigModule],
95
- inject: [ConfigService],
96
- useFactory: (config: ConfigService) => ({
97
- apiToken: config.getOrThrow<string>("CLOUDFLARE_API_TOKEN"),
98
- accountId: config.getOrThrow<string>("CLOUDFLARE_ACCOUNT_ID"),
99
- listId: config.getOrThrow<string>("CLOUDFLARE_LIST_ID"),
100
- comment: config.get<string>("CLOUDFLARE_LIST_COMMENT") || "Blocked",
101
- logPath: config.get<string>("CLOUDFLARE_LIST_LOG_PATH") || "/var/log/nestjs-attacks.log",
102
- allowedIps: ["1.2.3.4", "10.0.0.0/8"],
103
- excludedPaths: ["/api/health", "/api/webhook", /^\/api\/public\/.*/],
104
- silentPaths: ["/auth/me", "/auth/refresh"],
105
- verbose: false,
106
- whatsappNotify: {
107
- phone: config.getOrThrow<string>("WA_NOTIFY_PHONE"),
108
- },
109
- }),
110
- }),
111
- ],
112
- })
113
- export class AppModule {}
114
- ```
115
-
116
- ---
117
-
118
- ## Opzioni di configurazione
119
-
120
- | Opzione | Tipo | Obbligatorio | Descrizione |
121
- |---|---|---|---|
122
- | `apiToken` | `string` | si | Token API Cloudflare |
123
- | `accountId` | `string` | si | ID account Cloudflare |
124
- | `listId` | `string` | si | ID della IP List Cloudflare |
125
- | `comment` | `string` | si | Commento aggiunto ad ogni IP bloccato |
126
- | `logPath` | `string` | si | Percorso del file di log degli attacchi |
127
- | `allowedIps` | `string[]` | no | IP o CIDR mai bloccati (whitelist, supporta IPv4 e IPv6) |
128
- | `excludedPaths` | `(string \| RegExp)[]` | no | Path esclusi da ogni rilevamento e log |
129
- | `silentPaths` | `(string \| RegExp)[]` | no | Path bloccati su Cloudflare ma senza log su file |
130
- | `verbose` | `boolean` | no | Se `true`, il log include body, query, params e stack |
131
- | `whatsappNotify` | `{ phone: string }` | no | Invia una notifica WhatsApp quando un IP viene bannato |
132
-
133
- ### `whatsappNotify` — notifiche ban su WhatsApp
134
-
135
- Quando configurato, invia un messaggio WhatsApp ad un numero specifico ogni volta che un IP viene bloccato su Cloudflare. Richiede le variabili d'ambiente `WA_URL`, `WA_USER`, `WA_PASSWORD` e `CLOUDFLARE_WA_DEVICE_ID` già presenti nel progetto (lette automaticamente da `process.env`).
136
-
137
- ```typescript
138
- whatsappNotify: {
139
- phone: "393331234567", // numero con prefisso internazionale, senza +
140
- }
141
- ```
142
-
143
- Il messaggio inviato sarà:
144
- ```
145
- 🚨 IP bannato su Cloudflare
146
- IP: 1.2.3.4
147
- Subnet bloccata: 1.2.0.0/16
148
- ```
149
-
150
- Variabili d'ambiente richieste (lette automaticamente da `process.env`):
151
- ```env
152
- WA_URL=https://messaggistica.liberdesign.eu
153
- WA_USER=your-user
154
- WA_PASSWORD=your-password
155
- CLOUDFLARE_WA_DEVICE_ID=your-device-id
156
- WA_NOTIFY_PHONE=393331234567
157
- ```
158
-
159
- ### `allowedIps` — whitelist server
160
-
161
- Lista di IP o subnet CIDR che non vengono mai bloccati, indipendentemente dal comportamento. Utile per i propri server, proxy interni, CDN proprietarie. Supporta IPv4, CIDR IPv4, IPv6 e CIDR IPv6:
162
-
163
- ```typescript
164
- allowedIps: [
165
- "1.2.3.4", // IP singolo IPv4
166
- "10.0.0.0/8", // subnet IPv4
167
- "2001:db8::1", // IP singolo IPv6
168
- "2001:db8::/32", // subnet IPv6
169
- ]
170
- ```
171
-
172
- > I range ufficiali Cloudflare sono esclusi automaticamente senza bisogno di aggiungerli qui.
173
-
174
- ### `excludedPaths` vs `silentPaths`
175
-
176
- - **`excludedPaths`**: il path viene ignorato completamente — nessun log, nessun blocco. Usato per path infrastrutturali (`/health`, `/webhook`).
177
- - **`silentPaths`**: il path viene bloccato su Cloudflare ma non viene scritto sul file di log. Usato per path che generano 404 legittimi frequenti (`/auth/me`, `/auth/refresh`).
178
-
179
- ---
180
-
181
- ## License
182
-
183
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1
+ # @liberstudio/cloudflare-list
2
+
3
+ Modulo NestJS per il rilevamento e blocco automatico di attacchi tramite Cloudflare IP List.
4
+
5
+ Ogni richiesta che produce un `404` viene considerata sospetta (tipicamente bot, scanner, enumeratori). L'IP viene risolto tramite ASN lookup per ottenere l'intero range di subnet e bloccarlo direttamente sulla lista Cloudflare WAF. Se il lookup fallisce, viene bloccato il singolo IP.
6
+
7
+ ## Come funziona
8
+
9
+ ```
10
+ Request → 404 rilevato dal middleware
11
+ └─ IP estratto (cf-connecting-ip / x-real-ip / x-forwarded-for)
12
+ └─ throttle 5s per IP
13
+ └─ log su file (se path non è silentPath)
14
+ └─ allowedIps? → skip (log only)
15
+ └─ IP Cloudflare? → skip (log only)
16
+ └─ ASN lookup su api.hackertarget.com
17
+ ├─ range trovato → blocca intera subnet su Cloudflare
18
+ └─ fallback → blocca singolo IP /32 o /128
19
+ ```
20
+
21
+ ### Filtraggio automatico
22
+
23
+ Il modulo ignora automaticamente:
24
+ - IP nella whitelist `allowedIps` (configurazione statica)
25
+ - IP appartenenti ai range ufficiali Cloudflare (scaricati e cachati 24h)
26
+ - IP privati (10.x, 192.168.x, 172.16–31.x)
27
+ - Loopback (127.0.0.1, ::1)
28
+ - IPv4-mapped IPv6 (::ffff:x.x.x.x → convertiti in IPv4)
29
+
30
+ ### Gestione errori e log
31
+
32
+ - `AllExceptionsFilter` globale cattura tutte le eccezioni non gestite e le logga a console con IP, metodo, path e status code
33
+ - Con `verbose: true` il log include anche body, query, params e stack trace
34
+
35
+ ---
36
+
37
+ ## Prima di iniziare su Cloudflare
38
+
39
+ 1. Creare un account Cloudflare
40
+ 2. Scegliere il dominio da utilizzare
41
+ 3. **Manage Account > Configuration > Lists > Create List** (tipo: IP)
42
+ 4. **Account API tokens > Create Token > Create Custom Token**
43
+ - Permission: `Account > Account WAF > Edit`
44
+ - Permission: `Account > Account Filter Lists > Edit`
45
+ 5. **Security > WAF > Custom Rules > Create Rule**
46
+ - Field: `IP Source Address` — `is in` — scegli la lista creata
47
+ - Action: `Block`
48
+ 6. Salvare la regola
49
+
50
+ ---
51
+
52
+ ## Installazione
53
+
54
+ ```bash
55
+ npm install @liberstudio/cloudflare-list
56
+ ```
57
+
58
+ ---
59
+
60
+ ## Configurazione
61
+
62
+ ### Statica (`forRoot`)
63
+
64
+ ```typescript
65
+ import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
66
+
67
+ @Module({
68
+ imports: [
69
+ CloudflareAttacksModule.forRoot({
70
+ accountId: "abc123",
71
+ listId: "def456",
72
+ apiToken: "your-cloudflare-api-token",
73
+ comment: "Blocked by liberstudio/cloudflare-list",
74
+ logPath: "/var/log/nestjs-attacks.log",
75
+ allowedIps: ["1.2.3.4", "10.0.0.0/8"],
76
+ excludedPaths: ["/api/health", "/api/webhook"],
77
+ silentPaths: ["/auth/me", "/auth/refresh"],
78
+ verbose: false,
79
+ whatsappNotify: { phone: "393331234567" },
80
+ }),
81
+ ],
82
+ })
83
+ export class AppModule {}
84
+ ```
85
+
86
+ ### Asincrona (`forRootAsync`) con `ConfigModule`
87
+
88
+ ```typescript
89
+ import { CloudflareAttacksModule } from "@liberstudio/cloudflare-list";
90
+
91
+ @Module({
92
+ imports: [
93
+ CloudflareAttacksModule.forRootAsync({
94
+ imports: [ConfigModule],
95
+ inject: [ConfigService],
96
+ useFactory: (config: ConfigService) => ({
97
+ apiToken: config.getOrThrow<string>("CLOUDFLARE_API_TOKEN"),
98
+ accountId: config.getOrThrow<string>("CLOUDFLARE_ACCOUNT_ID"),
99
+ listId: config.getOrThrow<string>("CLOUDFLARE_LIST_ID"),
100
+ comment: config.get<string>("CLOUDFLARE_LIST_COMMENT") || "Blocked",
101
+ logPath: config.get<string>("CLOUDFLARE_LIST_LOG_PATH") || "/var/log/nestjs-attacks.log",
102
+ allowedIps: ["1.2.3.4", "10.0.0.0/8"],
103
+ excludedPaths: ["/api/health", "/api/webhook", /^\/api\/public\/.*/],
104
+ silentPaths: ["/auth/me", "/auth/refresh"],
105
+ verbose: false,
106
+ whatsappNotify: {
107
+ phone: config.getOrThrow<string>("WA_NOTIFY_PHONE"),
108
+ },
109
+ }),
110
+ }),
111
+ ],
112
+ })
113
+ export class AppModule {}
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Opzioni di configurazione
119
+
120
+ | Opzione | Tipo | Obbligatorio | Descrizione |
121
+ |---|---|---|---|
122
+ | `apiToken` | `string` | si | Token API Cloudflare |
123
+ | `accountId` | `string` | si | ID account Cloudflare |
124
+ | `listId` | `string` | si | ID della IP List Cloudflare |
125
+ | `comment` | `string` | si | Commento aggiunto ad ogni IP bloccato |
126
+ | `logPath` | `string` | si | Percorso del file di log degli attacchi |
127
+ | `allowedIps` | `string[]` | no | IP o CIDR mai bloccati (whitelist, supporta IPv4 e IPv6) |
128
+ | `excludedPaths` | `(string \| RegExp)[]` | no | Path esclusi da ogni rilevamento e log |
129
+ | `silentPaths` | `(string \| RegExp)[]` | no | Path bloccati su Cloudflare ma senza log su file |
130
+ | `verbose` | `boolean` | no | Se `true`, il log include body, query, params e stack |
131
+ | `whatsappNotify` | `{ phone: string }` | no | Invia una notifica WhatsApp quando un IP viene bannato |
132
+
133
+ ### `whatsappNotify` — notifiche ban su WhatsApp
134
+
135
+ Quando configurato, invia un messaggio WhatsApp ad un numero specifico ogni volta che un IP viene bloccato su Cloudflare. Richiede le variabili d'ambiente `WA_URL`, `WA_USER`, `WA_PASSWORD` e `CLOUDFLARE_WA_DEVICE_ID` già presenti nel progetto (lette automaticamente da `process.env`).
136
+
137
+ ```typescript
138
+ whatsappNotify: {
139
+ phone: "393331234567", // numero con prefisso internazionale, senza +
140
+ }
141
+ ```
142
+
143
+ Il messaggio inviato sarà:
144
+ ```
145
+ 🚨 IP bannato su Cloudflare
146
+ Provenienza: Blocked by liberstudio/cloudflare-list
147
+ IP: 1.2.3.4
148
+ Subnet bloccata: 1.2.0.0/16
149
+ ```
150
+
151
+ Variabili d'ambiente richieste (lette automaticamente da `process.env`):
152
+ ```env
153
+ WA_URL=https://messaggistica.liberdesign.eu
154
+ WA_USER=your-user
155
+ WA_PASSWORD=your-password
156
+ CLOUDFLARE_WA_DEVICE_ID=your-device-id
157
+ WA_NOTIFY_PHONE=393331234567
158
+ ```
159
+
160
+ ### `allowedIps` — whitelist server
161
+
162
+ Lista di IP o subnet CIDR che non vengono mai bloccati, indipendentemente dal comportamento. Utile per i propri server, proxy interni, CDN proprietarie. Supporta IPv4, CIDR IPv4, IPv6 e CIDR IPv6:
163
+
164
+ ```typescript
165
+ allowedIps: [
166
+ "1.2.3.4", // IP singolo IPv4
167
+ "10.0.0.0/8", // subnet IPv4
168
+ "2001:db8::1", // IP singolo IPv6
169
+ "2001:db8::/32", // subnet IPv6
170
+ ]
171
+ ```
172
+
173
+ > I range ufficiali Cloudflare sono esclusi automaticamente senza bisogno di aggiungerli qui.
174
+
175
+ ### `excludedPaths` vs `silentPaths`
176
+
177
+ - **`excludedPaths`**: il path viene ignorato completamente — nessun log, nessun blocco. Usato per path infrastrutturali (`/health`, `/webhook`).
178
+ - **`silentPaths`**: il path viene bloccato su Cloudflare ma non viene scritto sul file di log. Usato per path che generano 404 legittimi frequenti (`/auth/me`, `/auth/refresh`).
179
+
180
+ ---
181
+
182
+ ## License
183
+
184
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -66,7 +66,7 @@ let AttacksService = class AttacksService {
66
66
  return;
67
67
  }
68
68
  const auth = Buffer.from(`${user}:${password}`).toString("base64");
69
- const message = `🚨 IP bannato su Cloudflare\nIP: ${rawIp}\nSubnet bloccata: ${cidr}`;
69
+ const message = `🚨 IP bannato su Cloudflare\nProvenienza: ${this.options.comment}\nIP: ${rawIp}\nSubnet bloccata: ${cidr}`;
70
70
  (0, rxjs_1.firstValueFrom)(this.httpService.post(`${url}/send/message`, { phone: wa.phone, message }, { headers: { Authorization: `Basic ${auth}`, "X-Device-Id": deviceId } })).catch((err) => {
71
71
  const msg = err instanceof Error ? err.message : "Errore sconosciuto";
72
72
  this.logger.warn(`Notifica WhatsApp fallita: ${msg}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liberstudio/cloudflare-list",
3
- "version": "2.3.9",
3
+ "version": "2.4.0",
4
4
  "description": "Modulo NestJS per gestione IP List Cloudflare",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",