Get started with FilterX

Build your first FilterX block: filter, parse a key=value message, set a field, and check the output in a file.

In this tutorial, you build a FilterX block that processes iptables log messages. You learn how to filter messages, parse key=value pairs, modify a field, and send the result to a destination.

Prerequisites

Sample messages

You use two sample messages in this tutorial. The first one is an iptables message from the kernel. It’s an RFC3164-formatted message with space-separated key=value pairs:

Terminal window
Dec 08 12:00:00 hostname.example kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0

The second one is an sshd message. Your FilterX block must not let it through:

Terminal window
Dec 08 12:00:01 hostname.example sshd[1234]: Accepted publickey for alice from 192.0.2.2 port 54321 ssh2

Step 1: Start AxoSyslog without FilterX

First, you run a minimal configuration that reads messages from a file and writes them to another file.

  1. Create an empty directory and an empty input file:

    Terminal window
    mkdir filterx-tutorial
    cd filterx-tutorial
    touch input.log
  2. Create a file called filterx-tutorial.conf with the following content:

    Terminal window
    @version: current
    
    source s_tutorial {
        file("/tutorial/input.log" follow-freq(1));
    };
    
    destination d_tutorial {
        file("/tutorial/output.log" template("${PROGRAM}: ${MESSAGE}\n"));
    };
    
    log {
        source(s_tutorial);
        destination(d_tutorial);
    };
  3. Start AxoSyslog in a container. The container mounts the current directory as /tutorial, and keeps running in the background.

    Terminal window
    docker run -d --name filterx-tutorial -v "$PWD:/tutorial" ghcr.io/axoflow/axosyslog:latest -f /tutorial/filterx-tutorial.conf
  4. Append the two sample messages to the input file:

    Terminal window
    echo 'Dec 08 12:00:00 hostname.example kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0' >> input.log
    echo 'Dec 08 12:00:01 hostname.example sshd[1234]: Accepted publickey for alice from 192.0.2.2 port 54321 ssh2' >> input.log
  5. Check the output file:

    Terminal window
    tail -n 2 output.log

    You should see both messages:

    Terminal window
    kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0
    sshd: Accepted publickey for alice from 192.0.2.2 port 54321 ssh2

Keep the container running. In the next steps, you edit filterx-tutorial.conf, then apply the changes with the following command:

Terminal window
docker exec filterx-tutorial syslog-ng-ctl reload

You should see:

Terminal window
Config reload successful

Step 2: Filter the messages

Now you add a FilterX block that lets only iptables messages through.

  1. Replace the log path in filterx-tutorial.conf with the following:

    Terminal window
    log {
        source(s_tutorial);
        filterx {
            ${PROGRAM} == "kernel";
            ${MESSAGE} =~ "PROTO=";
        };
        destination(d_tutorial);
    };

    A message passes the FilterX block only if every statement in the block is true. For details, see Truthy and falsy values . The =~ operator checks if the message matches a regular expression. For details, see FilterX operator reference.

  2. Reload the configuration:

    Terminal window
    docker exec filterx-tutorial syslog-ng-ctl reload
  3. Append the two sample messages again:

    Terminal window
    echo 'Dec 08 12:00:00 hostname.example kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0' >> input.log
    echo 'Dec 08 12:00:01 hostname.example sshd[1234]: Accepted publickey for alice from 192.0.2.2 port 54321 ssh2' >> input.log
  4. Check the output file:

    Terminal window
    tail -n 3 output.log

    You should see:

    Terminal window
    kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0
    sshd: Accepted publickey for alice from 192.0.2.2 port 54321 ssh2
    kernel: IN=eth0 OUT= MAC=11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00 SRC=192.0.2.2 DST=192.168.0.1 LEN=40 TOS=0x00 PREC=0x00 TTL=232 ID=12345 PROTO=TCP SPT=54321 DPT=22 WINDOW=1023 RES=0x00 SYN URGP=0

    The first two lines are from step 1. The last line is new: the kernel message passed the filter, but the sshd message didn’t.

Step 3: Parse the key=value pairs

Next, you parse the iptables message into separate fields, and send them as JSON.

  1. Add the following lines to the end of the FilterX block, after the two filter statements:

    Terminal window
    iptables = parse_kv(${MESSAGE}, value_separator="=", pair_separator=" ");
    ${MESSAGE} = iptables;
    • parse_kv splits the message into key=value pairs, and stores them in the iptables variable.
    • iptables is a local FilterX variable, because its name doesn’t start with $. AxoSyslog doesn’t send local variables to the destination, unless you assign them to a name-value pair. For details, see FilterX variables in destinations .
    • Assigning the iptables dict to ${MESSAGE} stores it as JSON, so the destination receives a JSON string. You don’t have to format it explicitly. To send it in another format, use a formatting function, like format_kv . For details, see Format data.
  2. Reload the configuration, and append the two sample messages again, the same way as in Step 2 .

  3. Check the output file:

    Terminal window
    tail -n 1 output.log

    You should see the parsed message as JSON:

    Terminal window
    kernel: {"IN":"eth0","OUT":"","MAC":"11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00","SRC":"192.0.2.2","DST":"192.168.0.1","LEN":"40","TOS":"0x00","PREC":"0x00","TTL":"232","ID":"12345","PROTO":"TCP","SPT":"54321","DPT":"22","WINDOW":"1023","RES":"0x00","URGP":"0"}

    The word SYN isn’t in the output, because it isn’t a key=value pair.

Step 4: Rename a field

Finally, you rename the SRC field to src_ip.

  1. Add the following lines to the FilterX block, before the ${MESSAGE} = iptables; line:

    Terminal window
    iptables.src_ip = iptables.SRC;
    unset(iptables.SRC);

    The first line copies the value of SRC to a new src_ip field. The unset function deletes the SRC field.

    The FilterX block now looks like this:

    Terminal window
    filterx {
        ${PROGRAM} == "kernel";
        ${MESSAGE} =~ "PROTO=";
    
        iptables = parse_kv(${MESSAGE}, value_separator="=", pair_separator=" ");
        iptables.src_ip = iptables.SRC;
        unset(iptables.SRC);
        ${MESSAGE} = iptables;
    };
  2. Reload the configuration, and append the two sample messages again, the same way as in Step 2 .

  3. Check the output file:

    Terminal window
    tail -n 1 output.log

    You should see:

    Terminal window
    kernel: {"IN":"eth0","OUT":"","MAC":"11:22:33:44:55:66:aa:bb:cc:dd:ee:ff:08:00","DST":"192.168.0.1","LEN":"40","TOS":"0x00","PREC":"0x00","TTL":"232","ID":"12345","PROTO":"TCP","SPT":"54321","DPT":"22","WINDOW":"1023","RES":"0x00","URGP":"0","src_ip":"192.0.2.2"}

    The SRC field is gone, and the src_ip field is at the end of the JSON object.

What you built

You now have a FilterX block that selects iptables messages, parses them into fields, and renames a field. AxoSyslog sends the result to a file as JSON. Your complete filterx-tutorial.conf file looks like this:

Terminal window
@version: current

source s_tutorial {
    file("/tutorial/input.log" follow-freq(1));
};

destination d_tutorial {
    file("/tutorial/output.log" template("${PROGRAM}: ${MESSAGE}\n"));
};

log {
    source(s_tutorial);
    filterx {
        ${PROGRAM} == "kernel";
        ${MESSAGE} =~ "PROTO=";

        iptables = parse_kv(${MESSAGE}, value_separator="=", pair_separator=" ");
        iptables.src_ip = iptables.SRC;
        unset(iptables.SRC);
        ${MESSAGE} = iptables;
    };
    destination(d_tutorial);
};

Clean up

To stop and remove the container, run:

Terminal window
docker rm -f filterx-tutorial

Next steps