FAQ

Here's a FAQ for reaction:

What is JSONnet? Why should I use it over YAML?

Answer

JSONnet already has a good tutorial to start with.

It permits to define functions, variables, etc, and thus to avoid duplication.

Other configuration languages exist, like Nix, Dhall, etc. You should feel at home if you already used one of those.

JSONnet semantics look close to Javascript too.

How do I write defaults at one place and reuse them elsewhere?

Answer

You first define your defaults set.

Here's an example for default options for a filter:

local filter_default = {
  retry: 3,
  retryperiod: '3h',
  actions: banFor('24h'),
};

Then you can use it in your filters:

{
  streams: {
    ssh: {
      filters: {
        failedlogin: filter_default + {
          regex: ['...'],
        },
      },
    },
  },
}

You can override those defaults:

{
  streams: {
    ssh: {
      filters: {
        failedlogin: filter_default + {
          regex: ['...'],
          // retry is overriden here
          retry: 1,
        },
      },
    },
  },
}

And the + is optional.

{
  streams: {
    ssh: {
      filters: {
        failedlogin: filter_default {
          regex: ['...'],
        },
      },
    },
  },
}

How do I add multiple actions defined by JSONnet functions on the same filter?

Answer

Let's take this example: we have two functions defining actions:

The first is made to ban the IP using linux's iptables firewall:

local banFor(time) = {
  ban: {
    cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
  unban: {
    after: time,
    cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
};

The second sends a mail:

local sendmail(text) = {
  mail: {
    cmd: ['sh', '-c', '/root/scripts/mailreaction.sh', text],
  },
};

Both create a set of actions. We want to merge the two sets.

To merge two sets with JSONnet, it's as easy as set1 + set2.

Let's see what it looks like with a real example.

local banFor(time) = {
  ban: {
    cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
  unban: {
    after: time,
    cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
};

local sendmail(text) = {
  mail: {
    cmd: ['sh', '-c', '/root/scripts/mailreaction.sh', text],
  },
};

{
  streams: {
    ssh: {
      filters: {
        failedlogin: {
          regex: [
            // skipping
          ],
          retry: 3,
          retryperiod: '3h',
          actions: banFor('720h') + sendmail('banned <ip> from service ssh'),
        },
      },
    },
  },
}

This will generate this configuration:

{
  "streams": {
    "ssh": {
      "filters": {
        "failedlogin": {
          "regex": [ ],
          "retry": 3,
          "retryperiod": "3h",
          "actions": {
            "ban": {
              "cmd": [ "ip46tables", "-w", "-A", "reaction", "-s", "<ip>", "-j", "DROP" ]
            },
            "unban": {
              "after": "720h",
              "cmd": [ "ip46tables", "-w", "-D", "reaction", "-s", "<ip>", "-j", "DROP" ]
            },
            "mail": {
              "cmd": [ "sh", "-c", "/root/scripts/mailreaction.sh", "banned <ip> from service ssh" ]
            }
          }
        }
      }
    }
  }
}

How do I separate my configuration in multiple files?

Answer

Starting with reaction v2.0.0, you can specify a folder containing multiple configuration files. reaction will read and merge all files that:

  • Do not start with . or _.
  • And that ends with .json, .jsonnet, .yml or .yaml.

JSONnet files starting with _ or . will not be directly read. You can manually import them in other files, for example to store definitions used accross files:

You can use the import JSONnet keyword to import files, relative to the file calling them. (Remember, the JSONnet tutorial is a good place to understand its basics).

Here's an example of how you could do this:

/etc/reaction/main.jsonnet

{
  streams: {
    ssh: {
      cmd: [ "tail", "-F", /* skip */ ],
    },
    // ...
  },
}

/etc/reaction/ssh.jsonnet

local lib = import '_lib.jsonnet';
{
  streams: {
    ssh: {
        filters: {
          failedlogin: lib.filter_default + {
          regex: ['...'],
          retry: 3,
          retryperiod: '2h',
          actions: lib.banFor('30d'),
        },
      },
    },
  },
}

/etc/reaction/_lib.jsonnet (definitions you can reuse in all other files)

local banFor(time) = {
  ban: {
    cmd: ['ip46tables', '-w', '-A', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
  unban: {
    after: time,
    cmd: ['ip46tables', '-w', '-D', 'reaction', '-s', '<ip>', '-j', 'DROP'],
  },
};

local filter_default = {
  retry: 3,
  retryperiod: '3h',
  actions: banFor('24h'),
};

{
  banFor: banFor,
  filter_default: filter_default,
}

You can split different patterns, streams and filters accross files.

However, you can't spread the definition of one pattern or filter accross multiple files.