A Python FastAPI-based application intended to forward Github webhooks, primarily to a protected Jenkins instance.
The application checks the sender's IP, X-IP (if behind proxy) and validates the signature of the webhook before forwarding the webhook to a specified target address.
The intended usage is to host this on a public Internet-facing server/service, which can then forward the webhook to a different, more protected VM via different routes.
flowchart LR
subgraph gh[Github]
webhook[Send webhook]
end
subgraph midnet[Accessible VM]
fwd[Webhook Forwarder]
end
subgraph vpn[VPN]
jenkins[Jenkins]
end
webhook -->|Public internet| fwd
fwd --> |Private link| jenkins
flowchart LR
subgraph gh[Github]
webhook[Send webhook]
end
subgraph ngrok[Ngrok]
ngroker[Ngrok app]
end
subgraph compose[Docker Compose]
subgraph forwarder[Forwarder, port 8000]
fwd[Webhook Forwarder]
end
subgraph receiver[Receiver, 9001]
rec[Receiver logs output]
end
end
webhook --> |Internet| ngroker
ngroker --> |Ngrok forwarder| fwd
fwd --> |Webhook sent if valid| rec
Step 1: Ngrok startup
# start ngrok on port 8000
ngrok http 8000
Step 2: Configure your Github Webhook
- Point your github webhook URL to your ngrok url, appending "/forward_webhook" at the end to use the correct endpoint
- Configure your webhook secret to a particular value. Use an actual token, or a placeholder (e.g. "test").
For a real setup, you should actually configure these as environment variables. For testing, configure them directly in main.py near lines 33-38.
As TARGET_URL
, leave this to http://receiver:9001/receive_webhook
to run the example testing.
As WEBHOOK_TOKEN_SECRET
, set whatever you configured in your webhook.
TARGET_URL = http://127.0.0.1:7000
WEBHOOK_TOKEN_SECRET = "test"
Bring up two instances of webhook_forwarder, one acting as forwarder, one as receiver.
docker compose up --build
You can do this in your Github, either as "redeliver" if you already had sent one, or by committing to the repository.
- Github webhook is triggered, and is sent to the ngrok.url/forward_webhook endpoint configured earlier.
- Ngrok receives this and sends it to the 'forwarder' instance you have running on port 8000.
- If correctly configured, this instance should log the IP and signature of the message, and log whether it is forwarded (or an appropriate error message, on failure).
- The 'receiver' instance you have running on port 7000 receives the webhook and logs out its contents.
Sample terminal output after running the example with some less important lines cleaned:
Attaching to webhook_forwarder, webhook_receiver
webhook_forwarder | INFO: Uvicorn running on http://0.0.0.0:80 (Press CTRL+C to quit)
webhook_receiver | INFO: Uvicorn running on http://0.0.0.0:9001 (Press CTRL+C to quit)
webhook_forwarder | Environment configuration variables not found, using test value defaults.
webhook_receiver | Environment configuration variables not found, using test value defaults.
webhook_forwarder | Starting new HTTPS connection (1): api.github.com:443
webhook_receiver | Starting new HTTPS connection (1): api.github.com:443
webhook_forwarder | https://api.github.com:443 "GET /meta HTTP/1.1" 200 None
webhook_receiver | https://api.github.com:443 "GET /meta HTTP/1.1" 200 None
webhook_forwarder | INFO: Application startup complete.
webhook_receiver | INFO: Application startup complete.
# webhook is sent from github
webhook_forwarder | sender_ip/x_ip: 172.22.0.1/140.82.115.116 is valid!
webhook_forwarder | payld_signature: sha256=<signature>
webhook_forwarder | local_signature: sha256=<signature>
webhook_forwarder | Signature is valid, forwarding request...
webhook_forwarder | Starting new HTTP connection (1): receiver:9001
webhook_receiver | Webhook received, contents logged under debug
webhook_forwarder | http://receiver:9001 "POST /receive_webhook HTTP/1.1" 200 29
webhook_forwarder | INFO: 172.22.0.1:59160 - "POST /forward_webhook HTTP/1.1" 200 OK
webhook_receiver | INFO: 172.22.0.2:41668 - "POST /receive_webhook HTTP/1.1" 200 OK
The application offers the following endpoints:
- POST: /forward_webhook - target to which a GitHub webhook delivery can be pointed.
- POST: /receive_webhook - testing endpoint to help troubleshoot your setup, simply takes and outputs a given JSON payload.
- GET: /status - aliveness check, returns status:alive and a checksum based on the main.py to help with broad version tracking.