summaryrefslogtreecommitdiffstats
path: root/modules/daf/README.rst
blob: a5e025e92461de231fe4d67e29abacc589679581 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
.. SPDX-License-Identifier: GPL-3.0-or-later

.. _mod-daf:

DNS Application Firewall
========================

This module is a high-level interface for other powerful filtering modules and DNS views. It provides an easy interface to apply and monitor DNS filtering rules and a persistent memory for them. It also provides a restful service interface and an HTTP interface.

Example configuration
---------------------

Firewall rules are declarative and consist of filters and actions. Filters have ``field operator operand`` notation (e.g. ``qname = example.com``), and may be chained using AND/OR keywords. Actions may or may not have parameters after the action name.

.. code-block:: lua

    -- Let's write some daft rules!
    modules = { 'daf' }

    -- Block all queries with QNAME = example.com
    daf.add('qname = example.com deny')

    -- Filters can be combined using AND/OR...
    -- Block all queries with QNAME match regex and coming from given subnet
    daf.add('qname ~ %w+.example.com AND src = 192.0.2.0/24 deny')

    -- We also can reroute addresses in response to alternate target
    -- This reroutes 192.0.2.1 to localhost
    daf.add('src = 127.0.0.0/8 reroute 192.0.2.1-127.0.0.1')

    -- Subnets work too, this reroutes a whole subnet
    -- e.g. 192.0.2.55 to 127.0.0.55
    daf.add('src = 127.0.0.0/8 reroute 192.0.2.0/24-127.0.0.0')

    -- This rewrites all A answers for 'example.com' from
    -- whatever the original address was to 127.0.0.2
    daf.add('src = 127.0.0.0/8 rewrite example.com A 127.0.0.2')

    -- Mirror queries matching given name to DNS logger
    daf.add('qname ~ %w+.example.com mirror 127.0.0.2')
    daf.add('qname ~ example-%d.com mirror 127.0.0.3@5353')

    -- Forward queries from subnet
    daf.add('src = 127.0.0.1/8 forward 127.0.0.1@5353')
    -- Forward to multiple targets
    daf.add('src = 127.0.0.1/8 forward 127.0.0.1@5353,127.0.0.2@5353')

    -- Truncate queries based on destination IPs
    daf.add('dst = 192.0.2.51 truncate')

    -- Disable a rule
    daf.disable(2)
    -- Enable a rule
    daf.enable(2)
    -- Delete a rule
    daf.del(2)

    -- Delete all rules and start from scratch
    daf.clear()

.. warning:: Only the first matching rule's action is executed.  Defining
   additional actions for the same matching rule, e.g.  ``src = 127.0.0.1/8``,
   will have no effect.

If you're not sure what firewall rules are in effect, see ``daf.rules``:

.. code-block:: text

    -- Show active rules
    > daf.rules
    [1] => {
        [rule] => {
            [count] => 42
            [id] => 1
            [cb] => function: 0x1a3eda38
        }
        [info] => qname = example.com AND src = 127.0.0.1/8 deny
        [policy] => function: 0x1a3eda38
    }
    [2] => {
        [rule] => {
            [suspended] => true
            [count] => 123522
            [id] => 2
            [cb] => function: 0x1a3ede88
        }
        [info] => qname ~ %w+.facebook.com AND src = 127.0.0.1/8 deny...
        [policy] => function: 0x1a3ede88
    }

Web interface
-------------

If you have :ref:`HTTP/2 <mod-http>` loaded, the firewall automatically loads as a snippet.
You can create, track, suspend and remove firewall rules from the web interface.
If you load both modules, you have to load `daf` after `http`.

RESTful interface
-----------------

The module also exports a RESTful API for operations over rule chains.


.. csv-table::
    :header: "URL", "HTTP Verb", "Action"

    "/daf", "GET", "Return JSON list of active rules."
    "/daf", "POST", "Insert new rule, rule string is expected in body. Returns rule information in JSON."
    "/daf/<id>", "GET", "Retrieve a rule matching given ID."
    "/daf/<id>", "DELETE", "Delete a rule matching given ID."
    "/daf/<id>/<prop>/<val>", "PATCH", "Modify given rule, for example /daf/3/active/false suspends rule 3."

This interface is used by the web interface for all operations, but you can also use it directly
for testing.

.. code-block:: bash

    # Get current rule set
    $ curl -s -X GET http://localhost:8453/daf | jq .
    {}

    # Create new rule
    $ curl -s -X POST -d "src = 127.0.0.1 pass" http://localhost:8453/daf | jq .
    {
      "count": 0,
      "active": true,
      "info": "src = 127.0.0.1 pass",
      "id": 1
    }

    # Disable rule
    $ curl -s -X PATCH http://localhost:8453/daf/1/active/false | jq .
    true

    # Retrieve a rule information
    $ curl -s -X GET http://localhost:8453/daf/1 | jq .
    {
      "count": 4,
      "active": true,
      "info": "src = 127.0.0.1 pass",
      "id": 1
    }

    # Delete a rule
    $ curl -s -X DELETE http://localhost:8453/daf/1 | jq .
    true