mod_firewall/README.wiki
changeset 1786 29f3d6b7ad16
equal deleted inserted replaced
1785:12ac88940fe3 1786:29f3d6b7ad16
       
     1 #summary A rule-based stanza filtering module
       
     2 #labels Stage-Alpha
       
     3 
       
     4 ----
       
     5 
       
     6 *Note:* mod_firewall is in its very early stages. This documentation is liable to change, and some described functionality may be missing, incomplete or contain bugs. Feedback is welcome in the comments section at the bottom of this page.
       
     7 
       
     8 ----
       
     9 
       
    10 = Introduction =
       
    11 
       
    12 A firewall is an invaluable tool in the sysadmin's toolbox. However while low-level firewalls such as iptables and pf are incredibly good at what they do, they are generally not able to handle application-layer rules.
       
    13 
       
    14 The goal of mod_firewall is to provide similar services at the XMPP layer. Based on rule scripts it can efficiently block, bounce, drop, forward, copy, redirect stanzas and more! Furthermore all rules can be applied and updated dynamically at runtime without restarting the server.
       
    15 
       
    16 = Details =
       
    17 
       
    18 mod_firewall loads one or more scripts, and compiles these to Lua code that reacts to stanzas flowing through Prosody. The firewall script syntax is unusual, but straightforward.
       
    19 
       
    20 A firewall script is dominated by rules. Each rule has two parts: conditions, and actions. When a stanza matches all of the conditions, all of the actions are executed in order.
       
    21 
       
    22 Here is a simple example to block stanzas from spammer@example.com:
       
    23 
       
    24 {{{
       
    25 FROM: spammer@example.com
       
    26 DROP.
       
    27 }}}
       
    28 
       
    29 FROM is a condition, and DROP is an action. This is about as simple as it gets. How about heading to the other extreme? Let's demonstrate something more complex that mod_firewall can do for you:
       
    30 
       
    31 {{{
       
    32 %ZONE myorganisation: staff.myorg.example, support.myorg.example
       
    33 
       
    34 ENTERING: myorganisation
       
    35 KIND: message
       
    36 TIME: 12am-9am, 5pm-12am, Saturday, Sunday
       
    37 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.
       
    38 }}}
       
    39 
       
    40 This rule will reply with a short message whenever someone tries to send a message to someone at any of the hosts defined in the 'myorganisation' outside of office hours.
       
    41 
       
    42 Firewall rules should be written to a {{{ruleset.pfw}}} file. Multiple such rule
       
    43 files can be specified in the configuration using:
       
    44 
       
    45 {{{
       
    46 firewall_scripts = { "path/to/ruleset.pfw" }
       
    47 }}}
       
    48 
       
    49 == Conditions ==
       
    50 All conditions must come before any action in a rule block. The condition name is followed by a colon (':'), and the value to test for.
       
    51 
       
    52 A condition can be preceded or followed by `NOT` to negate its match. For example:
       
    53 
       
    54 {{{
       
    55 NOT FROM: user@example.com
       
    56 KIND NOT: message
       
    57 }}}
       
    58 
       
    59 === Zones ===
       
    60 
       
    61 A 'zone' is one or more hosts or JIDs. It is possible to match when a stanza is entering or leaving a zone, while at the same time not matching traffic passing between JIDs in the same zone.
       
    62 
       
    63 Zones are defined at the top of a script with the following syntax (they are not part of a rule block):
       
    64 
       
    65 {{{
       
    66 %ZONE myzone: host1, host2, user@host3, foo.bar.example
       
    67 }}}
       
    68 
       
    69 A host listed in a zone also matches all users on that host (but not subdomains).
       
    70 
       
    71 The following zone-matching conditions are supported:
       
    72 
       
    73 || *Condition* || *Matches* ||
       
    74 || `ENTERING` || When a stanza is entering the named zone ||
       
    75 || `LEAVING` || When a stanza is leaving the named zone ||
       
    76 
       
    77 === Stanza matching ===
       
    78 
       
    79 || *Condition* || *Matches* ||
       
    80 || `KIND` || The kind of stanza. May be 'message', 'presence' or 'iq' ||
       
    81 || `TYPE` || The type of stanza. This varies depending on the kind of stanza. See 'Stanza types' below for more information. ||
       
    82 || `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. ||
       
    83 || `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. ||
       
    84 
       
    85 ==== Stanza types ====
       
    86 
       
    87 || *Stanza* || *Valid types* ||
       
    88 || iq || get, set, result, error ||
       
    89 || presence || _available_, unavailable, probe, subscribe, subscribed, unsubscribe, unsubscribed, error ||
       
    90 || message || normal, chat, groupchat, headline, error ||
       
    91 
       
    92 *Note:* The type 'available' for presence does not actually appear in the protocol. Available presence is signalled by the omission of a type. Similarly, a message stanza with no type is equivalent to one of type 'normal'. mod_firewall handles these cases for you automatically.
       
    93 
       
    94 ==== INSPECT ====
       
    95 
       
    96 INSPECT takes a 'path' through the stanza to get a string (an attribute value or text content). An example is the best way to explain. Let's check that a user is not trying to register an account with the username 'admin'. This stanza comes from [http://xmpp.org/extensions/xep-0077.html#example-4 XEP-0077: In-band Registration]:
       
    97 
       
    98 {{{
       
    99 <iq type='set' id='reg2'>
       
   100   <query xmlns='jabber:iq:register'>
       
   101     <username>bill</username>
       
   102     <password>Calliope</password>
       
   103     <email>bard@shakespeare.lit</email>
       
   104   </query>
       
   105 </iq>
       
   106 }}}
       
   107 
       
   108 {{{
       
   109 KIND: iq
       
   110 TYPE: set
       
   111 PAYLOAD: jabber:iq:register
       
   112 INSPECT: {jabber:iq:register}query/username#=admin
       
   113 BOUNCE=not-allowed The username 'admin' is reserved.
       
   114 }}}
       
   115 
       
   116 That weird string deserves some explanation. It is a path, divided into segments by '/'. Each segment describes an element by its name, optionally prefixed by its namespace in curly braces ('{...}'). If the path ends with a '#' then the text content of the last element will be returned. If the path ends with '@name' then the value of the attribute 'name' will be returned.
       
   117 
       
   118 INSPECT is somewhat slower than the other stanza matching conditions. To minimise performance impact, always place it below other faster condition checks where possible (e.g. above we first checked KIND, TYPE and PAYLOAD matched before INSPECT).
       
   119 
       
   120 === Sender/recipient matching ===
       
   121 
       
   122 || *Condition* || *Matches* ||
       
   123 || `FROM` || The JID in the 'from' attribute matches the given JID ||
       
   124 || `TO`   || The JID in the 'to' attribute matches the given JID ||
       
   125 
       
   126 These conditions both accept wildcards in the JID when the wildcard expression is enclosed in angle brackets ('<...>'). For example:
       
   127 
       
   128 {{{
       
   129 # All users at example.com
       
   130 FROM: <*>@example.com
       
   131 }}}
       
   132 {{{
       
   133 # The user 'admin' on any subdomain of example.com
       
   134 FROM: admin@<*.example.com>
       
   135 }}}
       
   136 
       
   137 You can also use [http://www.lua.org/manual/5.1/manual.html#5.4.1 Lua's pattern matching] for more powerful matching abilities. Patterns are a lightweight regular-expression alternative. Simply contain the pattern in double angle brackets. The pattern is automatically anchored at the start and end (so it must match the entire portion of the JID).
       
   138 
       
   139 {{{
       
   140 # Match admin@example.com, and admin1@example.com, etc.
       
   141 FROM: <<admin%d*>>@example.com
       
   142 }}}
       
   143 
       
   144 *Note:* It is important to know that 'example.com' is a valid JID on its own, and does *not* match 'user@example.com'. To perform domain whitelists or blacklists, use Zones.
       
   145 
       
   146 *Note:* Some chains execute before Prosody has performed any normalisation or validity checks on the to/from JIDs on an incoming stanza. It is not advisable to perform access control or similar rules on JIDs in these chains (see the chain documentation for more info).
       
   147 
       
   148 === Time and date ===
       
   149 ==== TIME ====
       
   150 Matches stanzas sent during certain time periods.
       
   151 || *Condition* || *Matches* ||
       
   152 || TIME || When the current server local time is within one of the comma-separated time ranges given ||
       
   153 
       
   154 {{{
       
   155 TIME: 10pm-6am, 14:00-15:00
       
   156 REPLY=Zzzz.
       
   157 }}}
       
   158 
       
   159 ==== DAY ====
       
   160 It is also possible to match only on certain days of the week.
       
   161 
       
   162 || *Condition* || *Matches* ||
       
   163 || DAY || When the current day matches one, or falls within a rage, in the given comma-separated list of days ||
       
   164 
       
   165 Example:
       
   166 {{{
       
   167 DAY: Sat-Sun, Wednesday
       
   168 REPLY=Sorry, I'm out enjoying life!
       
   169 }}}
       
   170 
       
   171 
       
   172 === Rate-limiting ===
       
   173 It is possible to selectively rate-limit stanzas, and use rules to decide what to do with stanzas when over the limit.
       
   174 
       
   175 First, you must define any rate limits that you are going to use in your script. Here we create a limiter called 'normal' that will allow 2 stanzas per second, and then we define a rule to bounce messages when over this limit. Note that the `RATE` definition is not part of a rule (multiple rules can share the same limiter).
       
   176 
       
   177 {{{
       
   178 %RATE normal: 2 (burst 3)
       
   179 
       
   180 KIND: message
       
   181 LIMIT: normal
       
   182 BOUNCE=policy-violation (Sending too fast!)
       
   183 }}}
       
   184 
       
   185 The 'burst' parameter on the rate limit allows you to spread the limit check over a given time period. For example the definition shown above will allow the limit to be temporarily surpassed, as long as it is within the limit after 3 seconds. You will almost always want to specify a burst factor.
       
   186 
       
   187 Both the rate and the burst can be fractional values. For example a rate of 0.1 means only one event is allowed every 10 seconds.
       
   188 
       
   189 The LIMIT condition actually does two things; first it counts against the given limiter, and then it checks to see if the limiter over its limit yet. If it is, the condition matches, otherwise it will not.
       
   190 
       
   191 || *Condition* || *Matches* ||
       
   192 || `LIMIT` || When the named limit is 'used up'. Using this condition automatically counts against that limit. ||
       
   193 
       
   194 *Note:* Reloading mod_firewall resets the current state of any limiters.
       
   195 
       
   196 == Actions ==
       
   197 Actions come after all conditions in a rule block. There must be at least one action, though conditions are optional.
       
   198 
       
   199 An action without parameters ends with a full-stop/period ('.'), and one with parameters uses an equals sign ('='):
       
   200 
       
   201 {{{
       
   202 # An action with no parameters:
       
   203 DROP.
       
   204 
       
   205 # An action with a parameter:
       
   206 REPLY=Hello, this is a reply.
       
   207 }}}
       
   208 
       
   209 === Route modification ===
       
   210 The most common actions modify the stanza's route in some way. Currently the first matching rule to do so will halt further processing of actions and rules (this may change in the future).
       
   211 
       
   212 || *Action* || *Description* ||
       
   213 || `PASS.` || Stop executing actions and rules on this stanza, and let it through this chain. ||
       
   214 || `DROP.` || Stop executing actions and rules on this stanza, and discard it. ||
       
   215 || `REDIRECT=jid` || Redirect the stanza to the given JID. ||
       
   216 || `REPLY=text` || Reply to the stanza (assumed to be a message) with the given text. ||
       
   217 || `BOUNCE.` || Bounce the stanza with the default error (usually service-unavailable) ||
       
   218 || `BOUNCE=error` || Bounce the stanza with the given error (MUST be a defined XMPP stanza error, see [http://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions RFC6120]. ||
       
   219 || `BOUNCE=error (text)` || As above, but include the supplied human-readable text with a description of the error ||
       
   220 || `COPY=jid` || Make a copy of the stanza and send the copy to the specified JID. ||
       
   221 
       
   222 === Stanza modification ===
       
   223 These actions make it possible to modify the content and structure of a stanza.
       
   224 
       
   225 || *Action* || *Description* ||
       
   226 || `STRIP=name` || Remove any child elements with the given name in the default namespace ||
       
   227 || `STRIP=name namespace` || Remove any child elements with the given name and the given namespace ||
       
   228 || `INJECT=xml` || Inject the given XML into the stanza as a child element ||
       
   229 
       
   230 === Informational ===
       
   231 || *Action* || *Description* ||
       
   232 || `LOG=message` || Logs the given message to Prosody's log file. Optionally prefix it with a log level in square brackets, e.g. `[debug]`||