In this post I describe how to test GitHub webHooks locally using SSH and https://localhost.run/, a service that can be used to create a public endpoint that tunnels to a website running locally.
GitHub webhooks provide a way to receive notifications when an action is performed against your GitHub repository. Typical events include, a Pull Request was opened, an issue was raised, or a commit was pushed. When the event occurs, GitHub calls a configured URL with JSON describing the event.
You can create a GitHub webhook by navigating to the Settings page in your GitHub repository and clicking "Add webhook":
After clicking "Add webook" you are shown the following screen. This lets you enter the URL that will receive the webhook payload, the format of the payload, as well as which events you want to get notifications for:
If you choose individual events, you'll see there's a lot of potential events you can be notified about!
Webhooks can be used for automating all sorts of processes. They're obviously used extensively with CI, but there are a load of other potential uses too.
The one tricky part to working with webhooks is testing them. Lets say you're building an application that needs to be notified every time someone creates a Pull Request on a GitHub repository. Setting up a webhook to point to your production application will work well, but how do you test it locally before you've deployed your application.
When building and testing an application, you typically run it locally, and listen to a port on the loopback interface, http://localhost. The question is, how can you tell GitHub to send webhook events to your local application. Your local app isn't exposed to the internet, so on the face of it, it doesn't seem possible, but by using tunnelling, it can be done!
Tunnelling involves opening a connection between your local computer and some publicly accessible endpoint. GitHub then sends requests to the public endpoint, and the tunnel sends them to your local application. From GitHub's point of view, it's pointing to a public endpoint; from your local application's point of view, it's only listening to local traffic.
There are lots of different services you can use to create such a tunnel. One of the most popular is ngrok, but there are lots of possible options.. Most of these services require you to sign up for an account, install a client locally, and use that to connect to the proxy service.
One service I decided to try out is called localhost.run. The nice thing about this one is that simply uses ssh—no need to download a client—and (currently at least) you don't need to sign up for an account, so you can just get started right away!
All major operating systems (Windows included) have an SSH client built-in, so there's typically nothing for you to install. What you will need however is an SSH key pair. If you haven't used SSH before, you'll need to create a new key pair.
SSH keys are used to secure the communication between your local machine and a remote server. You can use the built-in SSH client to generate a new key pair.
For a more detailed guide about generating and managing SSH keys, see this doc by GitLab
The first choice you need to make is what type of algorithm to use for your keys. As far as I can tell, from my extensive research, go with RSA and you'll be fine.
To generate your key, run
ssh-keygen -t rsa -b 2048 at a terminal, follow the prompts, and enter a passphrase to protect your key:
> ssh-keygen -t rsa -b 2048 Generating public/private rsa key pair. Enter file in which to save the key (C:\Users\Sock/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in C:\Users\Sock/.ssh/id_rsa. Your public key has been saved in C:\Users\Sock/.ssh/id_rsa.pub.
You should now have an SSH key pair in the .ssh folder in your home directory, so you can make SSH requests.
You can use localhost.run to create an HTTP tunnel between their server and your local machine for testing GitHub webhooks. In my case, my local application was listening on port 5000, so I used the following command to tunnel public HTTP traffic from port 80 to localhost on port 5000:
ssh -R 80:localhost:5000 localhost.run
After entering the passphrase for my SSH key, localhost.run told me the public address of the other end of my tunnel: http://c44bc40ada31cb.localhost.run. This is the URL that GitHub will send webhook events to.
> ssh -R 80:localhost:5000 localhost.run =============================================================================== Welcome to localhost.run! Follow your favourite reverse tunnel at [https://twitter.com/localhost_run]. **You need a SSH key to access this service.** If you get a permission denied follow Gitlab's most excellent howto: https://docs.gitlab.com/ee/ssh/ *Only rsa and ed25519 keys are supported* To set up and manage custom domains go to https://admin.localhost.run/ More details on custom domains (and how to enable subdomains of your custom domain) at https://localhost.run/docs/custom-domains To explore using localhost.run visit the documentation site: https://localhost.run/docs/ =============================================================================== ** your connection id is 9e381a1b-13bf-49ee-a123-e3279bb07d94, please mention it if you send me a message about an issue. ** Enter passphrase for key 'C:\Users\Sock/.ssh/id_rsa': c44bc40ada31cb.localhost.run tunneled with tls termination, http://c44bc40ada31cb.localhost.run
Once you have an active connection, it's time to setup the webhook in GitHub.
To set up a webhook in GitHub, navigate to the Webhooks tab in your repository settings, and choose "Add webhook". Under "Payload URL", enter the localhost.run URL (plus any required path), choose
application/json or Form encoding for "Content type", enter a secret for securing your webhook, and choose the events you want.
After you click "Add webhook", GitHub sends a "test" event to the endpoint. If everything is working correctly, you should see the request come through in your local application, and GitHub should show that the request was successful under "Recent Deliveries" for the webhook
And that's it! Using localhost.run was so simple for quick testing like this that I'll definitely be using it again. If you need extra features like HTTPS, custom domains, or stable domain names, then you can signup for an account, but otherwise being able to simply test an application using SSH is great.
In this post I described how you can use localhost.run to test GitHub webhooks using an application running on localhost. Using an SSH tunnel, localhost.run tunnels HTTP traffic to a local application. You can then point GitHub's webhook endpoint at localhost.run, and you will receive the request in your application, with no client download or signup required.