How setup custom domain SSL with Let's Encrypt for changelog pages

How setup custom domain SSL with Let's Encrypt for changelog pages

This article introduces a sequence of more technical articles here on the Blog, articles I want to post more often.

I will divide this article into three parts: problem, search, and solution.

Problem

I wish Changelogfy users could create their changelog pages in their custom domains, such as changelog.yourcompany.com

But the address that the application creates is as follows https://{subdomain}.changelogfy.com

Now you might have thought, but this is very simple, just make a note of a Cname record from changelog.yourcompany.com to https://{subdomain}.changelogfy.com

And you're right, that would work if you didn't need SSL encryption.

Google Chrome, the world's most widely used browser since its version 68 of 2018, is forcing websites to have the https protocol.

Search for the solution

The main initial difficulty was that I didn't even know what to look for, as I was unfamiliar with the problem.

It took many days of reading and searching the internet for content until I began to understand the problem and what possible solutions I would adopt.

To give you an idea, I even thought that the customer should use a service like CloudFlare for example to generate SSL certificates.

But thanks to my persistence I found some solutions over the web.

The solution I most identified and used was Readme.io, but there are others like Shopify for example.

Solution

As I mentioned in the previous phase I chose to use the Readme.io method, which consists of creating a PROXY server in front of our servers.

Changelogfy is fully hosted by Digital Ocean, click here to win free $100.

The server used was OpenResty with Lua Resty Auto SSL enable, i will explain how it works:

Every time a Changelogfy customer enables a custom domain in the admin panel, this information is saved to our databases and also to a permanent REDIS server.

When a visitor first accesses your domain for example feedback.changelogfy.com, openresty will check on Redis if your domain is really a valid changelogfy customer (business rule), if this condition is true it will advance to the next stage.

SSL certificate creation

Here OpenResty also requires a Redis connection, only this time it checks whether the domain already has a certificate or not.

If the domain does not already have a valid SSL certificate it will be created and saved in the Redis records, so you will be able to perform backups, migrate from the server, among other things.

Certificate Renewal

Another important point is certificate renewal, with openresty you can configure the timeout you want to renew existing certificates.

For example, the default expiration value for Lets Encrypt certificates is 90 days, so you can set OpenResty to renew certificates every 60 days, so you won't take unnecessary risks.

Example nginx conf at OpenResty + lua-resty-auto-ssl:

events {
  worker_connections 1024;
}

http {
  lua_shared_dict auto_ssl 10m;
  lua_shared_dict auto_ssl_settings 64k;
  resolver 8.8.8.8 ipv6=off;

  init_by_lua_block {
    auto_ssl = (require "resty.auto-ssl").new()

    auto_ssl:set("storage_adapter", "resty.auto-ssl.storage_adapters.redis")
    auto_ssl:set("redis", {
      host = "REDIS_IP",
      auth = "REDIS_PASS",
      port = "6379"
    })

    auto_ssl:set("allow_domain", function(domain, auto_ssl)
        local redis_connection = auto_ssl.storage.adapter:get_connection()
        local res  = redis_connection:get(domain)

        if not res then
           return false
        end

        if res == ngx.null then
           return false
         end

        return true

    end)
    auto_ssl:init()
  }

  init_worker_by_lua_block {
    auto_ssl:init_worker()
  }
  
  server {
    listen 443 ssl;
    location / {
        resolver 8.8.8.8;  # use Google's open DNS server

        set $path '';
        access_by_lua '
            # business logic here
        ';
        proxy_ssl_server_name on;
        proxy_pass https://$path.changelogfy.com$request_uri;
    }

    ssl_certificate_by_lua_block {
      auto_ssl:ssl_certificate()
    }

    ssl_certificate /etc/ssl/resty-auto-ssl-fallback.crt;
    ssl_certificate_key /etc/ssl/resty-auto-ssl-fallback.key;
  }

  server {
    listen 80 default_server;
    location / {
      return 301 https://$host$request_uri;
    }

    location /.well-known/acme-challenge/ {
      content_by_lua_block {
        auto_ssl:challenge_server()
      }
    }
  }

  server {
    listen 127.0.0.1:8999;
    client_body_buffer_size 128k;
    client_max_body_size 128k;

    location / {
      content_by_lua_block {
        auto_ssl:hook_server()
      }
    }
  }
}

This was my first technical article here on the Changelogfy blog.

If you have any questions, please comment below.

See you in the next article!