Door with a sign that says "Restricted Area, Authorized persons only"

Adding Elegance & Security to Deployment

In the last part, we got a deployment strategy working by mapping our workflow to git branches and setting up a script for automatic deployment. At the end of it I mentioned that our script was not elegant since it would run with any push.

It wouldn’t matter if code was pushed to the branch that our server was interested in. Second, anyone (including bots) could trigger the deployment. Third, a push event on any of the configured repository will trigger the deploy.

Webhook Payload

When you push to your remote git repository, it doesn’t just ping the webhook url, it actually sends a whole bunch of data to your webhook, as well. Here are links to examples of the data sent with each of them:

Except GitHub, where you can choose to send form data, all of them send the data as a JSON in the body of the payload. For consistency, I’m going to ignore the data sent as a form and only use the JSON version.

Checking Repository Information in the Payload

On the example payloads shared above, you can see that GitHub‘s payload contains the git url at repository -> git_url.

GitLab has the git url at repository -> url.

BitBucket‘s payload doesn’t have the git url anywhere, but it does have two keys that are close enough. One is the repository -> links ->html and the other is repository -> full_name. We won’t be able to match the whole git url, but we can match the /team_name/repo_name or

Using this information, we can look into our payload and confirm that the push event was for our repository.

Checking Branch Information in Payload

We only wish to know what branch was this push event for. If it is the branch mapped to the server, we deploy. Else, we do nothing.

In both GitHub & GitLab‘s payload, the branch information is in the value of the ref key.

With BitBucket, it is inside push -> changes -> new -> name.

Using this information, we can figure out if the push event was for the branch our server’s interested in.

Securing Webhooks

There are 3 ways by which you can ensure that the webhook is secure:

  1. By confirming that the request is coming from an expected IP address.
  2. By confirming a security token that you can set up when adding the webhook on GitHub & GitLab. BitBucket doesn’t support adding keys.
  3. Using SSL verification option, when setting up the webhook. For this, you’ll need to have a SSL certificate using letsencrypt (or another signing authority) activated on your server.

The IP address range to whitelist is available both for GitHub and BitBucket. While BitBucket doesn’t use a security token, GitLab sends it, as it is, as a request header and GitHub hashes it and sends it as a request header.

We can check the particular request header, and match with the expected header.

Finally, by turning on the SSL verification, the payload becomes inherently secure.

Why no code?

There’s no PHP code in this tutorial for implementing such validations. That’s because with these additional validations, our script needs a little more abstraction and modularity since it’s going to get a little complicated; not simple anymore. 🙁

However, I have used the concepts and principles in the series to write a deployment tool in PHP. You can check out the heavily commented code for it here:

It’s still a work in progress. I still need to add logging functionality and test it out with GitLab and BitBucket. I’ve tested it with GitHub though. In fact, you’re invited to look at the code, test it out and send in your suggestions and questions as issues here: Better still, you could send in some pull requests.

Now, you’re a pro. Go deploy! 🙂

That’s about it. I hope this series helps you setup your own deployment strategy and tool. If you do, please do share your experience and insights with me.

What do you think?