Wildcard SSL and Beyond

This is the third article in the Taming Nginx series, where we explore creative ways to configure Nginx. In this article, we focus on securing your web server with SSL, wildcard certificates, and we touch on mutual SSL.

So far, we've covered:

It might be worth reading In the Beginning... We Shared (whether we liked it or not!) which is a light-hearted exploration of how self-hosting evolved over the decades.


What in the world is SSL and where did TLS come from?

SSL (Secure Sockets Layer) was introduced in the mid-1990s to secure internet communications but was deprecated in favour of TLS (Transport Layer Security), which is far more secure and efficient. While SSL versions are now obsolete, TLS 1.2 and 1.3 remain widely used, providing encryption, integrity, and authenticity.

Fun Fact: "SSL" is still colloquially used to refer to TLS, even though SSL is no longer in use.

Today, we have services like Cloudflare and Let’s Encrypt that have made securing your site or service so much easier.

But this wasn’t always the case. In the early days of web hosting, you had to purchase certificates from providers at exorbitant prices, ranging from $100 to over $1,500 per year, depending on the reputation of the Certificate Authority!

Thankfully, if you’re looking for the simplest way to secure your site today, Cloudflare offers an outstanding free solution.

Cloudflare

To be fair, when Cloudflare was first introduced, it didn’t yet issue certificates for your domain. That said, it was already a game-changer, excelling at protecting your site from DDoS attacks and offering useful 3rd party integrations like analytics.

SSL wasn’t even part of the picture yet, but Cloudflare still stood out as a critical tool for website owners.

The way it worked was simple: you’d buy a domain from a registrar of your choice, then point your domain’s name servers (NS Records) to Cloudflare.

This allowed Cloudflare to act as your DNS provider and deliver its features seamlessly.

Perhaps the biggest benefit? You didn’t have to pay a cent. And when Cloudflare introduced automated SSL provisioning, it added a layer of security that elevated its value to an entirely new level.

At the time of this article, there’s still no service quite like it that offers so much - for free.

Getting Started with Cloudflare

Setting up Cloudflare for your domain is straightforward:

  1. Sign Up: Create a free Cloudflare account.

  2. Add Your Domain: Register your domain with Cloudflare and follow the step-by-step instructions to configure DNS settings.

  3. Update Your Name Servers: Point your domain’s name servers (NS Records) to Cloudflare as directed during setup.

And that’s it, really! Once you’ve completed the setup, you’ll have DDoS protection and SSL out of the box. However, Cloudflare offers a few options that are worth mentioning to get the most out of your configuration.

DNS Proxied vs. DNS Only

  • Proxied: This routes traffic through Cloudflare’s network, enabling features like DDoS protection, caching, and SSL termination.

  • DNS Only: This points traffic directly to your origin server, bypassing Cloudflare. Use this for services like mail servers or anything incompatible with Cloudflare’s proxy.

SSL Options Cloudflare offers several SSL modes, depending on your requirements:

  • Off: No SSL (not recommended).

  • Flexible: Encrypts traffic between the browser and Cloudflare but leaves traffic to your origin server unencrypted (not secure).

  • Full: Encrypts traffic end-to-end but skips validation of the origin server’s certificate.

  • Full (Strict): The most secure option, encrypting traffic end-to-end while validating the origin server’s certificate.

For a fully secure setup, always aim for Full (Strict) SSL mode if your origin server has a valid certificate (such as one issued by Let’s Encrypt or your own CA). While Cloudflare simplifies SSL for many scenarios, it’s worth knowing how to use Certbot to issue your own certificates, which we’ll dive into next.

Certbot

Let’s Encrypt revolutionized SSL provisioning by making certificates free and accessible to everyone. At the heart of this process is certbot, a powerful CLI tool that automates certificate issuance and renewal.

Fun Fact: certbot—the CLI tool—used to be known as letsencrypt and was really annoying to type out each time! The rebranding was more than welcome, not to mention its expanded capabilities.

There are many tutorials on using Certbot to provision single-domain certificates, so this guide focuses on wildcard certificates. These simplify SSL management by covering all subdomains of a domain, making them ideal for scalable and dynamic setups.

Getting Started with Let's Encrypt

Let’s set up Certbot on Ubuntu with Nginx to automate SSL provisioning. If your configuration differs, the Certbot interactive guide is a great resource. We’ll focus on the basics:

# Update your package list and install snapd (if not already installed)
sudo apt update
sudo apt install snapd

# Install Certbot using snap
sudo snap install --classic certbot

# Verify Certbot installation
certbot --version

# If certbot isn't found, create a symlink to the binary
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# Allow Certbot to use plugins with root access
sudo snap set certbot trust-plugin-with-root=ok

# Install Certbot's Cloudflare DNS plugin
sudo snap install certbot-dns-cloudflare

To get a Cloudflare API Token:

API Tokens in Cloudflare’s DashboardCreate Token → Use the Edit zone DNS template → Select the domain (zone) you want to manage → Copy the token and save it securely.

On your machine, create a cloudflare.ini file:

# Cloudflare API token used by Certbot
dns_cloudflare_api_token = <Your Token>

Save the file in a secure location and set its permissions:

# use sudo depending on where you save it.
chmod 600 ./cloudflare.ini

And now it's time to generate the wildcard certificate:

certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
  -dns-cloudflare-propagation-seconds 60 \
  -d example.com \
  -d *.example.com

The command may take a minute due to the DNS propagation delay, but once complete, your certificates will be saved to:

Nginx Integration

Remember the handy ssl_params file from the first article? That’s where you’ll update the certificate paths with the locations generated above.

To illustrate how this works, here’s a minimal example of an Nginx configuration for a subdomain:

# File: /etc/nginx/sites-available/freebies.example.com
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name freebies.example.com;

    root /srv/freebies.example.com/;
    index index.html;

    # SSL Configuration is the same for every subdomains thanks the
    # wildcard certificate
    include /etc/nginx/ssl_params;

    # Serve files and directories, or return 404
    location / {
        try_files $uri $uri/ =404;
    }
}

This example illustrates how the wildcard certificate makes SSL configuration consistent across subdomains. With this foundation in place, let’s take it a step further and explore enabling Mutual SSL for secure system-to-system communication.

Mutual SSL

Before APIs were everywhere, developers relied on simpler methods like API keys or IP whitelisting to secure system-to-system communication. Mutual SSL improves on this by verifying both the client and server using certificates, ensuring trust at the system level.

It’s important to note that Mutual SSL verifies requests at the transport layer and does not replace user-level authentication methods like OAuth or passwords.

How to Do It with Nginx

Let's take a hypothetical scenario where you want to establish Mutual SSL with two of your subdomains:

Since we’ve already created a wildcard certificate earlier for browser-level verification, that setup remains unchanged. Here, we’ll generate additional certificates specifically for Mutual SSL using OpenSSL:

For a production-grade guide on setting this up, be sure to check out Nginx’s Documentation

# Create a custom CA certificate and private key
openssl req -new -x509 -days 365 -nodes -out ca.crt -keyout ca.key

# Generate a private key and CSR (Certificate Signing Request) for the client
openssl req -new -nodes -out client.csr -keyout client.key

# Sign the CSR with the CA to create the client certificate
openssl x509 \
  -req \
  -in client.csr \               # Input: Client CSR
  -CA ca.crt \                   # CA certificate
  -CAkey ca.key \                # CA private key
  -CAcreateserial \              # Generate a serial number for the certificate
  -out client.crt \              # Output: Client certificate
  -days 365                      # Valid for 365 days

Server Configuration for api.example.com

Update the configuration for api.example.com to include Mutual SSL settings:

# File: /etc/nginx/sites-available/api.example.com
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name api.example.com;

    root /srv/api.example.com/;
    index index.html;

    # Include wildcard certificate
    include /etc/nginx/ssl_params;

    # Mutual SSL settings
    ssl_client_certificate /path/to/ca.crt;
    ssl_verify_client on;

    location / {
        # Enforce client verification by leveraging ssl_verify_client
        error_page 495 = /error/missing_or_invalid_client_cert;
        try_files $uri $uri/ =404;
    }

    # Custom error for missing or invalid certificates
    location = /error/missing_or_invalid_client_cert {
        return 403 "mTLS certificate missing or invalid";
    }
}

Client Configuration for client.example.com

To connect securely to the API, configure the client.example.com subdomain to use the generated certificates:

# File: /etc/nginx/sites-available/client.example.com
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name client.example.com;

    root /srv/client.example.com/;
    index index.html;

    # Include wildcard certificate
    include /etc/nginx/ssl_params;

    # Configure Mutual SSL for outgoing requests to api.example.com
    location /api/ {
        proxy_pass https://api.example.com;

        proxy_ssl_certificate /path/to/client.crt;
        proxy_ssl_certificate_key /path/to/client.key;
        proxy_ssl_trusted_certificate /path/to/ca.crt;
    }
}

Verification Steps

Let’s make sure everything is configured correctly by testing NGINX and verifying the API endpoint.

# Test the NGINX configuration for syntax errors
sudo nginx -t

# Reload NGINX to apply changes (use restart if significant changes were made)
sudo service nginx reload # or restart

# Verify the API with the client certificate and key
curl --cert /path/to/client.crt --key /path/to/client.key https://api.example.com

If everything is set up correctly, the nginx -t test should pass, and the curl command should return a valid response from the API. If something’s off, check Nginx's access.log or error.log for SSL-related issues or misconfigurations.

Finally, open client.example.com in your browser and ensure the WebApp loads without SSL warnings. No red screens? Perfect! Mutual SSL is up and running while keeping browser compatibility intact!


I didn’t anticipate this article being so detailed, but I wanted to share a bit more depth compared to the usual guides out there. This wasn’t meant to be a quick copy-paste style tutorial so I hope you found it informative and useful.

So far, we’ve explored three articles in the Taming Nginx series, and next up, we’ll dive into some Advanced Caching and Scaling Techniques. Stay tuned, and don’t forget to subscribe to the newsletter below to get the latest updates.

Cheers!

0
Subscribe to my newsletter

Read articles from Jason Joseph Nathan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jason Joseph Nathan
Jason Joseph Nathan

Yo! I’m J, your go-to geek at Geekist. With nearly two decades under my belt, I craft high-performance software that’s as sleek as it is functional, specialising in JavaScript/TypeScript and modern full-stack solutions. Beyond code, my world revolves around music, mentoring budding developers, and cracking up my two wonderful daughters. Whether jamming out to Punjabi beats with my wife or leading dynamic teams across continents, I’m all about mixing passion with innovation. Here at Geekist, I share top-notch tutorials, tech wisdom, and a bit of humor to spice up your dev journey. So, whether you’re looking to skill up or just hang out, you’re in the right place. Welcome to our community of creators and thinkers!