diff options
author | Ralf Jung <post@ralfj.de> | 2015-02-22 22:58:37 +0100 |
---|---|---|
committer | Ralf Jung <post@ralfj.de> | 2015-02-22 22:58:37 +0100 |
commit | 1a695451f3a3261b0d73884b372f579a594b8313 (patch) | |
tree | af3b5cbc369ecbe503802efcf4aa35c207c8a5bd | |
parent | 5e4cc8e8aed90a3f5464cbdd1656e47bf3fc810e (diff) |
verify the HMAC that GitHub sends
-rw-r--r-- | git_mirror.py | 18 | ||||
-rwxr-xr-x | webhook-core.py | 17 | ||||
-rwxr-xr-x | webhook.py | 11 |
3 files changed, 23 insertions, 23 deletions
diff --git a/git_mirror.py b/git_mirror.py index 0bd4e81..3a65d85 100644 --- a/git_mirror.py +++ b/git_mirror.py @@ -22,7 +22,8 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================== import sys, os, os.path, subprocess -import configparser, itertools, json, re +import configparser, itertools, re +import hmac, hashlib import email.mime.text, email.utils, smtplib mail_sender = "null@localhost" @@ -76,21 +77,13 @@ def send_mail(subject, text, recipients, sender, replyTo = None): s.sendmail(sender, recipients, msg.as_string()) s.quit() -def get_github_payload(): - '''Reeturn the github-style JSON encoded payload (as if we were called as a github webhook)''' - try: - data = sys.stdin.buffer.read() - data = json.loads(data.decode('utf-8')) - return data - except: - return {} # nothing read - class Repo: def __init__(self, name, conf): '''Creates a repository from a section of the git-mirror configuration file''' self.name = name self.local = conf['local'] self.owner = conf['owner'] # email address to notify in case of problems + self.hmac_secret = conf['hmac-secret'].encode('utf-8') self.deploy_key = conf['deploy-key'] # the SSH ky used for authenticating against remote hosts self.mirrors = {} # maps mirrors to their URLs mirror_prefix = 'mirror-' @@ -101,6 +94,11 @@ class Repo: def mail_owner(self, msg): global mail_sender send_mail("git-mirror {0}".format(self.name), msg, recipients = [self.owner], sender = mail_sender) + + def compute_hmac(self, data): + h = hmac.new(self.hmac_secret, digestmod = hashlib.sha1) + h.update(data) + return h.hexdigest() def find_mirror_by_url(self, match_urls): for mirror, url in self.mirrors.items(): diff --git a/webhook-core.py b/webhook-core.py index d4c1ab7..934d41f 100755 --- a/webhook-core.py +++ b/webhook-core.py @@ -24,9 +24,22 @@ #============================================================================== # This is the hook called by GitHub as webhook. It updats the local repository, and then all the other mirrors. -import sys, traceback +import sys, traceback, json from git_mirror import * +def get_github_payload(repo, signature): + '''Return the github-style JSON encoded payload (as if we were called as a github webhook)''' + data = sys.stdin.buffer.read() + verify_signature = repo.compute_hmac(data) + if signature != "sha1="+verify_signature: + raise Exception("You are not GitHub!") + try: + data = json.loads(data.decode('utf-8')) + return data + except ValueError: + return {} # nothing read + + if __name__ == "__main__": # call this with: <reponame> <event name> <signature> repo = None # we will try to use this during exception handling @@ -42,7 +55,7 @@ if __name__ == "__main__": repo = repos[reponame] # now sync this repository - data = get_github_payload() + data = get_github_payload(repo, githubSignature) if githubEvent == 'ping': # github sends this initially print("Content-Type: text/plain") @@ -29,17 +29,6 @@ webhook_core = "/home/git/git-mirror/webhook-core.py" # import urllib.request, urllib.parse, json, os, sys -def is_github(remote_addr): - '''Returns whether the address is a github hook address. This function requires Python 3.3.''' - from ipaddress import ip_address, ip_network - remote_addr = ip_address(ip_network) - github = urllib.request.urlopen('https://api.github.com/meta').read() - github = json.loads(github.decode('utf-8')) - for net in github['hooks']: - if remote_addr in ip_network(net): - return True - return False - # get repository from query string query = os.getenv("QUERY_STRING") query = urllib.parse.parse_qs(query) |