How to enable HTTPS for my marketplace

I'm building a marketplace platform that enables many merchants to sell to their customers. At the moment I serve stores on my own domain like so https://storeA.mydomain.com or https://storeB.mydomain.com (I'm able to use the subdomain to distinguish which store is to be served). However, I'd like to enable my merchants to use their own domains on my platform whilst being able to secure the site over HTTPS, how can I achieve this automatically? In the past, I've tried CNAME to CNAME but this doesn't allow HTTPS.

EDIT: If you know of any intermediary services (that offer some sort of domain masking) that the merchants would be able to use that would be great!


Solution 1:

It should be possible to automate this with Let's Encrypt using Certbot, but I'm afraid there's no existing ready for use solution for this. Therefore, it requires some scripting, and you may need to hire someone, as you asked this question.

I would suggest something like this (examples are for the Apache 2.4 web server):

  1. Create a catch-all configuration that points http://*/.well-known/acme-challenge/ to the same directory with any domain and with the default virtual host for the rest. This is possible with a global Alias that could be placed in /etc/apache2/conf-enabled/acme-challenge.conf:

    <IfModule alias_module>
      Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
    </IfModule>
    
  2. Let your customers input their custom domains for validation and save the information somewhere, with a reference to the correct customer. A database would be ideal for that, wouldn't it. Possibly limit this to one custom hostname (& www) per customer.

  3. Instruct your customers to point their domains to the correct IP address. In the case of a subdomain, a CNAME record will work, but at the domain apex you will need to provide instructions for an A record.

    Here, the examples simply assume every domain has both example.com and www.example.com, but you can modify this according to your requirements.

  4. Do not launch HTTP-01 challenge immediately, but create a script launched with a cronjob or a Systemd timer. The script should first check whether the domains waiting for validation points to your server or not, and launch the ACME challenge only for domains that meets the condition. Otherwise, someone could abuse the feature and make your server perform unnecessary Let’s Encrypt validations.

    #!/bin/bash
    MYSERVERIP="192.0.2.123"
    
    if [ "$#" -ne 1 ]; then
      printf "\n%s\n\n" "Usage: $0 example.com" >&2
      exit 1
    fi
    
    host "$1" 2>&1 > /dev/null
    if [ $? -ne 0 ]; then
      printf "\n%s\n\n" "The given domain is not a valid FQDN." >&2
      exit 1
    fi
    IPAPEX=$(dig "$1" +short | tail -n 1)
    IPWWW=$(dig "www.$1" +short | tail -n 1)
    
    if [ "$IPAPEX" = "$MYSERVERIP" ]; then
      if [ "$IPWWW" = "$MYSERVERIP" ]; then
        certbot certonly --quiet --webroot -w /var/www/letsencrypt -d $1 -d www.$1
        if [ $? -ne 0 ]; then
          printf "\n%s\n\n" "Certbot failed with HTTP-01 challenge." >&2
          exit 1
        fi
      else
        printf "\n%s\n\n" "Failed: www.$1 is not pointing to $MYSERVERIP." >&2
        exit 1
      fi
    else
      printf "\n%s\n\n" "Failed: $1 is not pointing to $MYSERVERIP." >&2
      exit 1
    fi
    
  5. Once the validation is completed, the script may also add the configuration to the web server. You might use a macro, e.g. /etc/apache2/conf-enabled/custdomain-macro.conf:

    <Macro CustomWebShopDomain $customer $domain>
        <VirtualHost *:80>
            ServerName $domain
            ServerAlias www.$domain
            Redirect permanent / https://$domain/
        </VirtualHost>
        <VirtualHost *:443>
            ServerName $domain
            SSLEngine on
            SSLCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem
            SSLCertificateKeyFile /etc/letsencrypt/live/$domain/privkey.pem
            SSLVerifyClient None
            DocumentRoot /path/to/webshop/$customer
        </VirtualHost>
        <VirtualHost *:443>
            ServerName www.$domain
            SSLEngine on
            SSLCertificateFile /etc/letsencrypt/live/$domain/fullchain.pem
            SSLCertificateKeyFile /etc/letsencrypt/live/$domain/privkey.pem
            SSLVerifyClient None
            Redirect permanent / https://$domain/
        </VirtualHost>
    </Macro>
    

    In this case, adding a new customer domain would be simple:

    Use CustomWebShopDomain customerid example.com
    

    Your script might add this line to the configuration and then reload the Apache web server:

    printf "%s\n" "Use CustomWebShopDomain $2 $1" \
      >> /etc/apache2/conf-enabled/custdomain-use.conf
    systemctl reload apache2
    

Be sure to clean out expired domains

Certbot adds all the domains for automatic renewals. If those renewals start to fail, you don't want to keep those domains in the configuration forever. It's best to automate the removal, i.e.

  1. Remove the Certbot configuration /etc/letsencrypt/renewal/example.com
  2. Remove the Use CustomWebShopDomain customerid example.com line.