Skip to content

Serverless, easy-to-setup GitHub webhook filtering proxy that relays or drops events when they match configurable JSONPath expressions, powered by Cloudflare Workers.

License

Notifications You must be signed in to change notification settings

heytimmies/GitHub-webhook-filter-proxy

 
 

Repository files navigation

GitHub webhook filter proxy

A serverless, easy-to-setup GitHub webhook filtering proxy that relays or drops events when they match configurable JSONPath expressions, powered by Cloudflare Workers.

CI workflow status Deploy workflow status

💡 Background

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.

Use cases

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.

✨ Quickstart

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!

  1. Click the button below. Follow the instructions that appear.

Deploy to Cloudflare Workers

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.

  1. If everything goes well, you will be greeted with the following screen, and the worker will be deployed. Click the "Worker dash" button.

Cloudflare Workers deployment screen

  1. 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".

Worker variables screen

  1. 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 running echo "$(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 is drop, so set it to relay 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:

Worker variables edition form preview

  1. 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 in SECRET_TOKEN. If you are using the default workers.dev route, you can (and should) leave SSL verification enabled.

GitHub webhook configuration screen

  1. 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.

⚙️ Configuration

The proxy can be configured via the following environment variables.

SECRET_TOKEN

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.

TARGET_URL

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.

UNMATCHED_EVENT_ACTION

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.

<EVENT NAME>_EVENT_MATCH_JSONPATH

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).

<EVENT NAME>_EVENT_MATCH_ACTION

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.

Configuration via files

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.

Examples

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

❤️ Contributing

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.

🤝 Contact

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.

🧑‍🤝‍🧑 Contributors

Thanks goes to these wonderful people (emoji key):


Alejandro González

💻 📖 🚧 📆

This project follows the all-contributors specification. Contributions of any kind welcome!

About

Serverless, easy-to-setup GitHub webhook filtering proxy that relays or drops events when they match configurable JSONPath expressions, powered by Cloudflare Workers.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 90.5%
  • JavaScript 9.5%