Skip to content

Commit f9ef91c

Browse files
committed
Github webhook receiver, with security features
1 parent 216a1af commit f9ef91c

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from gitconsensusservice import app
2+
from flask import abort, request
3+
import hmac
4+
from hashlib import sha1
5+
from ipaddress import ip_address, ip_network
6+
import json
7+
import logging
8+
import requests
9+
10+
11+
@app.route('/githook', methods=['POST'])
12+
def githook():
13+
14+
# Restrict to only Github IP Addresses unless in Debug mode
15+
if 'DEBUG' not in app.config or not app.config['DEBUG']:
16+
trusted_proxies = {'127.0.0.1'} # define your own set
17+
route = request.access_route + [request.remote_addr]
18+
remote_addr = next((addr for addr in reversed(route) if addr not in trusted_proxies), request.remote_addr)
19+
ip_remote_address = ip_address(remote_addr)
20+
whitelist = requests.get('https://api.github.com/meta').json()['hooks']
21+
for valid_ip in whitelist:
22+
if ip_remote_address in ip_network(valid_ip):
23+
break
24+
else:
25+
logging.warning('Github webhook must come from a Github IP Address')
26+
abort(403)
27+
28+
# If secret is set use it to validate message.
29+
if 'webhook_secret' in app.config:
30+
secret = app.config.get['webhook_secret']
31+
header_signature = request.headers.get('X-Hub-Signature')
32+
if header_signature is None:
33+
logging.warning('Github webhook received without expected authentication signature')
34+
abort(403)
35+
36+
sha_type, signature = header_signature.split('=')
37+
if sha_type != 'sha1':
38+
logging.warning('Github webhook sent with invalide authentication algorithm')
39+
abort(501)
40+
41+
mac = hmac.new(str(secret), msg=request.data, digestmod='sha1')
42+
if not hmac.compare_digest(str(mac.hexdigest()), str(signature)):
43+
logging.warning('Github webhook sent without valid signature')
44+
abort(403)
45+
46+
if 'X-Github-Event' not in request.headers:
47+
logging.warning('Github webhook is missing event header')
48+
abort(400)
49+
50+
event = request.headers.get('X-GitHub-Event')
51+
payload = request.get_json()
52+
53+
if event == 'ping':
54+
return json.dumps({'msg': 'pong'})

gitconsensusservice/www.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from flask import Flask, session, redirect, url_for, escape, request, render_template, flash, send_from_directory
22
from gitconsensusservice import app
3+
import gitconsensusservice.routes.webhooks
34

45

56
@app.route('/')

0 commit comments

Comments
 (0)