CertBot, bundled NGINX and ACME HTTP-01 challenge

Hello again,

Is it possible to use Certbot and the bundled NGINX to manage Let’s Encrypt certificates for the web UI? Is it safe to create files under /var/opt/firezone/nginx/etc/sites-enabled? Would them be wiped by Chef on reconfigure or restart?


Hi @lucas8831, i manage to create a certificate using certbot to cloudflare
After the successful generation of the certificate, I changed my /etc/firezone/firezone.rb, since this its recommended to ensure that will be able to update the certificate using certbot if needed.

default['firezone']['ssl']['directory'] = '/etc/letsencrypt/live/firezone.mydomain.co'
default['firezone']['ssl']['enabled'] = true
default['firezone']['ssl']['certificate'] = '/etc/letsencrypt/live/firezone.mydomain.co/fullchain.pem'
default['firezone']['ssl']['certificate_key'] = '/etc/letsencrypt/live/firezone.mydomain.co/privkey.pem'
default['firezone']['ssl']['ciphers'] = 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH'

Here is exact steps that can be used if you are using the CloudFlare DNS validation plugin (assuming Ubuntu):

Install certbot (I am using snap) and the CloudFlare plugin:

snap install core
snap refresh core
snap install --classic certbot
ln -s /snap/bin/certbot /usr/bin/certbot
snap set certbot trust-plugin-with-root=ok
snap install certbot-dns-cloudflare

In your CloudFlare account, create a token that has DNS read/write access to the DNS zone required.

Create the letsencrypt directory and store the CloudFlare token in it and fix permissions (set 123123 to the token generated):

mkdir /etc/letsencrypt
 echo "dns_cloudflare_api_token = 123123" > /etc/letsencrypt/cloudflare.ini
chmod 0600 /etc/letsencrypt/cloudflare.ini

Note I put a space before echo; this stops it getting written to the shell history file.

Finally you can request the certificate:

certbot certonly \
  --agree-tos \
  --no-eff-email \
  -m "my-contact-email@domain.com" \
  --deploy-hook "firezone-ctl restart nginx" \
  --key-type ecdsa \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d "my-vpn-hostname.domain.com"

Then you can use the above configuration provided by @aschiavon.

Thanks for the answers, @aschiavon and @gbe0 ! Just now I realized I made a mistake and I wasn’t precise enough in my original post.

I don’t have issues configuring it; in fact, my own Firezone is already deployed with Let’s Encrypt. I got a cert before installing it using certbot certonly --standalone. I can’t do that again without stopping the bundled NGINX now tho, as the ports will collide, and I don’t intend to change the bundled NGINX to listen on non-default ports: it isn’t nice for the users to visit https://firezone.invalid:8443/.

The situation now is:

  • I don’t feel too inclined to use a DNS-based solution, as that would mean granting this EC2 instance permissions for Route53. I can probably make it pretty tight, but I don’t have a lot of desire to fiddle with IAM policies right now, so it would be good if a workaround can be found.
  • Given that we’re already running an NGINX, having the possibility to use Certbot with --webroot would be great.

So the question should be: is it possible to configure the bundled NGINX config to handle /.well-known/acme-challenge so Certbot can be used only with --webroot?

Understood, that makes sense. There is already a feature request for this (#524). Edit: Linked to wrong issue sorry…

It seems that any files you create in /var/opt/firezone/nginx/etc/sites-enabled do not get wiped out (however changes to the phoenix file will be reverted) by a reconfigure. Based on that you should be able to create a file loaded before the phoenix one (eg. /var/opt/firezone/nginx/etc/sites-enabled/00-acme) with something like:

server {
  listen 80;
  listen [::]:80;

  ## Change to your server hostname
  server_name vpn.mydomain.com;

  ## Serve acme requests from webroot directory /some/directory
  location ^~ /.well-known/acme-challenge/ {
    default_type "text/plain";
    root /some/directory;

  ## 404 acme directory direct hits
  location = /.well-known/acme-challenge/ {
    return 404;

  ## Force all other requests to HTTPS
  location / {
    limit_req zone=firezone;
    if ($http_x_forwarded_proto != 'https') {
      return 301 https://$server_name$request_uri;


I wouldn’t rely on this to work in future though as I don’t see this as a documented “feature”.

Thanks for this @gbe0 . It’s good enough for now.