Example: Handling a GitHub Webhook

This example demonstrates how to create a secure webhook to process events from GitHub, such as newly opened issues. The script verifies the request signature to ensure it genuinely comes from GitHub.

The Script: github.sh

This script is designed to receive a GitHub webhook payload. It performs two key actions:

  1. Validates the Signature: It uses a secret key to compute the HMAC-SHA1 signature of the request body and compares it to the X-Hub-Signature header sent by GitHub.
  2. Parses the Payload: It uses jq to parse the JSON payload and extract information. In this case, it checks if an issue was opened and creates a notification.
#!/bin/sh

# Functions
die() { echo "error: $1" 1>&2 ; exit 1; }
confDie() { echo "error: $1 Check the server configuration!" 1>&2 ; exit 2; }
debug() {
  [ "$debug" = "true" ] && echo "debug: $1"
}

# Validate global configuration
[ -z "$GITHUB_SECRET" ] && confDie "GITHUB_SECRET not set."

# Validate Github hook signature
signature=$(echo -n "$1" | openssl sha1 -hmac "$GITHUB_SECRET" | sed -e 's/^.* //')
[ "sha1=$signature" != "$x_hub_signature" ] && die "bad hook signature"

# Validate parameters
payload=$1
[ -z "$payload" ] && die "missing request payload"
payload_type=$(echo $payload | jq type -r)
[ $? != 0 ] && die "bad body format: expecting JSON"
[ ! $payload_type = "object" ] && die "bad body format: expecting JSON object but having $payload_type"

debug "received payload: $payload"

# Extract values
action=$(echo $payload | jq .action -r)
[ $? != 0 -o "$action" = "null" ] && die "unable to extract 'action' from JSON payload"

# Do something with the payload:
# Here, create a simple notification when an issue has been opened.
if [ "$action" = "opened" ]
then
  issue_url=$(echo $payload | jq .issue.url -r)
  sender=$(echo $payload | jq .sender.login -r)
  echo "notify: New issue from $sender: $issue_url"
fi

Setup Instructions

  1. Place the Script: Save the script above as scripts/github.sh and make it executable (chmod +x scripts/github.sh).

  2. Set the Secret: The script requires a secret key to validate signatures. This secret must be passed to webhookd as an environment variable. A secure way to do this is with a .env file and Docker Compose.

    File: .env

    GITHUB_WEBHOOK_SECRET=a_very_long_and_random_secret_string

  3. Configure Docker Compose: Use the distrib image since this script requires jq and openssl.

    File: docker-compose.yml

    version: "3.6"
    
    services:
      webhookd:
        image: ncarlier/webhookd:latest-distrib
        container_name: webhookd
        restart: always
        ports:
          - "8080:8080"
        env_file:
          - .env
        environment:
          # Pass the secret from the .env file to the container environment
          - GITHUB_SECRET=${GITHUB_WEBHOOK_SECRET}
          - WHD_NOTIFICATION_URI=... # Your notification channel URI
        volumes:
          - ./scripts:/scripts

  4. Configure GitHub Webhook:

    • In your GitHub repository, go to Settings > Webhooks > Add webhook.
    • Payload URL: Set this to your public webhookd URL, e.g., http://your-server.com:8080/github.
    • Content type: Select application/json.
    • Secret: Enter the same secret string you defined in your .env file.
    • Which events would you like to trigger this webhook? Select "Let me select individual events." and check Issues.
    • Click Add webhook.

Now, whenever a new issue is opened in your repository, GitHub will send a signed webhook to your server, which will trigger the script and send you a notification.