- Published on
Forwarding GitHub webhook to Jenkins behind Cloudflare Tunnel using Cloudflare Worker
- Authors
- Name
- Tong
Background
In a previous post, I have set up Jenkins on my Raspberry Pi to auto deploy my webservice when I push to GitHub. It uses Git Polling to check for changes every 3 minutes. It's not efficient. I want to use GitHub Webhook to trigger the build.
Problem
My Jenkins server is behind a Cloudflare Tunnel. It's not accessible from the internet. I need to forward the GitHub Webhook to Jenkins.
Solution
I deployed a Cloudflare Worker to forward the GitHub Webhook to Jenkins. Here is a graph to illustrate the flow:
+------------+
| Developer |
| (Push to |
| Repo) |
+-----+------+
|
v
+------------+ +-------------------+ +------------------+
| GitHub | -----> | Cloudflare Worker | -----> | Jenkins Server |
| (Webhook) | | | | behind Cloudflare|
+------------+ +-------------------+ | Tunnel |
+---------+--------+
|
v
+-------------+
| Docker |
| (Deployment)|
+-------------+
Crate a service token
Go to Cloudflare Zero Trust Dashboard
Go to Access
-> Service Tokens
-> Create Service Token
In my case, I named it jenkins-worker-tunnel-token
and set it Non-Expiring
.
Keep the page open for later use since it will show the token only once.
Allow the Jenkins application to be accessed by the service token
Go to Cloudflare Zero Trust Dashboard
Go to Access
-> Applications
, configure the Jenkins application, and click Add a policy
.
I named it Allow Jenkins token
and set action to Service Auth
.
In Create additional rules
, add Include
and set Selector
to Service Token
and Value
to jenkins-worker-tunnel-token
.
Save the policy and save the application.
Setup webhook receiver in Jenkins
Install Generic Webhook Trigger Plugin
in Jenkins.
https://plugins.jenkins.io/generic-webhook-trigger/
Open Job configuration to enable Generic Webhook Trigger
, and add a token.`
My job name is BuildAndDeployVideoManagementServiceJob
, and I added a token BuildAndDeployVideoManagementServiceJobToken
.
Create a Cloudflare Worker
Go to Cloudflare Dashboard
Go to Workers & Pages
-> Create application
-> Create Worker
Add the following code:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Check if the request is for the webhook trigger
if (url.pathname === '/generic-webhook-trigger/invoke') {
// Extract token from the query parameter
const token = url.searchParams.get('token');
if (!token) {
return new Response('Token not provided', { status: 400 });
}
// Construct the target URL with the token
const targetUrl = `${env.TUNNEL_HOST}/generic-webhook-trigger/invoke?token=${token}`;
// Clone the original request to modify headers
let newRequest = new Request(targetUrl, request);
// Add or modify headers
newRequest.headers.set('CF-Access-Client-Id', env.CF_CLIENT_ID);
newRequest.headers.set('CF-Access-Client-Secret', env.CF_CLIENT_SECRET);
// Forward the request to the target service
let response = await fetch(newRequest);
// Return the original response if not a 302 redirect
return response;
} else {
return new Response('Not Found', { status: 404 });
}
},
};
Go to Settings
-> Variables
-> Environment Variables
and add the following variables:
TUNNEL_HOST
: The hostname of the Cloudflare application behind the tunnelCF_CLIENT_ID
: The client ID of the service tokenCF_CLIENT_SECRET
: The client secret of the service token
The worker will forward the request to the Jenkins server behind the Cloudflare Tunnel with CF-Access-Client-Id
and CF-Access-Client-Secret
headers.1
Add a webhook in GitHub
Go to the repository in GitHub, and go to Settings
-> Webhooks
-> Add webhook
Set the Payload URL
to the Cloudflare Worker URL with path /jenkins/generic-webhook-trigger/invoke
and the token.
Now when I push to the repository, the GitHub Webhook will be forwarded to Jenkins behind the Cloudflare Tunnel using the Cloudflare Worker and the Jenkins job will be triggered.
Footnotes
This GitHub repository helped me understand how a Cloudflare Worker can access a service behind a Cloudflare Tunnel: cloudflare-worker-tunnel-mysql-example ↩