Software /
code /
prosody-modules
Comparison
mod_firewall/README.md @ 6209:d611ed13df7e draft
Merge
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Tue, 18 Mar 2025 00:16:25 +0700 |
parent | 5862:mod_firewall/README.markdown@1ae8f7b3787a |
comparison
equal
deleted
inserted
replaced
6208:e20901443eae | 6209:d611ed13df7e |
---|---|
1 --- | |
2 labels: | |
3 - 'Stage-Alpha' | |
4 summary: 'A rule-based stanza filtering module' | |
5 rockspec: | |
6 build: | |
7 modules: | |
8 mod_firewall.actions: actions.lib.lua | |
9 mod_firewall.conditions: conditions.lib.lua | |
10 mod_firewall.definitions: definitions.lib.lua | |
11 mod_firewall.marks: marks.lib.lua | |
12 mod_firewall.test: test.lib.lua | |
13 copy_directories: | |
14 - scripts | |
15 --- | |
16 | |
17 ------------------------------------------------------------------------ | |
18 | |
19 **Note:** mod\_firewall is in its very early stages. This documentation | |
20 is liable to change, and some described functionality may be missing, | |
21 incomplete or contain bugs. | |
22 | |
23 ------------------------------------------------------------------------ | |
24 | |
25 Introduction | |
26 ============ | |
27 | |
28 A firewall is an invaluable tool in the sysadmin's toolbox. However | |
29 while low-level firewalls such as iptables and pf are incredibly good at | |
30 what they do, they are generally not able to handle application-layer | |
31 rules. | |
32 | |
33 The goal of mod\_firewall is to provide similar services at the XMPP | |
34 layer. Based on rule scripts it can efficiently block, bounce, drop, | |
35 forward, copy, redirect stanzas and more! Furthermore all rules can be | |
36 applied and updated dynamically at runtime without restarting the | |
37 server. | |
38 | |
39 Details | |
40 ======= | |
41 | |
42 mod\_firewall loads one or more scripts, and compiles these to Lua code | |
43 that reacts to stanzas flowing through Prosody. The firewall script | |
44 syntax is unusual, but straightforward. | |
45 | |
46 A firewall script is dominated by rules. Each rule has two parts: | |
47 conditions, and actions. When a stanza matches all of the conditions, | |
48 all of the actions are executed in order. | |
49 | |
50 Here is a simple example to block stanzas from spammer@example.com: | |
51 | |
52 FROM: spammer@example.com | |
53 DROP. | |
54 | |
55 FROM is a condition, and DROP is an action. This is about as simple as | |
56 it gets. How about heading to the other extreme? Let's demonstrate | |
57 something more complex that mod\_firewall can do for you: | |
58 | |
59 %ZONE myorganisation: staff.myorg.example, support.myorg.example | |
60 | |
61 ENTERING: myorganisation | |
62 KIND: message | |
63 TIME: 12am-9am, 5pm-12am, Saturday, Sunday | |
64 REPLY=Sorry, I am afraid our office is closed at the moment. If you need assistance, please call our 24-hour support line on 123-456-789. | |
65 | |
66 This rule will reply with a short message whenever someone tries to send | |
67 a message to someone at any of the hosts defined in the 'myorganisation' | |
68 outside of office hours. | |
69 | |
70 Specifying rule sets | |
71 -------------------- | |
72 | |
73 Firewall rules should be written into text files, e.g. `ruleset.pfw` file. | |
74 One or more rule files can be specified in the configuration using: | |
75 | |
76 firewall_scripts = { "path/to/ruleset.pfw", "path/to/ruleset2.pfw" } | |
77 | |
78 If multiple files are specified and they both add rules to the same [chains](#chains), | |
79 each file's rules will be processed in order, but the order of files is undefined. | |
80 | |
81 Reloading Prosody's configuration also reloads firewall rules. | |
82 | |
83 Make sure that `firewall_scripts` is in the global section of the configuration file | |
84 and not below a virtual host or a component - unless you want per-vhost | |
85 firewall rules. | |
86 | |
87 Conditions | |
88 ---------- | |
89 | |
90 All conditions must come before any action in a rule block. The | |
91 condition name is followed by a colon (':'), and the value to test for. | |
92 | |
93 A condition can be preceded or followed by `NOT` to negate its match. | |
94 For example: | |
95 | |
96 NOT FROM: user@example.com | |
97 KIND NOT: message | |
98 | |
99 Some conditions do not take parameters, and these should end with just a | |
100 question mark, like: | |
101 | |
102 IN ROSTER? | |
103 | |
104 ### Zones | |
105 | |
106 A 'zone' is one or more hosts or JIDs. It is possible to match when a | |
107 stanza is entering or leaving a zone, while at the same time not | |
108 matching traffic passing between JIDs in the same zone. | |
109 | |
110 Zones are defined at the top of a script with the following syntax (they | |
111 are not part of a rule block): | |
112 | |
113 %ZONE myzone: host1, host2, user@host3, foo.bar.example | |
114 | |
115 There is an automatic zone named `$local`, which automatically includes | |
116 all of the current server's active hosts (including components). It can | |
117 be used to match stanzas entering or leaving the current server. | |
118 | |
119 A host listed in a zone also matches all users on that host (but not | |
120 subdomains). | |
121 | |
122 The following zone-matching conditions are supported: | |
123 | |
124 Condition Matches | |
125 ------------ ------------------------------------------ | |
126 `ENTERING` When a stanza is entering the named zone | |
127 `LEAVING` When a stanza is leaving the named zone | |
128 | |
129 ### Lists | |
130 | |
131 It is possible to create or load lists of strings for use in scripts. For | |
132 example, you might load a list of blocked JIDs, malware URLs or simple words | |
133 that you want to filter messages on. | |
134 | |
135 List type Example | |
136 ----------- ----------------------- | |
137 memory %LIST spammers: memory | |
138 file %LIST spammers: file:/etc/spammers.txt | |
139 http %LIST spammers: http://example.com/spammers.txt | |
140 | |
141 #### List types | |
142 ##### memory | |
143 | |
144 ``` | |
145 %LIST name: memory (limit: number) | |
146 ``` | |
147 | |
148 A memory-only list, with an optional limit. Supports addition and removal of items by scripts. | |
149 | |
150 If a limit is provided, the oldest item will be discarded to make room for a new item if the | |
151 list is full. The limit is useful to prevent infinite memory growth on busy servers. | |
152 | |
153 ##### file | |
154 | |
155 ``` | |
156 %LIST name: file:/path/to/file (missing: string) | |
157 ``` | |
158 | |
159 Reads a list from a file. The list can be added to and removed from by scripts, but | |
160 these changes do not persist between restarts. | |
161 | |
162 If the file is missing, an error will be raised. The optional 'missing' parameter can be set | |
163 to 'ignore' (e.g. `(missing: ignore)`) to ignore a missing file. | |
164 | |
165 ##### http | |
166 | |
167 ``` | |
168 %LIST name: http://example.com/ (ttl: number, pattern: pat, hash: sha1, checkcerts: when-sni) | |
169 ``` | |
170 | |
171 Fetches a list from a HTTP or HTTPS URL. The following options are accepted: | |
172 | |
173 Option Description | |
174 ------- ----------- | |
175 ttl Seconds to cache the list for. After expiry, it will be refetched. Default 3600 (1 hour). | |
176 pattern Optional pattern used to extract list entries from the response. Default is to treat each line as a single item. | |
177 hash Optional hash to be applied to items before looking them up in the list, e.g. sha1 or sha256. | |
178 checkcert Whether to verify HTTPS certificates. May be "always", "never" or "when-sni". Default "when-sni". | |
179 | |
180 The "when-sni" default disables certificate verification when Prosody's HTTP client API doesn't support SNI, | |
181 as in Prosody 0.11.6 and earlier. | |
182 | |
183 #### CHECK LIST | |
184 | |
185 Checks whether a simple [expression](#expressions) is found in a given list. | |
186 | |
187 Example: | |
188 | |
189 %LIST blocked_jids: file:/etc/prosody/blocked_jids.txt | |
190 | |
191 # Rule to block presence subscription requests from blocked JIDs | |
192 KIND: presence | |
193 TYPE: subscribe | |
194 CHECK LIST: blocked_jids contains $<@from> | |
195 BOUNCE=policy-violation (Your JID is blocked) | |
196 | |
197 #### SCAN | |
198 | |
199 SCAN allows you to search inside a stanza for a given pattern, and check each result against a list. For example, | |
200 you could scan a message body for words and check if any of the words are found in a given list. | |
201 | |
202 Before using SCAN, you need to define a search location and a pattern. The search location uses the same 'path' | |
203 format as documented under the 'INSPECT' condition. Patterns can be any valid Lua pattern. | |
204 | |
205 To use the above example: | |
206 | |
207 # Define a search location called 'body' which fetches the text of the 'body' element | |
208 %SEARCH body: body# | |
209 # Define a pattern called 'word' which matches any sequence of letters | |
210 %PATTERN word: [A-Za-z]+ | |
211 # Finally, we also need our list of "bad" words: | |
212 %LIST badwords: file:/etc/prosody/bad_words.txt | |
213 | |
214 # Now we can use these to SCAN incoming stanzas | |
215 # If it finds a match, bounce the stanza | |
216 SCAN: body for word in badwords | |
217 BOUNCE=policy-violation (This word is not allowed!) | |
218 | |
219 #### COUNT | |
220 | |
221 COUNT is similar to SCAN, in that it uses a defined SEARCH and breaks it up according to a PATTERN. Then it | |
222 counts the number of results. | |
223 | |
224 For example, to block every message with more than one URL: | |
225 | |
226 # Define a search location called 'body' which fetches the text of the 'body' element | |
227 %SEARCH body: body# | |
228 # Define a pattern called 'url' which matches HTTP links | |
229 %PATTERN url: https?://%S+ | |
230 | |
231 COUNT: url in body > 1 | |
232 BOUNCE=policy-violation (Up to one HTTP URL is allowed in messages) | |
233 | |
234 ### Stanza matching | |
235 | |
236 Condition Matches | |
237 ----------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ | |
238 `KIND` The kind of stanza. May be 'message', 'presence' or 'iq' | |
239 `TYPE` The type of stanza. This varies depending on the kind of stanza. See 'Stanza types' below for more information. | |
240 `PAYLOAD` The stanza contains a child with the given namespace. Useful for determining the type of an iq request, or whether a message contains a certain extension. | |
241 `INSPECT` The node at the specified path exists or matches a given string. This allows you to look anywhere inside a stanza. See below for examples and more. | |
242 | |
243 #### Stanza types | |
244 | |
245 Stanza Valid types | |
246 ---------- ------------------------------------------------------------------------------------------ | |
247 iq get, set, result, error | |
248 presence *available*, unavailable, probe, subscribe, subscribed, unsubscribe, unsubscribed, error | |
249 message normal, chat, groupchat, headline, error | |
250 | |
251 **Note:** The type 'available' for presence does not actually appear in | |
252 the protocol. Available presence is signalled by the omission of a type. | |
253 Similarly, a message stanza with no type is equivalent to one of type | |
254 'normal'. mod\_firewall handles these cases for you automatically. | |
255 | |
256 #### Sender/recipient matching | |
257 | |
258 Condition Matches | |
259 --------------- ------------------------------------------------------- | |
260 `FROM` The JID in the 'from' attribute matches the given JID. | |
261 `TO` The JID in the 'to' attribute matches the given JID. | |
262 `TO SELF` The stanza is sent by any of a user's resources to their own bare JID. | |
263 `TO FULL JID` The stanza is addressed to a **valid** full JID on the local server (full JIDs include a resource at the end, and only exist for the lifetime of a single session, therefore the recipient **must be online**, or this check will not match). | |
264 `FROM FULL JID` The stanza is from a full JID (unlike `TO FULL JID` this check is on the format of the JID only). | |
265 | |
266 The TO and FROM conditions both accept wildcards in the JID when it is | |
267 enclosed in angle brackets ('\<...\>'). For example: | |
268 | |
269 # All users at example.com | |
270 FROM: <*>@example.com | |
271 | |
272 # The user 'admin' on any subdomain of example.com | |
273 FROM: admin@<*.example.com> | |
274 | |
275 You can also use [Lua's pattern | |
276 matching](http://www.lua.org/manual/5.1/manual.html#5.4.1) for more | |
277 powerful matching abilities. Patterns are a lightweight | |
278 regular-expression alternative. Simply contain the pattern in double | |
279 angle brackets. The pattern is automatically anchored at the start and | |
280 end (so it must match the entire portion of the JID). | |
281 | |
282 # Match admin@example.com, and admin1@example.com, etc. | |
283 FROM: <<admin%d*>>@example.com | |
284 | |
285 **Note:** It is important to know that 'example.com' is a valid JID on | |
286 its own, and does **not** match 'user@example.com'. To efficiently match | |
287 domains we recommend defining them as [Zones](#zones). | |
288 | |
289 Condition Matches | |
290 ---------------- --------------------------------------------------------------- | |
291 `FROM_EXACTLY` The JID in the 'from' attribute exactly matches the given JID | |
292 `TO_EXACTLY` The JID in the 'to' attribute exactly matches the given JID | |
293 | |
294 These additional conditions do not support pattern matching, but are | |
295 useful to match the exact to/from address on a stanza. For example, if | |
296 no resource is specified then only bare JIDs will be matched. TO and FROM | |
297 match all resources if no resource is specified to match. | |
298 | |
299 **Note:** Some chains execute before Prosody has performed any | |
300 normalisation or validity checks on the to/from JIDs on an incoming | |
301 stanza. It is not advisable to perform access control or similar rules | |
302 on JIDs in these chains (see the [chain documentation](#chains) for more info). | |
303 | |
304 #### GeoIP matching | |
305 | |
306 Condition Matches | |
307 ---------------- -------------------------------------------------------------- | |
308 `FROM COUNTRY` Two or three letter country code looked up in GeoIP database | |
309 | |
310 This condition uses a GeoIP database to look up the origin country of | |
311 the IP attached to the current session. | |
312 | |
313 For example: | |
314 | |
315 # 3 letter country code | |
316 FROM COUNTRY: SWE | |
317 | |
318 # or 2 letter | |
319 FROM COUNTRY: SE | |
320 | |
321 # Explicit | |
322 FROM COUNTRY: code=SE | |
323 FROM COUNTRY: code3=SWE | |
324 | |
325 **Note:** This requires that the `lua-geoip` and `geoip-database` | |
326 packages are installed (on Debian, package names may differ on other | |
327 operating systems). | |
328 | |
329 #### INSPECT | |
330 | |
331 INSPECT takes a 'path' through the stanza to get a string (an attribute | |
332 value or text content). An example is the best way to explain. Let's | |
333 check that a user is not trying to register an account with the username | |
334 'admin'. This stanza comes from [XEP-0077: In-band | |
335 Registration](http://xmpp.org/extensions/xep-0077.html#example-4): | |
336 | |
337 ``` xml | |
338 <iq type='set' id='reg2'> | |
339 <query xmlns='jabber:iq:register'> | |
340 <username>bill</username> | |
341 <password>Calliope</password> | |
342 <email>bard@shakespeare.lit</email> | |
343 </query> | |
344 </iq> | |
345 ``` | |
346 | |
347 KIND: iq | |
348 TYPE: set | |
349 PAYLOAD: jabber:iq:register | |
350 INSPECT: {jabber:iq:register}query/username#=admin | |
351 BOUNCE=not-allowed (The username 'admin' is reserved.) | |
352 | |
353 That weird string deserves some explanation. It is a path, divided into | |
354 segments by '/'. Each segment describes an element by its name, | |
355 optionally prefixed by its namespace in curly braces ('{...}'). If the | |
356 path ends with a '\#' then the text content of the last element will be | |
357 returned. If the path ends with '@name' then the value of the attribute | |
358 'name' will be returned. | |
359 | |
360 You can use INSPECT to test for the existence of an element or attribute, | |
361 or you can check if it matches a specific value, e.g. by appending `=VALUE` | |
362 (like in the example above, that checks if the content of username is 'admin'). | |
363 | |
364 #### INSPECT comparison operators | |
365 | |
366 As well as checking for an exact string match, there are some other modifiers | |
367 you can apply to the comparison: | |
368 | |
369 Comparison Matches when | |
370 ------------- ------------------------------------------------------- | |
371 `=` The value is exactly the given string. | |
372 `/=` The value is or *contains* the given string (e.g. `/=admin` would match `administrator` or `myadmin`). | |
373 `~=` The value matches the given [Lua pattern](https://www.lua.org/manual/5.2/manual.html#6.4.1). | |
374 | |
375 Finally, if the comparison operator is preceded by a `$` character, [expressions](#expressions) | |
376 will be interpreted in the string following the comparison operator. | |
377 | |
378 e.g. `INSPECT: {jabber:iq:register}query/username}$/=$(session.host)` would match | |
379 if the username of an account registration contained the session's current hostname | |
380 somewhere in it. | |
381 | |
382 #### INSPECT performance | |
383 | |
384 INSPECT can be somewhat slower than the other stanza matching conditions. To | |
385 minimise performance impact, always place it below other faster | |
386 condition checks where possible (e.g. in the example above we first checked KIND, | |
387 TYPE and PAYLOAD matched what we wanted before reaching the INSPECT rule). | |
388 | |
389 ### Roster | |
390 | |
391 These conditions access the roster of the recipient (only). Therefore they cannot (currently) | |
392 be used in some [chains](#chains), such as for outgoing messages (the recipient may be on another server). | |
393 | |
394 Performance note: these checks can potentially cause storage access (especially if the recipient | |
395 is currently offline), so you may want to limit their use in high-traffic situations, and place rules | |
396 containing a roster check below other rules (such as a rate limiter). The storage access is | |
397 performed unconditionally just before evaluation of the first rule that contains a roster-based | |
398 condition, even if earlier conditions in that rule do not match. | |
399 | |
400 #### IN ROSTER | |
401 | |
402 Tests whether the sender is in the recipient's roster. | |
403 | |
404 IN ROSTER? | |
405 | |
406 #### IN ROSTER GROUP | |
407 | |
408 Tests whether the sender is in the recipient's roster, and in the named group. | |
409 | |
410 IN ROSTER GROUP: Friends | |
411 | |
412 #### SUBSCRIBED | |
413 | |
414 Tests whether the recipient is subscribed to the sender, ie will receive | |
415 presence updates from them. | |
416 | |
417 Note that this *does* work, regardless of direction and which [chain](#chain) is | |
418 used, since both the sender and the recipient will have mirrored roster | |
419 entries. | |
420 | |
421 ### Groups | |
422 | |
423 Using Prosody's mod\_groups it is possible to define groups of users on the server. You can | |
424 match based on these groups in firewall rules. | |
425 | |
426 Condition Matches | |
427 ----------------- ---------------------------- | |
428 `FROM GROUP` When the stanza is being sent from a member of the named group | |
429 `TO GROUP` When the stanza is being sent to a member of the named group | |
430 `CROSSING GROUPS` When the stanza is being sent between users of different named groups | |
431 | |
432 #### CROSSING GROUPS | |
433 | |
434 The `CROSSING GROUPS` condition takes a comma-separated list of groups to check. If the | |
435 sender and recipient are not in the same group (only the listed groups are checked), then the | |
436 this condition matches and the stanza is deemed to be crossing between groups. | |
437 | |
438 For example, if you had three groups: Engineering, Marketing and Employees. All users are | |
439 members of the 'Employees' group, and the others are for employees of the named department only. | |
440 | |
441 To prevent employees in the marketing department from communicating with engineers, you could use | |
442 the following rule: | |
443 | |
444 ``` | |
445 CROSSING GROUPS: Marketing, Engineering | |
446 BOUNCE=policy-violation (no communication between these groups is allowed!) | |
447 ``` | |
448 | |
449 This works, even though both the users are in the 'Employees' group, because that group is not listed | |
450 in the condition. | |
451 | |
452 In the above example, a user who is member of both groups is not restricted. | |
453 | |
454 #### SENT DIRECTED PRESENCE TO SENDER | |
455 | |
456 This condition matches if the recipient of a stanza has previously sent directed presence to the sender of the stanza. This | |
457 is often done in XMPP to exchange presence information with JIDs that are not on your roster, such as MUC rooms. | |
458 | |
459 This condition does not take a parameter - end the condition name with a question mark: | |
460 | |
461 # Rule to bounce messages from senders not in the roster who haven't been sent directed presence | |
462 NOT IN ROSTER? | |
463 NOT SENT DIRECTED PRESENCE TO SENDER? | |
464 BOUNCE=service-unavailable | |
465 | |
466 ### Permissions | |
467 | |
468 Rules can consult Prosody's internal role and permissions system to check whether a certain action may | |
469 be performed. The acting entity, their role, and appropriate context is automatically inferred. All you | |
470 need to do is provide the identifier of the permission that should be checked. | |
471 | |
472 Condition Description | |
473 ----------------------- -------------------------------------------------------------------- | |
474 `MAY=permission` Checks whether 'permission' is allowed in the current context. | |
475 | |
476 As with all other conditions, `MAY` can be combined with `NOT` to negate the result of the check. | |
477 | |
478 Example, blocking outgoing stanzas from users with roles that do not allow the 'xmpp:federate' permission: | |
479 | |
480 ``` | |
481 ::deliver_remote | |
482 MAY NOT: xmpp:federate | |
483 BOUNCE=policy-violation (You are not allowed access to the federation) | |
484 ``` | |
485 | |
486 ### Roles | |
487 | |
488 Condition Matches | |
489 ---------------- ------------------------------------------------------------------------------------- | |
490 `TO ROLE` When the recipient JID of the stanza has the named role | |
491 `FROM ROLE` When the sender JID of the stanza has the named role | |
492 | |
493 **Note:** In most cases, you should avoid checking for specific roles, and instead check for | |
494 permissions granted by those roles (using the 'MAY' condition). | |
495 | |
496 ### Admins | |
497 | |
498 **Deprecated:** These conditions should no longer be used. Prefer 'MAY', 'TO ROLE' or 'FROM ROLE'. | |
499 | |
500 Prosody allows certain JIDs to be declared as administrators of a host, component or the whole server. | |
501 | |
502 Condition Matches | |
503 ---------------- ------------------------------------------------------------------------------------- | |
504 `TO ADMIN` When the recipient of the stanza is admin of the current host | |
505 `FROM ADMIN` When the sender of the stanza is admin of the current host | |
506 `FROM ADMIN OF` When the sender of the stanza is an admin of the named host on the current server | |
507 `TO ADMIN OF` When the recipient of the stanza is an admin of the named host on the current server | |
508 | |
509 ### Time and date | |
510 | |
511 #### TIME | |
512 | |
513 Matches stanzas sent during certain time periods. | |
514 | |
515 Condition Matches | |
516 ----------- ------------------------------------------------------------------------------------------- | |
517 TIME When the current server local time is within one of the comma-separated time ranges given | |
518 | |
519 TIME: 10pm-6am, 14:00-15:00 | |
520 REPLY=Zzzz. | |
521 | |
522 #### DAY | |
523 | |
524 It is also possible to match only on certain days of the week. | |
525 | |
526 Condition Matches | |
527 ----------- ----------------------------------------------------------------------------------------------------- | |
528 DAY When the current day matches one, or falls within a rage, in the given comma-separated list of days | |
529 | |
530 Example: | |
531 | |
532 DAY: Sat-Sun, Wednesday | |
533 REPLY=Sorry, I'm out enjoying life! | |
534 | |
535 All times and dates are handled in the server's local time. | |
536 | |
537 ### Rate-limiting | |
538 | |
539 It is possible to selectively rate-limit stanzas, and use rules to | |
540 decide what to do with stanzas when over the limit. | |
541 | |
542 First, you must define any rate limits that you are going to use in your | |
543 script. Here we create a limiter called 'normal' that will allow 2 | |
544 stanzas per second, and then we define a rule to bounce messages when | |
545 over this limit. Note that the `RATE` definition is not part of a rule | |
546 (multiple rules can share the same limiter). | |
547 | |
548 %RATE normal: 2 (burst 3) | |
549 | |
550 KIND: message | |
551 LIMIT: normal | |
552 BOUNCE=policy-violation (Sending too fast!) | |
553 | |
554 The 'burst' parameter on the rate limit allows you to spread the limit | |
555 check over a given time period. For example the definition shown above | |
556 will allow the limit to be temporarily surpassed, as long as it is | |
557 within the limit after 3 seconds. You will almost always want to specify | |
558 a burst factor. | |
559 | |
560 Both the rate and the burst can be fractional values. For example a rate | |
561 of 0.1 means only one event is allowed every 10 seconds. | |
562 | |
563 The LIMIT condition actually does two things; first it counts against | |
564 the given limiter, and then it checks to see if the limiter over its | |
565 limit yet. If it is, the condition matches, otherwise it will not. | |
566 | |
567 Condition Matches | |
568 ----------- -------------------------------------------------------------------------------------------------- | |
569 `LIMIT` When the named limit is 'used up'. Using this condition automatically counts against that limit. | |
570 | |
571 **Note:** Reloading mod\_firewall resets the current state of any | |
572 limiters. | |
573 | |
574 #### Dynamic limits | |
575 | |
576 Sometimes you may want to have multiple throttles in a single condition, using some property of the session or stanza | |
577 to determine which throttle to use. For example, you might have a limit for incoming stanzas, but you want to limit by | |
578 sending JID, instead of all incoming stanzas sharing the same limit. | |
579 | |
580 You can use the 'on' keyword for this, like so: | |
581 | |
582 LIMIT: normal on EXPRESSION | |
583 | |
584 For more information on [expressions](#expressions), see the section later in this document. | |
585 | |
586 Each value of 'EXPRESSION' has to be tracked individually in a table, which uses a small amount of memory. To prevent | |
587 memory exhaustion, the number of tracked values is limited to 1000 by default. You can override this by setting the | |
588 maximum number of table entries when you define the rate: | |
589 | |
590 %RATE normal: 2 (burst 3) (entries 4096) | |
591 | |
592 Old values are automatically removed from the tracking table. However if the tracking table becomes full, new entries | |
593 will be rejected - it will behave as if the rate limit was reached, even for values that have not been seen before. Since | |
594 this opens up a potential denial of service (innocent users may be affected if malicious users can fill up the tracking | |
595 table within the limit period). You can choose to instead "fail open", and allow the rate limit to be temporarily bypassed | |
596 when the table is full. To choose this behaviour, add `(allow overflow)` to the RATE definition. | |
597 | |
598 ### Session marking | |
599 | |
600 It is possible to 'mark' sessions (see the MARK_ORIGIN action below). To match stanzas from marked sessions, use the | |
601 `ORIGIN_MARKED` condition. | |
602 | |
603 Condition Description | |
604 ------------------------------- --------------------------------------------------------------- | |
605 ORIGIN MARKED: markname Matches if the origin has been marked with 'markname'. | |
606 ORIGIN MARKED: markname (Xs) Matches if the origin has been marked with 'markname' within the past X seconds. | |
607 | |
608 Example usage: | |
609 | |
610 # This rule drops messages from sessions that have been marked as spammers in the past hour | |
611 ORIGIN MARKED: spammer (3600s) | |
612 DROP. | |
613 | |
614 # This rule marks the origin session as a spammer if they send a message to a honeypot JID | |
615 KIND: message | |
616 TO: honeypot@example.com | |
617 MARK ORIGIN=spammer | |
618 | |
619 Actions | |
620 ------- | |
621 | |
622 Actions come after all conditions in a rule block. There must be at | |
623 least one action, though conditions are optional. | |
624 | |
625 An action without parameters ends with a full-stop/period ('.'), and one | |
626 with parameters uses an equals sign ('='): | |
627 | |
628 # An action with no parameters: | |
629 DROP. | |
630 | |
631 # An action with a parameter: | |
632 REPLY=Hello, this is a reply. | |
633 | |
634 ### Route modification | |
635 | |
636 The following common actions modify the stanza's route in some way. These | |
637 rules will halt further processing of the stanza - no further actions will be | |
638 executed, and no further rules will be checked. | |
639 | |
640 Action Description | |
641 ----------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- | |
642 `PASS.` Stop executing actions and rules on this stanza, and let it through this chain and any calling chains. | |
643 `DROP.` Stop executing actions and rules on this stanza, and discard it. | |
644 `DEFAULT.` Stop executing actions and rules on this stanza, prevent any other scripts/modules from handling it, to trigger the appropriate default "unhandled stanza" behaviour. Do not use in custom chains (it is treated as PASS). | |
645 `REDIRECT=jid` Redirect the stanza to the given JID. | |
646 `BOUNCE.` Bounce the stanza with the default error (usually service-unavailable) | |
647 `BOUNCE=error` Bounce the stanza with the given error (MUST be a defined XMPP stanza error, see [RFC6120](http://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions). | |
648 `BOUNCE=error (text)` As above, but include the supplied human-readable text with a description of the error | |
649 | |
650 **Note:** It is incorrect behaviour to reply to an 'error' stanza with another error, so BOUNCE will simply act the same as 'DROP' for stanzas that should not be bounced (error stanzas and iq results). | |
651 | |
652 ### Replying and forwarding | |
653 | |
654 These actions cause a new stanza to be generated and sent somewhere. | |
655 Processing of the original stanza will continue beyond these actions. | |
656 | |
657 Action Description | |
658 ------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------- | |
659 `REPLY=text` Reply to the stanza (assumed to be a message) with the given text. | |
660 `COPY=jid` Make a copy of the stanza and send the copy to the specified JID. The copied stanza flows through Prosody's routing code, and as such is affected by firewall rules. Be careful to avoid loops. | |
661 `FORWARD=jid` Forward a copy of the stanza to the given JID (using XEP-0297). The stanza will be sent from the current host's JID. | |
662 | |
663 ### Reporting | |
664 | |
665 Action Description | |
666 ------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- | |
667 `REPORT TO=jid [reason] [text]` Forwards the full stanza to `jid` with a XEP-0377 abuse report attached. | |
668 | |
669 Only the `jid` is mandatory. The `reason` parameter should be either `abuse`, `spam` or a custom URI. If not specified, it defaults to `abuse`. | |
670 After the reason, some human-readable text may be included to explain the report. | |
671 | |
672 Example: | |
673 | |
674 ``` | |
675 KIND: message | |
676 TO: honeypot@example.com | |
677 REPORT TO=antispam.example.com spam Caught by the honeypot! | |
678 DROP. | |
679 ``` | |
680 | |
681 ### Stanza modification | |
682 | |
683 These actions make it possible to modify the content and structure of a | |
684 stanza. | |
685 | |
686 Action Description | |
687 ------------------------ ------------------------------------------------------------------------ | |
688 `STRIP=name` Remove any child elements with the given name in the default namespace | |
689 `STRIP=name namespace` Remove any child elements with the given name and the given namespace | |
690 `INJECT=xml` Inject the given XML into the stanza as a child element | |
691 | |
692 ### Sessions | |
693 | |
694 It is possible to mark sessions, and then use these marks to match rules later on. | |
695 | |
696 Action Description | |
697 ------------------------ -------------------------------------------------------------------------- | |
698 `MARK ORIGIN=mark` Marks the originating session with the given flag. | |
699 `UNMARK ORIGIN=mark` Removes the given mark from the origin session (if it is set). | |
700 | |
701 **Note:** Marks apply to sessions, not JIDs. E.g. if marking in a rule that matches a stanza received | |
702 over s2s, it is the s2s session that is marked. | |
703 | |
704 It is possible to have multiple marks on an origin at any given time. | |
705 | |
706 ### Informational | |
707 | |
708 Action Description | |
709 --------------- ------------------------------------------------------------------------------------------------------------------------ | |
710 `LOG=message` Logs the given message to Prosody's log file. Optionally prefix it with a log level in square brackets, e.g. `[debug]` | |
711 | |
712 You can include [expressions](#expressions) in log messages, using `$(...)` syntax. For example, to log the stanza that matched the rule, | |
713 you can use `$(stanza)`, or to log just the top tag of the stanza, use `$(stanza:top_tag())`. To fetch the sender JID, use `$(stanza.attr.from)`. | |
714 | |
715 Example: | |
716 | |
717 # Log all stanzas to user@example.com: | |
718 TO: user@example.com | |
719 LOG=[debug] User received: $(stanza) | |
720 | |
721 More info about expressions can be found below. | |
722 | |
723 Chains | |
724 ------ | |
725 | |
726 Rules are grouped into "chains", which are injected at particular points in Prosody's routing code. | |
727 | |
728 Available built-in chains are: | |
729 | |
730 Chain Description | |
731 -------------- ------------------------------------------------------------------------------------------- | |
732 deliver Applies to stanzas delivered to local recipients (regardless of the stanza's origin) | |
733 deliver_remote Applies to stanzas delivered to remote recipients (just before they leave the local server) | |
734 preroute Applies to incoming stanzas from local users, before any routing rules are applied | |
735 | |
736 A chain is begun by a line `::name` where 'name' is the name of the chain you want the following rules to be | |
737 inserted into. If no chain is specified, rules are put into the 'deliver' chain. | |
738 | |
739 It is possible to create custom chains (useful with the `JUMP CHAIN` action described below). User-created | |
740 chains must begin with "user/", e.g. "user/spam_filtering". | |
741 | |
742 Example of chain use: | |
743 | |
744 # example.com's firewall script | |
745 | |
746 # This line is optional, because 'deliver' is the default chain anyway: | |
747 ::deliver | |
748 | |
749 # This rule matches any stanzas delivered to our local user bob: | |
750 TO: bob@example.com | |
751 DROP. | |
752 | |
753 # Oops! This rule will never match, because alice is not a local user, | |
754 # and only stanzas to local users go through the 'deliver' chain: | |
755 TO: alice@remote.example.com | |
756 DROP. | |
757 | |
758 # Create a 'preroute' chain of rules (matched for incoming stanzas from local clients): | |
759 ::preroute | |
760 # These rules are matched for outgoing stanzas from local clients | |
761 | |
762 # This will match any stanzas sent to alice from a local user: | |
763 TO: alice@remote.example.com | |
764 DROP. | |
765 | |
766 Action Description | |
767 ------------------------ ------------------------------------------------------------------------ | |
768 `JUMP CHAIN=name` Switches chains, and passes the stanza through the rules in chain 'name'. If the new chain causes the stanza to be dropped/redirected, the current chain halts further processing. | |
769 `RETURN.` Stops executing the current chain and returns to the parent chain. For built-in chains, equivalent to PASS. RETURN is implicit at the end of every chain. | |
770 | |
771 It is possible to jump to chains defined by other scripts and modules. | |
772 | |
773 Expressions | |
774 ----------- | |
775 | |
776 Some conditions and actions in rules support "expressions" in their parameters (their documentation will indicate if this is the case). Most parameters | |
777 are static once the firewall script is loaded and compiled internally, however parameters that allow expressions can be dynamically calculated when a | |
778 rule is being run. | |
779 | |
780 There are two kinds of expression that you can use: stanza expressions, and code expressions. | |
781 | |
782 ### Stanza expressions | |
783 | |
784 Stanza expressions are of the form `$<...>`, where `...` is a stanza path. For syntax of stanza paths, see the documentation for the 'INSPECT' condition | |
785 above. | |
786 | |
787 Example: | |
788 | |
789 LOG=Matched a stanza from $<@from> to $<@to> | |
790 | |
791 There are built in functions which can be applied to the output of a stanza expression, by appending the pipe ('|') operator, followed by the function | |
792 name. These functions are: | |
793 | |
794 Function Description | |
795 ------------ --------------------------------------- | |
796 bare Given a JID, strip any resource | |
797 node Return the node ('user part') of a JID | |
798 host Return the host ('domain') part of a JID | |
799 resource Return the resource part of a JID | |
800 | |
801 For example, to apply a rate limit to stanzas per sender domain: | |
802 | |
803 LIMIT normal on $<@from|host> | |
804 | |
805 If the path does not match (e.g. the element isn't found, or the attribute doesn't exist) or any of the functions fail to produce an output (e.g. an invalid | |
806 JID was passed to a function that only handles valid JIDs) the expression will return the text `<undefined>`. You can override this by ending the expression | |
807 with a double pipe ('||') followed by a quoted string to use as a default instead. E.g. to default to the string "normal" when there is no 'type' attribute: | |
808 | |
809 LOG=Stanza type is $<@type||"normal"> | |
810 | |
811 ### Code expressions | |
812 | |
813 Code expressions use `$(...)` syntax. Code expressions are powerful, and allow unconstrained access to Prosody's internal environment. Therefore | |
814 code expressions are typically for advanced use-cases only. You may want to refer to Prosody's [developer documentation](https://prosody.im/doc/developers) | |
815 for more information. In particular, within code expressions you may access the 'session' object, which is the session object of the origin of the stanza, | |
816 and the 'stanza' object, which is the stanza being considered within the current rule. Whatever value the expression returns will be converted to a string. | |
817 | |
818 Example to limit stanzas per session type: | |
819 | |
820 LIMIT: normal on $(session.type) |