How to setup Openresty/Nginx to auto generate SSL certificates for all your registered domains/subdomains

By Ghouse Mohamed On September 06 2020

Generating SSL certificates using Let's Encrypt and Certbot has long been the norm. It is often used as a default choice when it comes to generating SSL certificates. Certbot makes it intuitive and seamless to generate SSL certificates for any site we wish.

What if we wanted to generate SSL certificates on the fly, entirely automating the generation of such certificates on-demand. This is made possible using Openresty as a webserver.

Introducing OpenResty

Openresty is a superset of Nginx, with many carefully written Lua Libraries, and 3rd party Nginx modules. It is designed to help developers build dynamic web gateways, web services and web applications. Openresty turns the nginx server into a powerful app, using the Lua programming language underneath to script various existing nginx C modules. They are highly performant servers capable of handling 10k ~ 1000k+ connections in a single box. Applications of Openresty include building dynamic web portals, web gateways, web application firewalls and dynamic web sites.

We will be using lua-resty-auto-ssl plugin for openresty to handle SSL registrations and renewal inside Openresty with Let's encrypt. This OpenResty plugin automatically and transparently issues SSL certificates from Let's Encrypt as requests are received.

This is how a typical request lifecycle would look like using this plugin:

  1. A SSL request for a SNI hostname is received.
  2. If the system already has a SSL certificate for that domain, it is immediately returned (with OCSP stapling).
  3. If the system does not yet have an SSL certificate for this domain, it issues a new SSL certificate from Let's Encrypt. Domain validation is handled for you. After receiving the new certificate (usually within a few seconds), the new certificate is saved, cached, and returned to the client (without dropping the original request).

Openresty Installation

Let's make sure we have certbot installed:

sudo apt-get update apt install certbot

Now that we have certbot installed, we can proceed with the openresty installation.

Import GCP key from official openresty server:

wget -qO - https://openresty.org/package/pubkey.gpg | sudo apt-key add -

Add openresty repository using the GCP key. After installing the software-properties-common

sudo apt-get -y install software-properties-common

and run,

sudo add-apt-repository -y "deb http://openresty.org/package/ubuntu $(lsb_release -sc) main"

Install openresty using apt

sudo apt-get install openresty

Run sudo service openresty start to start the Openresty service.

Visit the server's public IP address to check if Openresty was successfully installed. You should get a screen looking like this:

Openresty-SSL

If you already have Nginx running, then you might have to disable and stop the already running Nginx service like so:

sudo systemctl disable nginx

sudo systemctl stop nginx

Now that we have Openresty running as a service, we can go ahead and install LuaRocks, and the associated openresty plugin lua-resty-auto-ssl which we are using to auto generate our SSL certificates.

LuaRocks & lua-resty-auto-ssl Installation

Run these commands to install LuaRocks and lua-resty-auto-ssl

sudo apt-get install -y luarocks

sudo apt-get install -y luarocks

sudo luarocks install lua-resty-auto-ssl

After running these commands in your terminal you should see this:

1
You should see this : lua-resty-auto-ssl 0.13.1-1 is now installed in /usr/local (license: MIT)

Configure Openresty along with lua-resty-auto-ssl Plugin

First let's create a directory where the regular and fallback certificates can be stored

sudo mkdir /etc/resty-auto-ssl

After this, we want to give the concerned user running the nginx workers permission to this directory. In this case, we are going with root. Replace this with your appropriate user.

sudo chown root /etc/resty-auto-ssl

After this we want to generate fallback certificates using openssl, and this fallback certificate will be used in case a certificate for a subdomain or domain could not be generated.

1
2
3
4
openssl req -new -newkey rsa:2048 -days 3650 -nodes -x509 \
   -subj '/CN=sni-support-required-for-valid-ssl' \
    -keyout /etc/ssl/resty-auto-ssl-fallback.key \
   -out /etc/ssl/resty-auto-ssl-fallback.crt

Now we are ready to modify the nginx.conf file under /etc/openresty, and set it up along with lua-resty-auto-ssl

cd /etc/openresty

Let's remove the default nginx configuration file and write our own from scratch.

1
2
rm nginx.conf
vi nginx.conf

Insert the following in the nginx.conf file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#Important to note the user. Change the name of user in charge accordingly

user root;

events {
  worker_connections 1024;
}

http {
  lua_shared_dict auto_ssl 1m;
  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("allow_domain", function(domain)
      return true
    end)
    auto_ssl:init()
  }

  init_worker_by_lua_block {
    auto_ssl:init_worker()
  }

  server {
    listen 443 ssl;
    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;
    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()
      }
    }
  }
}

Restart the Openresty service:

sudo service openresty restart

And voila, SSL certificates will get automatically get generated for any domain/subdomain you visit that is pointing to your public server IP. If you wish to restrict the automatic generation of SSL certificates to only certain subdomains, you can certainly do so by modifying the allow_domain property like so:

1
2
3
auto_ssl:set("allow_domain", function(domain, auto_ssl, ssl_options, renewal)
  return ngx.re.match(domain, "^(example.com|example.net)$", "ijo")
end)

You can visit lua-resty-auto-ssl's documentation to introduce custom options for your need.

With this, you now don't have to worry about generating SSL certificates for each of your subdomain individually. If you wish to change the name of your subdomain at any point in time, you do so without the worry of having to generate SSL certificate for it. Openresty will now take care of that for you.