3. Filtering with Haka

In this section you will learn how to define a security policy in Haka. To do so, you will be led to use the IP, TCP and HTTP dissector.

3.1. Basic IP filtering

In the previous section we wrote a simple rule to log every IP packet. This time, we want to block IP packet originating from a forbidden network. To do so we need to be able to check that an IP packet is originating from a given network, let say 192.168.10.0/27.

Haka provide some facilities for this kind of IP manipulation. You can see a full list of IP tools in ipv4 module documentation. Especially you can see that it is possible to build a network object:

local bad_network = ipv4.network("192.168.10.0/27")

And that you can simply check if an IP belongs to this network:

bad_network:contains(ipv4.addr("192.168.10.30"))

Finally, you will notice in the documentation that Haka also provide the usual drop function on every IP packet.

pkt:drop()

Exercise

Now you have all what you need to create a rule to block IP packet originating from a forbidden network. Create a script named ipfilter.lua and run it against the provided pcap file ipfilter.pcap.

$ hakapcap ipfilter.lua ipfilter.pcap

In order to check that your script correctly block some IP packets you can save the output to a new pcap.

$ hakapcap ipfilter.lua ipfilter.pcap -o output.pcap

The resulting pcap file can simply be opened with wireshark.

3.1.1. Full script

You will find a full script for it here ipfilter.lua.

3.2. TCP filtering

The aim of this section is to be able to block all TCP traffic except the one directed to port 22 (ssh) and 80 (http).

Obviously, Haka provide a TCP dissector module named protocol/tcp_connection. You can load it with the usual require keyword.

As you can see in the corresponding documentation, tcp_connection provide an interesting event: tcp_connection.events.new_connection.

This event pass 2 arguments to the eval function:

  • flow (TcpConnectionDissector) – TCP flow.
  • tcp (TcpDissector) – TCP packet.

Exercise

Write a new rule that check the TCP connection port and block it if port doesn’t match 22 or 80.

You can then test your configuration against the pcap tcpfilter.pcap.

3.2.1. Full script

You can download the full script at tcpfilter.lua.

3.3. Filtering with NFQueue

All the examples so far have used hakapcap to test some recorded packets.

Haka can also use nfqueue to capture packets from a live interface. The following examples will illustrate how to do that.

When configured to use nfqueue, Haka will hook itself up to the raw nfqueue table in order to inspect, modify, create and delete packets in real time.

The rest of this tutorial assumes that the Haka package is installed on a host which has a network interface named eth0.

The configuration file for the daemon is given below (haka.conf):

[general]
# Select the haka configuration file to use
configuration = "tcpfilter.lua"

# Optionally select the number of thread to use. 
#thread = 4

# Pass-through mode
# If yes, haka will only inspect packet
# If no, it means that haka can also modify and create packet
pass-through = no

[packet]
# Select the capture model, nfqueue or pcap
module = "packet/nfqueue"

# Select the interfaces to listen to
interfaces = "lo"
#interfaces = "eth0"

# Select packet dumping for nfqueue
#dump = yes
#dump_input = "/tmp/input.pcap"
#dump_output = "/tmp/output.pcap"

[log]
# Select the log module
module = "log/syslog"

[alert]
# Select the alert module
module = "alert/syslog"

In order to be able to capture packets, the haka daemon needs to be run as root. The --no-daemon option will prevent haka from detaching from the controlling terminal and will force haka to send its outputs to stdout instead of syslog.

$ sudo haka -c haka.conf --no-daemon

Exercise

The Haka script file used here is the one from the previous exercise. This filter will only allow connection to the ports 22 and 80.

The configuration put Haka on the loopback interface, check using ssh and http (wget or iceweasel) that those connections are allowed toward 127.0.0.1.

You can also change the listening interface to eth0 to check haka on some real servers.

3.4. Optional: Interactive rule debugging

Haka allows to interactively debug Haka script file. A script containing a lua error is provided as erroneousrule.lua.

This script will authorize only packets from/to port 80.

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at http://mozilla.org/MPL/2.0/.

------------------------------------
-- Loading dissectors
------------------------------------

require('protocol/ipv4')
local tcp = require('protocol/tcp')

-- Allow only packets to/from port 80
haka.rule{
    hook = tcp.events.receive_packet,
    eval = function (pkt)
        -- The next line will generate a lua error:
        -- there is no 'destport' field. Replace 'destport' by 'dstport'
        if pkt.destport == 80 or pkt.srcport == 80 then
            haka.log("Filter", "Authorizing trafic on port 80")
        else
            haka.log("Filter", "Trafic not authorized on port %d", pkt.dstport)
            pkt:drop()
        end
    end
}

To run this example, use the following commands:

$ hakapcap erroneousrule.lua tcpfilter.pcap

When running this script, Haka will output a high number of errors, complaining that the field destport doesn’t exist. We will use Haka’s debug facilities to find out precisely where the error occurs.

See also

Debugging contains documentation on Haka’s debugging facilities.

To start Haka in debugging mode, add --debug-lua at the end of the command line:

$ hakapcap erroneousrule.lua tcpfilter.pcap --debug-lua

When a Lua error code occurs, the debugger breaks and outputs the error and a backtrace.

   entering debugger: unknown field 'destport'
    thread: 0
    Backtrace
    =>0     [C]: in function '(null)'
      #1    [C]: in function '(null)'
      #2    [C]: in function '__index'
      #3    erroneousrule.lua:18: in function 'signal'
      #4    /usr/share/haka/core/events.bc:0: in the main chunk
      #5    /usr/share/haka/core/events.bc:0: in the main chunk
      #6    /usr/share/haka/core/context.bc:0: in the main chunk
      #7    [string "tcp"]:14: in function 'receive'
      #8    /usr/share/haka/core/dissector.bc:0: in the main chunk
      #9    [C]: in function 'xpcall'
     ...

The general syntax of the debugger is close to the syntax of gdb.

Here we are interested in the third frame which is the one in the Lua script itself.

To set the debugger to focus on that particular frame, type frame 3. We can now use the list command to display the faulty source code:

debug>  list
      14:      hook = haka.event('tcp', 'receive_packet'),
      15:      eval = function (pkt)
      16:          -- The next line will generate a lua error:
      17:          -- there is no 'destport' field. replace 'destport' by 'dstport'
      18=>         if pkt.destport == 80 or pkt.srcport == 80 then
      19:              haka.log("Filter", "Authorizing trafic on port 80")
      20:          else
      21:              haka.log("Filter", "Trafic not authorized on port %d", pkt.dstport)
      22:              pkt:drop()
      23:          end

We now see that Lua is complaining about an unknown field destport on the line testing the destination port of the packet.

Packets, like all structures provided by Haka, can be printed easily using the debugger.

To see the content of the packet, type print pkt:

debug>  print pkt
      #1    userdata tcp {
              ack_seq : 0
              checksum : 417
              dstport : 80
              flags : userdata tcp_flags {
                ack : false
                all : 2
                cwr : false
                ecn : false
                fin : false
                psh : false
                rst : false
                syn : true
                urg : false
              }
              hdr_len : 40
              ip : userdata ipv4 {
                  ...
              }
              ...
              srcport : 37542
              urgent_pointer : 0
              window_size : 14600
            }

You can notice that there is no field called destport. The correct name for the field is dstport. Once this typo is corrected, the script will run properly

Press CTRL-C to quit or type help to get the list of available commands.

Note

You can use tab to auto-complete your commands