This section describes how to obtain and maintain publicly trusted SSL/TLS Certificate that will be used to setup HTTPS for the Nextcloud.
The certificate will be issued by Let’s Encrypt [lets_encrypt] and will be obtained and renewed using Certbot [certbot].
The certificate needs to be obtained before Nextcloud installation, since Nextcloud will only be reachable via HTTPS (not plain HTTP) and the web server won’t start without certificate.
ACME DNS challenge is used for domain validation (needed to issue the initial certificate and for subsequent renewals). One major advantage DNS challenge has over more widely used HTTP challenge is that it doesn’t require your web server to use standard ports: 80 and 443. This allows to host Nextcloud on non-standard port which can be advantageous for two reasons: using non-standard port can dramatically reduce amount of unwanted requests from bots, and it may be the only option if your ISP blocks standard ports 80 and 443.
Certbot can be installed from the PPA maintained by the EFF team (the installation is described in more details in Certbot documentation [1]):
sudo add-apt-repository ppa:certbot/certbot sudo apt install certbot
While its possible to get certificate for the {SB_SUBDOMAIN}
domain and use it to access the Nextcloud,
it may be better to use a subdomain, like for example nextcloud.silverbox.example.com
.
This offers some extra flexibility and you can take advantage of the same origin policy.
In this document, the subdomain for Nextcloud is referred as {SB_NEXTCLOUD_DOMAIN}
.
The first step is to create a CNAME DNS record to point {SB_NEXTCLOUD_DOMAIN}
to {SB_SUBDOMAIN}
.
Thus, you won’t have to also dynamically update host A record for the {SB_NEXTCLOUD_DOMAIN}
,
as it is already updated for the {SB_SUBDOMAIN}
.
The second step is to create a TXT DNS record for the _acme-challenge.{SB_NEXTCLOUD_DOMAIN}
with any value
(the value is just a placeholder and will be replaced with the actual DNS ACME challenge during
domain validation).
To confirm ownership of the domain (for certificate generation and renewal) a DNS challenge will be used. The reason is that HTTP(S) challenge is way to restrictive, in particular, it forces using the standard 80 and 443 ports which is not always desirable. DNS challenge, however, only requires you to be able to create a TXT record with a given content, without enforcing any specific port numbers.
At the moment of writing, Certbot did not support Namesilo API, which is why DNS challenge is done using manual hooks.
Create a directory where all Certbot related scripts will reside:
sudo mkdir /root/silverbox/certbot sudo chmod 700 /root/silverbox/certbot
Inside it, create the dns-challenge-auth.sh
file with the following content:
#!/bin/bash
ACME_SUBDOMAIN="_acme-challenge"
DOMAIN=`awk -F '.' '{print $(NF-1)"."$NF}' <<< "$CERTBOT_DOMAIN"` || exit 1
NS=`dig "$DOMAIN" NS +short | head -1` || exit 2
echo "Performing DNS Challenge for domain: $CERTBOT_DOMAIN in $DOMAIN with authoritative NS $NS"
docker run --rm --network common --cpus="1" -v /root/silverbox/namesilo:/secrets dns-updater -k /secrets/api-key -a update-txt -d "$ACME_SUBDOMAIN.$CERTBOT_DOMAIN" -t "$CERTBOT_VALIDATION" || exit 3
for i in {1..20}; do # (1)
echo "Checking if DNS updated, attempt $i..."
TXT=`dig "@$NS" "$ACME_SUBDOMAIN.$CERTBOT_DOMAIN" TXT +short | sed 's/"//g'`
if [ "$TXT" == "$CERTBOT_VALIDATION" ]; then
echo "Record updated. Waiting extra minute before returning."
sleep 60 # (2)
exit 0
else
echo "Record still contains '$TXT'. Waiting for 1 minute..."
sleep 60 # (3)
fi
done
exit 4
-
20 is the number of attempts to check if the TXT record has been updated.
-
Without this extra wait sometime Certbot won’t pick up the updated TXT value.
-
How long to wait between attempts.
Next, create the dns-challenge-cleanup.sh
file with the following content:
#!/bin/bash
ACME_SUBDOMAIN="_acme-challenge"
DOMAIN=`awk -F '.' '{print $(NF-1)"."$NF}' <<< "$CERTBOT_DOMAIN"` || exit 1
NS=`dig "$DOMAIN" NS +short | head -1` || exit 2
echo "Performing DNS Challenge Cleanup for domain: $CERTBOT_DOMAIN in $DOMAIN with authoritative NS $NS"
docker run --rm --network common --cpus="1" -v /root/silverbox/namesilo:/secrets dns-updater -k /secrets/api-key -a update-txt -d "$ACME_SUBDOMAIN.$CERTBOT_DOMAIN" -t "none" || exit 3 # (1)
echo "Record cleaned up"
-
In this example,
none
value is used as the new TXT record value (since empty value is not allowed).
Assign the following permissions to these files:
sudo chmod 770 /root/silverbox/certbot/dns-challenge-auth.sh sudo chmod 770 /root/silverbox/certbot/dns-challenge-cleanup.sh
The next step is to create renewal hooks that will stop Apache web server before renewal and start it once new certificate is obtained.
To create directories for the renewal hooks run:
sudo certbot certificates
Create the /etc/letsencrypt/renewal-hooks/post/nextcloud-web-restart.sh
file with the following content:
#!/bin/bash
if [ "$CERTBOT_DOMAIN" = "{SB_NEXTCLOUD_DOMAIN}" ]; then # (1)
echo "Restarting Nextcloud web server"
docker compose -f /root/silverbox/containers/nextcloud/docker-compose.yml restart nextcloud-web
else
echo "Skipping Nextcloud web server restart - different domain: $CERTBOT_DOMAIN"
fi
-
Replace
{SB_NEXTCLOUD_DOMAIN}
with the actual domain name.
This script will be executed automatically by Certbot during the renewal of a certificate, and if the renewal is for the Nextcloud certificate it will restart Nextcloud’s web server to use the new certificate.
And assign the following permissions to this file:
sudo chmod 770 /etc/letsencrypt/renewal-hooks/post/nextcloud-web-restart.sh
Finally, install the dnsutils
package which contains dig
tool:
sudo apt install dnsutils
To test that domain validation and certificate renewal works, it is possible to use Let’s Encrypt test server to generate test (not trusted) certificate.
To get test certificate run:
sudo certbot certonly --test-cert \ --agree-tos \ -m {SB_EMAIL} \ # (1) --manual \ --preferred-challenges=dns \ --manual-auth-hook /root/silverbox/certbot/dns-challenge-auth.sh \ --manual-cleanup-hook /root/silverbox/certbot/dns-challenge-cleanup.sh \ --must-staple \ -d {SB_NEXTCLOUD_DOMAIN} # (2)
-
Replace
{SB_EMAIL}
with the email address you wish to use for certificate generation. -
Replace
{SB_NEXTCLOUD_DOMAIN}
with the actual domain name.
Note
|
This may take a while. |
To view information about the generated certificate:
sudo certbot certificates
To test certificate renewal:
sudo certbot renew --test-cert --dry-run --cert-name {SB_NEXTCLOUD_DOMAIN}
To revoke and delete the test certificate:
sudo certbot revoke --test-cert --cert-name {SB_NEXTCLOUD_DOMAIN}
To get the real certificate run:
sudo certbot certonly \ --agree-tos \ -m {SB_EMAIL} \ --manual \ --preferred-challenges=dns \ --manual-auth-hook /root/silverbox/certbot/dns-challenge-auth.sh \ --manual-cleanup-hook /root/silverbox/certbot/dns-challenge-cleanup.sh \ --must-staple \ -d {SB_NEXTCLOUD_DOMAIN}