A serverless, easy-to-setup GitHub webhook filtering proxy that relays or drops events when they match configurable JSONPath expressions, powered by Cloudflare Workers.
GitHub supports sending HTTP POST requests to an arbitrary server in response to events on the platform, known as webhooks, to notify external services about them. They are particularly useful for integrating with GitHub.
However, while GitHub lets users select what types of events trigger a webhook, it does not offer any functionality for filtering events by more fine-grained conditions. For example, when a webhook is triggered by a push
event, it is triggered for a push
in any branch, even if the service is only interested in a single branch.
This behavior can be undesirable: it wastes traffic for would-be-ignored events, and several external services, which in some cases are not controlled by the end user, may not offer any filtering features, triggering superfluous actions.
This project offers an easy-to-setup, flexible proxy that anyone can use to drop events before they are delivered to the actual external service, as if they never were triggered. GitHub can be configured to deliver events to this proxy, which then decides to relay the event to the external service or drop it, based on whether the event JSON body matches a JSONPath expression.
This proxy is helpful for chores such as:
- Limiting what pull requests and branches send chat notifications on Discord or Slack. Get rid of the notification spam caused by automation tools, such as Dependabot or Renovate, and focus on worthwhile events!
- Enforcing event sender authentication. GitHub can sign webhook requests with a shared secret, and this proxy expects and verifies that signature. Rest assured knowing that every event relayed by this proxy comes from GitHub.
- Working around a lack of event filtering features. Trigger deployments, continuous integration, and similar tasks only for a subset of events, even if the external service does not support that.
- Reducing the load on an overwhelmed service.
The easiest way to get started is to fork this repository and deploy it to your Cloudflare account. There are step-by-step instructions to do that below. It's free, and it will only take you a few minutes!
- Click the button below. Follow the instructions that appear.
Note: Cloudflare will ask you for an API token, which you will need to create if you don't have one yet. The "Edit Cloudflare Workers" template generates API tokens suitable for deploying this proxy.
- If everything goes well, you will be greeted with the following screen, and the worker will be deployed. Click the "Worker dash" button.
- Find your way to the "Settings" tab of the worker you've just deployed, at Workers ›
github-webhook-filter-proxy
. Once there, go to the "Variables" section and add a new environment variable by clicking "Add variable".
- As you may have guessed, the proxy is configured via environment variables. For now, we will set three environment variables:
SECRET_TOKEN
: a random, unique string that only GitHub and the proxy should know, used by the proxy to authenticate that events come from GitHub. Some ways of generating this token include runningecho "$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c32)"
on a Unix-like terminal, or using websites like random.org. Make sure to click the "Encrypt" button once you are done typing the token!TARGET_URL
: the URL to which the proxy will relay events that it does not drop. This is the URL of your target service (or another proxy, if you are into that).UNMATCHED_EVENT_ACTION
: the action to perform when a JSONPath expression match is not configured for an input event. The default isdrop
, so set it torelay
to make the proxy send all the events it receives to the target URL.
Before clicking "Save" to apply the changes, the edition form should look like this:
- On GitHub, go to the corresponding webhooks settings page. Set the payload URL to the route of your worker, select
application/json
as "Content type", and type the same secret as inSECRET_TOKEN
. If you are using the defaultworkers.dev
route, you can (and should) leave SSL verification enabled.
- The proxy is ready to rock! 🎉 If you want to know what is going on, check out the "Recent deliveries" on GitHub and the worker logs on Cloudflare. The next section of this document describes the environment variables you can use to configure how it filters events.
The proxy can be configured via the following environment variables.
Required: yes
A random secret shared by GitHub and the proxy that is used to validate the digital signature that GitHub adds to the webhook event request.
The proxy expects and validates these signatures, so both GitHub and the proxy must be configured with the same value for this token.
Required: yes
Accepted values: any HTTP(S) URL
The URL to which the proxy will relay events that it does not drop. This is the URL of the service that is being proxied.
Required: no
Accepted values: relay
or drop
Default value: drop
The action to perform when a JSONPath expression match is not defined for an event. drop
discards the event, while relay
forwards the request to the target URL.
Required: no
Accepted values: a JSONPath expression, as a string
Default value: not defined
The JSONPath expression to match against the JSON event payload for <EVENT NAME>
events, where <EVENT NAME>
is the name of a webhook event listed in the GitHub documentation, converted to uppercase.
For matching, the event JSON object is put into a single-element array, and then the specified JSONPath expression is evaluated by the jsonpath
npm package. This wrapping allows filtering JSONPath subscript operator expressions on the event object keys.
If a match occurs, the proxy will either relay or drop the event as defined by <EVENT NAME>_EVENT_MATCH_ACTION
. If the payload does not match the JSONPath expression, the proxy will take the opposite action to the one it would take if it matched (dropping instead of relaying, and vice versa).
Required: no
Accepted values: relay
or drop
Default value: drop
The action to take when the JSONPath expression defined by <EVENT NAME>_EVENT_MATCH_JSONPATH
matches the JSON event payload.
The quickstart guide sets worker environment variables using the Cloudflare web dashboard. Nevertheless, it is also possible to configure them in the wrangler.toml
file, which is the approach recommended by Cloudflare.
The Cloudflare documentation explains how to set environment variables this way, and describes the wrangler.toml
file format. The wrangler.toml
file at this repository contains comments that illustrate how it would be done.
The next example variables configure the proxy to relay everything except push events made by bots or on branches managed by Renovate.
PUSH_EVENT_MATCH_JSONPATH
:$[?(@.ref.startsWith('refs/heads/renovate/') || @.sender.login == 'github-actions[bot]')]
PUSH_EVENT_MATCH_ACTION
(optional):drop
UNMATCHED_EVENT_ACTION
:relay
Pull requests are accepted. Feel free to contribute if you can improve some aspect of the project!
Contributions include, but are not limited to:
- Writing good bug reports or feature requests.
- Sending a PR with code changes that implement an improvement or fix an issue.
- Recommending the project to others and engaging with the community.
- Economically supporting the project (check out the "Sponsor" button in the GitHub page).
Code contributions must pass CI checks and be deemed of enough quality by a repository maintainer to be merged.
The proxy source artifacts are structured as a standard npm
project, coded in TypeScript. The Wrangler CLI tool is used for development and deployment. After the first npm install
, npm run start
will launch the worker in a local server for development. Before committing any change, you should run ESlint and Prettier with npm run lint
and npm run format
, respectively.
We welcome friendly talk about the project, including questions, congratulations, and suggestions. Head to the GitHub Discussions page to interact with fellow users, contributors and developers.
Thanks goes to these wonderful people (emoji key):
Alejandro González 💻 📖 🚧 📆 |
This project follows the all-contributors specification. Contributions of any kind welcome!