Configuring Nginx to Proxy Webhooks
In my last post, I set up a workflow that allows me to make changes to my website in a local git repository, push it to GitHub, and have my remote server build the static content with Hugo and serve up the changes. On the remote server, I handled the GitHub hooks through a small Golang server called webhook
, but wasn’t really happy with needing to open up a separate port (it defaults to 9000) on my server firewall. In the grand scheme of things, it probably doesn’t matter much from a security standpoint, but I thought I could do a bit better and learn something in the process of configuring Nginx to proxy the requests through HTTPS.
When I set up my website, I had already used Let’s Encrypt to enable HTTPS by default. As part of that process, I used the certbot
tool they provide to automatically modify my Nginx server block configuration, but now I needed to go back and modify them by hand.
Starting configuration
Firewall status
At the end of the last post, I had port 9000 open through ufw to enable the webhooks server:
$ sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
9000 ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
9000 (v6) ALLOW Anywhere (v6)
Nginx configuration
My starting Nginx configuration looked like this, based on these instructions and what Certbot changed:
server {
# Redirect www to non-www. Hugo doesn't like subdomains
if ($host = www.ansonvandoren.com) {
return 301 https://ansonvandoren.com$request_uri;
}
root /var/www/ansonvandoren.com/html;
index index.html index.htm index.nginx-debian.html;
server_name www.ansonvandoren.com ansonvandoren.com;
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/ansonvandoren.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/ansonvandoren.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
# Redirect to HTTPS (and also redirect www to non-www)
if ($host = www.ansonvandoren.com) {
return 301 https://ansonvandoren.com$request_uri;
} # managed by Certbot
# Redirect to HTTPS
if ($host = ansonvandoren.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name ansonvandoren.com www.ansonvandoren.com
return 404; # managed by Certbot
}
Actually, it was quite a bit messier than that when I first looked, but I did some cleanup and rearranging through trial and error until I got things looking the way I wanted.
webhooks server
My webhooks
server was listening on 0.0.0.0, port 9000
GitHub configuration
My website repo on GitHub had a webhook set up with:
- Payload URL: http://ansonvandoren.com:9000/hooks/redeploy
- Content type: application/json
- Enable SSL verification turned on
- Triggering event set to ‘Just the push event’
What I wanted instead
I had two main things I wanted to accomplish with these changes:
- Close port 9000 on
ufw
- Change my Payload URL on GitHub to use HTTPS (and not require port 9000)
How I did it
In hindsight, this was actually not a difficult problem to solve, but it did take quite a bit of trial and error, and I couldn’t find any examples that accomplished exactly what I was looking for.
Close the firewall port
Since I’m the only one pushing commits to the website repo, I won’t be interrupting anything if I close the port now.
$ sudo ufw delete allow 9000
Rule deleted
Rule deleted (v6)
$ sudo ufw status
Status: active
To Action From
-- ------ ----
OpenSSH ALLOW Anywhere
Nginx Full ALLOW Anywhere
OpenSSH (v6) ALLOW Anywhere (v6)
Nginx Full (v6) ALLOW Anywhere (v6)
So far, so good…
Add Nginx proxy
This took me the most time to sort out, but it’s relatively simple.
$ sudo vim /etc/nginx/sites-available/ansonvandoren.com
In my first server block (the one listening on 443), I added the highlighted lines below
server {
# Redirect www to non-www. Hugo doesn't like subdomains
if ($host = www.ansonvandoren.com) {
return 301 https://ansonvandoren.com$request_uri;
}
root /var/www/ansonvandoren.com/html;
index index.html index.htm index.nginx-debian.html;
server_name www.ansonvandoren.com ansonvandoren.com;
# Forward webhook requests
location /hooks/ {
proxy_pass http://127.0.0.1:9000/hooks/;
}
location / {
try_files $uri $uri/ =404;
}
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/ansonvandoren.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/ansonvandoren.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
# Redirect to HTTPS (and also redirect www to non-www)
if ($host = www.ansonvandoren.com) {
return 301 https://ansonvandoren.com$request_uri;
} # managed by Certbot
# Redirect to HTTPS
if ($host = ansonvandoren.com) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name ansonvandoren.com www.ansonvandoren.com
return 404; # managed by Certbot
}
Simple, right? If any web requests come in over HTTPS on the /hooks path, they get proxied to localhost:9000, which is where the webhooks server is already listening.
Update GitHub
Almost done, and just need to let my GitHub webhook know about the changes. Back on the same settings page, I just needed to change the Payload URL to:
- Payload URL: https://ansonvandoren.com/hooks/redeploy
To prevent you needing to scroll back up, I just removed the ‘:9000’ port identifier, and changed the http:// to https://
Testing it out
To make sure everything worked after my changes, I made some edits to my site locally, committed them, and pushed to GitHub.
The webhook service looks good:
$ systemctl --user status webhook
● webhook.service - Simple Golang webhook server
Loaded: loaded (/home/bloguser/.config/systemd/user/webhook.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2019-01-21 00:40:37 UTC; 1h 40min ago
Main PID: 851 (webhook)
CGroup: /user.slice/user-1000.slice/user@1000.service/webhook.service
└─851 /usr/local/bin/webhook -ip 127.0.0.1 -hooks /opt/hooks/hooks.json -verbose
Jan 21 01:22:00 ansonvandoren webhook[851]: Pages | 20
Jan 21 01:22:00 ansonvandoren webhook[851]: Paginator pages | 0
Jan 21 01:22:00 ansonvandoren webhook[851]: Non-page files | 0
Jan 21 01:22:00 ansonvandoren webhook[851]: Static files | 5
Jan 21 01:22:00 ansonvandoren webhook[851]: Processed images | 0
Jan 21 01:22:00 ansonvandoren webhook[851]: Aliases | 7
Jan 21 01:22:00 ansonvandoren webhook[851]: Sitemaps | 1
Jan 21 01:22:00 ansonvandoren webhook[851]: Cleaned | 0
Jan 21 01:22:00 ansonvandoren webhook[851]: Total in 99 ms
Jan 21 01:22:00 ansonvandoren webhook[851]: [webhook] 2019/01/21 01:22:00 [0fa3ba] finished redeploy
A quick check of the webpage shows the changes propagated correctly. Success!