Rules-based webhook filtering & routing

By Karolis Rusenas · Apr 2, 2019

cover

Some webhook providers do not offer fine-grained control over what events are being sent via webhooks. Sometimes, you end up in a situation where webhooks, based on their type have to be delivered to different servers. We will have a look at a new Webhook Relay forwarding feature that allows users to define rules on outputs that help in these situations.

Each output can now have one or more, multi-level rules and rule groups. In this short tutorial, we will create a rule to verify GitHub webhooks and route them to appropriate endpoints based on their contents.

Preparing webhooks bucket

First, go to the Webhook Relay buckets page and create a new bucket. Once you have it, you should be able to set your public input endpoint:

bucket input

Buckets are a grouping mechanism that let you define multiple inputs and multiple output destinations.

Creating a new GitHub webhook

To create a GitHub repository webhooks, go to Settings -> Webhooks and set few parameters:

  • Payload URL should be your input endpoints: https://my.wehbookrelay.com/v1/webhooks/......
  • Content type is whatever your webhook receiver is comfortable consuming, usually it will be `application/json.
  • Secret is your secret key, set it to anything you want, I set mine to very-secret :)

setting up GitHub webhooks

X-Hub-Signature rule

Open your bucket details page from the buckets page and create a new output (mine relays to a helper service on https://bin.webhookrelay.com):

creating bucket output

Once you have your output, click on a triangle to define your rules. For now, we will create a single rule. Click on the dropdown next to “ADD RULE”, select header and then click the “ADD RULE” button.

In the parameter set X-Hub-Signature. It tells Webhook Relay where to find the parameter to match against. Then, from the dropdown select payload SHA1 and set the last field to your secret value, mine was very-secret:

github webhook signature matcher

Configuration is now set. Try pushing to your repository. Webhook should be pass through the service. Feel free to change your secret either in Webhook Relay configuration or GitHub webhook config and retrying push, it will be rejected.

At this point, we can determine whether webhooks are coming from GitHub or not. Next step - differentiating between pushes and tags.

Detecting tags

If you create a new release or just make a git tag on your repository, a new webhook will be visible in your repository. Go to request details:

github tag webhook details

The part that’s interesting for us is "ref": "refs/tags/0.1.0":

{
    "ref": "refs/tags/0.1.0",
    "before": "0000000000000000000000000000000000000000",
    "after": "39237a56aab6ed53030b086e6d02d04aa232b337",
    "created": true,
    "deleted": false,
    "forced": false,
    ...
    ...

And the second webhook will be for the tag itself:

{
    "ref": "0.1.1",
    "ref_type": "tag",
    "master_branch": "master",
    "description": null,
    "pusher_type": "user",
    ...
    ...

Let’s create a rule that will catch any tag requests:

  1. Create a new output called production-ci.
  2. Copy existing signature matcher rule from the first output (click on a JSON VIEW when in the rules modal and copy it into the new output rule).
  3. Add a new group and set Logical Operator to Any. This works the same way as or in programming.
  4. Add a new rule that will be matching payload, set parameter to ref (it will be getting it from payload), select contains matcher and to the value field set refs/tags:

tags matcher rule

Now, if you push again, webhook will not be relayed to production-ci destination. Let’s modify our first rule to reject all tag webhooks. You can do that by creating a similar rule to the one we added in production-ci output but instead of contains set it to does not contain. Now, make a new tag and check out your bucket logs:

bucket logs

Next steps

As you see, it’s trivial to set up multi-level rules that can route webhooks to desired destinations based on their contents and also do some basic validation. If you need yet more power on selecting correct webhooks, check out our example implementation of the socket protocol here: https://github.com/webhookrelay/webhookrelay-ws-client. Using WebSockets, you can receive webhooks directly inside your application and process them accordingly. Library is already used in Node-RED workflows via our node (blog post here).

I have recently also had a chance to look at Rust programming language and found that implementing a webhook forwarding application can be quite interesting: https://github.com/rusenask/rust-ws-client.