Set up Let’s Encrypt with multiple domains / redirects

This is to address the problem where you get certificate errors with any redirects or CNAME DNS entries which point to your actual installed Discourse (sub)domain.

If you do not have https configured already (you do if you have done a standard install recently) see Setting up Let’s Encrypt as your first step.

Add something like the following to your app.yml hooks section (towards the end of the file):

  after_ssl:
    - replace:
        filename: "/etc/runit/1.d/letsencrypt"
        from: /-d www.first-domain.com/
        to: "-d www.first-domain.com -d second-domain.com -d www.second-domain.com -d other-domain.com -d www.other-domain.com "

This will allow you to have HTTPS configured for a second domain (+/- third) that will redirect to the correct one without certificate issues.

For example, if you want people who visit https://forum.example.com to be redirected to your forum at https://community.example.com without a certificate error, this is all you need.

41 Likes

Thanks, this work to redirect “www.example.com” to “comunnity.example.com” ?
Or how can i do it?
I have issues with my domain www.example.com, i configure dns to redirect to comunnity.example.com but not work on firefox or chrome.

1 Like

There’s a redirect checker tool to check your redirects.

1 Like

I’m struggling with this a bit. OK a lot.

I decided to experiment with adding a CDN to one of my sites.

After reading the documentation I realised that it was better for my site to move to a subdomain from it’s current apex domain in order to fulfil criteria recommended by Fastly (and the general advice to do so).

So I thought “meh, this is going to be a cinch, I’ve done it before …”. oh yeah?! :sweat_smile:

The site in question is https://starzen.space.

I moved it this weekend to https://www.starzen.space by using the guide here

All went fine, BUT of course I still need to consider the small amount of users I’ve so far picked up on this site, so I wanted to add a redirect.

It is my understanding I then also need the original link issued with a cert, so following this guide (which used to be much more involved?) I added this to app.yml:

hooks:
  after_ssl:
    - replace:
        filename: "/etc/runit/1.d/letsencrypt"
        from: /--keylength/
        to: "-d starzen.space --keylength"
    - replace:
        filename: "/etc/nginx/conf.d/discourse.conf"
        from: /return 301 https.+/
        to: |
          return 301 https://$host$request_uri;
  after_web_config:
    - replace:
        filename: /etc/nginx/nginx.conf
        from: /sendfile.+on;/
        to: |
          server_names_hash_bucket_size 64;
          sendfile on;
    - file:
        path: /etc/nginx/conf.d/discourse_redirect_1.conf
        contents: |
          server {
            listen 80;
            listen 443 ssl;
            server_name starzen.space;
            return 301 $scheme://www.starzen.space$request_uri;
          }

On a rebuild everything seems to go fine.

However, if I try to get to https://starzen.space via a browser I get:

And if I curl:

blah discourse % curl https://starzen.space
curl: (60) SSL: no alternative certificate subject name matches target host name 'starzen.space'
More details here: https://curl.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

I’m pretty sure it’s the certificate at fault, because if I run the same in insecure mode I get:

blah discourse % curl -k https://starzen.space
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>

which I believe is what I want.

I think the modified script file is correct, this is what I have:

root@starship-enterprise:/etc/runit/1.d# cat letsencrypt 
#!/bin/bash
/usr/sbin/nginx -c /etc/nginx/letsencrypt.conf

issue_cert() {
  LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --issue $2 -d www.starzen.space -d starzen.space --keylength $1 -w /var/www/discourse/public
}

cert_exists() {
  [[ "$(cd /shared/letsencrypt/www.starzen.space$1 && openssl verify -CAfile <(openssl x509 -in ca.cer) fullchain.cer | grep "OK")" ]]
}

########################################################
# RSA cert
########################################################
issue_cert "4096"

if ! cert_exists ""; then
  # Try to issue the cert again if something goes wrong
  issue_cert "4096" "--force"
fi
<SNIP>

I have even run this from the command line within the container, prior to which I’ve moved ALL the cert files from the target directory to a backup directory so the right command is run for the double domain:

root@starship-enterprise:/etc/runit/1.d# ./letsencrypt 
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
[Sun 25 Sep 2022 05:50:04 PM UTC] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Sun 25 Sep 2022 05:50:04 PM UTC] Creating domain key
[Sun 25 Sep 2022 05:50:05 PM UTC] The domain key is here: /shared/letsencrypt/www.starzen.space/www.starzen.space.key
[Sun 25 Sep 2022 05:50:05 PM UTC] Multi domain='DNS:www.starzen.space,DNS:starzen.space'
[Sun 25 Sep 2022 05:50:05 PM UTC] Getting domain auth token for each domain
[Sun 25 Sep 2022 05:50:08 PM UTC] Getting webroot for domain='www.starzen.space'
[Sun 25 Sep 2022 05:50:08 PM UTC] Getting webroot for domain='starzen.space'
[Sun 25 Sep 2022 05:50:08 PM UTC] www.starzen.space is already verified, skip http-01.
[Sun 25 Sep 2022 05:50:08 PM UTC] Verifying: starzen.space
[Sun 25 Sep 2022 05:50:12 PM UTC] Pending
[Sun 25 Sep 2022 05:50:15 PM UTC] Success
[Sun 25 Sep 2022 05:50:15 PM UTC] Verify finished, start to sign.
[Sun 25 Sep 2022 05:50:15 PM UTC] Lets finalize the order.
[Sun 25 Sep 2022 05:50:15 PM UTC] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/590255196/128806215177'
[Sun 25 Sep 2022 05:50:16 PM UTC] Downloading cert.
[Sun 25 Sep 2022 05:50:16 PM UTC] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/03ff6b1b76f8516165032c6c2e02205a529b'
[Sun 25 Sep 2022 05:50:17 PM UTC] Cert success.
-----BEGIN CERTIFICATE-----
Lotsofcrazytext
-----END CERTIFICATE-----
[Sun 25 Sep 2022 05:50:17 PM UTC] Your cert is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] Your cert key is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.key 
[Sun 25 Sep 2022 05:50:17 PM UTC] The intermediate CA cert is in  /shared/letsencrypt/www.starzen.space/ca.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] And the full chain certs is there:  /shared/letsencrypt/www.starzen.space/fullchain.cer 
[Sun 25 Sep 2022 05:50:17 PM UTC] Installing key to:/shared/ssl/www.starzen.space.key
[Sun 25 Sep 2022 05:50:17 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space.cer
[Sun 25 Sep 2022 05:50:17 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 579) 35281s
[Sun 25 Sep 2022 05:50:17 PM UTC] Reload success
[Sun 25 Sep 2022 05:50:18 PM UTC] Domains not changed.
[Sun 25 Sep 2022 05:50:18 PM UTC] Skip, Next renewal time is: Wed 23 Nov 2022 10:01:01 AM UTC
[Sun 25 Sep 2022 05:50:18 PM UTC] Add '--force' to force to renew.
[Sun 25 Sep 2022 05:50:18 PM UTC] Installing key to:/shared/ssl/www.starzen.space_ecc.key
[Sun 25 Sep 2022 05:50:18 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space_ecc.cer
[Sun 25 Sep 2022 05:50:18 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 579) 35282s
[Sun 25 Sep 2022 05:50:18 PM UTC] Reload success

Mostly great success!!!, curl is much kinder now and gives me the redirect:

blah discourse % curl https://starzen.space
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.21.6</center>
</body>
</html>

And https://starzen.space in Firefox & Chrome now works, redirecting to the right sub, but I’m still getting the dreaded graphic of doom in Safari, what gives? I’ve even restarted it cleared the caches for this site:

taking a look at the certificate from the browser, I see:

1 Like

I’ve been meaning to take a closer look at this. I think that there are now two places in the let’s encrypt template that the additional domain needs to go. I don’t think your need to make any changes to the nginx stuff because it’s already doing 301 to anything other than the hostname.

What to do is look at the let’s encrypt template and see where it’s putting the hostname and see that you’re doing that with the additional hostname as well.

2 Likes

Yeah, thanks, I did that for completeness and there doesn’t yet seem to be any harm in it, but happy to rebuild without it at some stage.

There appear to be two sets of crytographic files?:

root@starship-enterprise:/shared/letsencrypt# cd starzen.space
root@starship-enterprise:/shared/letsencrypt/starzen.space# ls
backup	ca.cer	fullchain.cer  starzen.space.cer  starzen.space.conf  starzen.space.csr  starzen.space.csr.conf  starzen.space.key
root@starship-enterprise:/shared/letsencrypt/starzen.space# cd ..
root@starship-enterprise:/shared/letsencrypt# cd www.starzen.space
root@starship-enterprise:/shared/letsencrypt/www.starzen.space# ls
backup	    ca.cer	   www.starzen.space.cer   www.starzen.space.csr       www.starzen.space.key
backup_two  fullchain.cer  www.starzen.space.conf  www.starzen.space.csr.conf
root@starship-enterprise:/shared/letsencrypt/www.starzen.space# 

Ah, could it be here (and below)

It’s only including info for the hostname, not the apex?

1 Like

No I think this is correct? There should be one cert and it should work for both www. and apex.

This tool suggests there is only one domain on my public cert (which would be the source of the problem?):

1 Like

You need to change the part of the let’s encrypt that gets the certificate. It needs to request the single certificate for both domains. These instructions used to work, but something changed on how the certificate gets requested, I think. If it gets the right certificate then everything else just works

1 Like

If you run certbot certificates it will show your certificate(s) and the domain(s) it covers. If it doesn’t cover both the apex and the www, you can

  1. run certbot again and have it create a cert for both the apex and the www;

If you go with this option, you run certbot certificates to get the name of the cert you want deleted. Run certbot delete (certificate name of the cert you want deleted). You should be left with only your new cert (with both the apex and the www).

or (easiest)

  1. run `certbot --expand -d existing.domain -d added.domain

This will update your cert with a new cert containing the original domain and those you add with the -d flag.

2 Likes

Jim, certbot command not found? is this part of the standard install and there’s just a path issue?

1 Like

Without actually looking… I think that certbot is what you use normally, but inside the container discourse used acme.

And also related, are you trying this inside or outside the container?

(Also my day is getting full and I might not be able to look s carefully at this as I’d once thought, but it has been on my list)

2 Likes

Agree … one way to do it I believe is this?:

If I use from any suitable linux command line

true | openssl s_client -connect www.starzen.space:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -0777 -ne '@names=/\bDNS:([^\s,]+)/g; print join("\n", sort @names);'

I only get one domain and it’s missing the apex.

1 Like

Inside the container you find the code that is requesting (and renewing?) the certificate and see that it is requesting it for both domains.

1 Like

Yep it is, as per:

LE_WORKING_DIR="${LETSENCRYPT_DIR}" /shared/letsencrypt/acme.sh --issue $2 -d www.starzen.space -d starzen.space --keylength

And you can see this in the log output above.

However, there are some other install cert steps that only include -d www.starzen.space which may be an issue? Although if this cert is built for both, that is maybe not the problem …

1 Like

That’s what I’ve been trying to say. You need to update those as well, it seems. Not sure why now there are multiple ones, but the OP needs to be updated with code to change all of those steps. Or so I think.

3 Likes

Yeah, I’m going to give that a shot, at first manually

2 Likes

Oh good. That’s what I was going to do. Maybe I have tricked you into doing it. :stuck_out_tongue_winking_eye:

1 Like

It’s sensible to try, only I might have to rebuild container to get nano installed … :sweat_smile:

1 Like

No!

 apt-get uodate;apt-get install nano

You can just run that inside the container. I do it all the time (except I use vim)

3 Likes

Unfortunately that didn’t work:

root@starship-enterprise:/etc/runit/1.d# ./letsencrypt 
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
[Mon 26 Sep 2022 12:35:54 PM UTC] Using CA: https://acme-v02.api.letsencrypt.org/directory
[Mon 26 Sep 2022 12:35:54 PM UTC] Creating domain key
[Mon 26 Sep 2022 12:35:56 PM UTC] The domain key is here: /shared/letsencrypt/www.starzen.space/www.starzen.space.key
[Mon 26 Sep 2022 12:35:56 PM UTC] Multi domain='DNS:www.starzen.space,DNS:starzen.space'
[Mon 26 Sep 2022 12:35:56 PM UTC] Getting domain auth token for each domain
[Mon 26 Sep 2022 12:35:59 PM UTC] Getting webroot for domain='www.starzen.space'
[Mon 26 Sep 2022 12:35:59 PM UTC] Getting webroot for domain='starzen.space'
[Mon 26 Sep 2022 12:35:59 PM UTC] www.starzen.space is already verified, skip http-01.
[Mon 26 Sep 2022 12:35:59 PM UTC] starzen.space is already verified, skip http-01.
[Mon 26 Sep 2022 12:36:00 PM UTC] Verify finished, start to sign.
[Mon 26 Sep 2022 12:36:00 PM UTC] Lets finalize the order.
[Mon 26 Sep 2022 12:36:00 PM UTC] Le_OrderFinalize='https://acme-v02.api.letsencrypt.org/acme/finalize/590255196/129044627717'
[Mon 26 Sep 2022 12:36:01 PM UTC] Downloading cert.
[Mon 26 Sep 2022 12:36:01 PM UTC] Le_LinkCert='https://acme-v02.api.letsencrypt.org/acme/cert/03ffc90cecd2f11f2ba386da2d501127aee5'
[Mon 26 Sep 2022 12:36:02 PM UTC] Cert success.
-----BEGIN CERTIFICATE-----
phewbigcert
-----END CERTIFICATE-----
[Mon 26 Sep 2022 12:36:02 PM UTC] Your cert is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] Your cert key is in  /shared/letsencrypt/www.starzen.space/www.starzen.space.key 
[Mon 26 Sep 2022 12:36:02 PM UTC] The intermediate CA cert is in  /shared/letsencrypt/www.starzen.space/ca.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] And the full chain certs is there:  /shared/letsencrypt/www.starzen.space/fullchain.cer 
[Mon 26 Sep 2022 12:36:02 PM UTC] Installing key to:/shared/ssl/www.starzen.space.key
[Mon 26 Sep 2022 12:36:02 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space.cer
[Mon 26 Sep 2022 12:36:02 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 2970) 329s
[Mon 26 Sep 2022 12:36:02 PM UTC] Reload success
[Mon 26 Sep 2022 12:36:03 PM UTC] Domains not changed.
[Mon 26 Sep 2022 12:36:03 PM UTC] Skip, Next renewal time is: Wed 23 Nov 2022 10:01:01 AM UTC
[Mon 26 Sep 2022 12:36:03 PM UTC] Add '--force' to force to renew.
[Mon 26 Sep 2022 12:36:04 PM UTC] Installing key to:/shared/ssl/www.starzen.space_ecc.key
[Mon 26 Sep 2022 12:36:04 PM UTC] Installing full chain to:/shared/ssl/www.starzen.space_ecc.cer
[Mon 26 Sep 2022 12:36:04 PM UTC] Run reload cmd: sv reload nginx
ok: run: nginx: (pid 2970) 331s
[Mon 26 Sep 2022 12:36:04 PM UTC] Reload success

Which still appears to have exposed a cert with a single domain …

true | openssl s_client -connect www.starzen.space:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -0777 -ne '@names=/\bDNS:([^\s,]+)/g; print join("\n", sort @names);'
www.starzen.space
1 Like