
How to Use Let's Encrypt TLS Certificates on GoDaddy Shared Hosting
The Goal
I wanted to update a set of web sites
that are hosted on GoDaddy.
The sites had been there for a while, hosted on "Economy Linux"
platforms that had been the standard when
the sites were created.
In the past year or two GoDaddy had moved forward to their
newer "Economy Linux with cPanel" platform.
The newer platform was based on a significantly newer
distribution, meaning security improvements and possibly
performance improvements.
What's more, the price had dropped, no doubt due to the
rise of Google Cloud Platform.
If I was deploying those sites today,
I would go with the Google Cloud Platform.
This happened in September 2017, when Google was warning
that Chrome would start displaying "Insecure" on plain old
HTTP pages within a few weeks, and a lack of HTTPS would
start to hurt you in search result rankings some time
in the coming months.
The business owner of one of the sites had purchased a
TLS certificate from GoDaddy for about US$ 60/year.
On the other sites I went with free
Let's Encrypt
TLS certificates.
GoDaddy wants to sell certificates to make up for
reduced hosting income, so while they don't block the
use of Let's Encrypt certificates, they certainly don't
help you to do it.
But it's not at all hard to do.
Step 1: Upgrade
The old platform was really old.
It had a file /etc/*-release
which revealed
that it was based on CentOS 5.5!
Its version of OpenSSH was so old that it still supported
nothing but the DSA (or ssh-dss) public-key algorithm
for host keys.
OpenSSH 7.0 and later disabled DSA host keys.
That meant that you no longer could connect from reasonably
current platforms without adding a stanza to the bottom
of your /etc/ssh/ssh_config
file:
$ tail /etc/ssh/ssh_config ## GoDaddy only supports the ssh-dss (DSA) public key algorithm ## for host keys. OpenSSH 7.0 and greater disables it. For ## details see: http://www.openssh.com/legacy.html Host example1.com example2.com example3.com example4.com HostkeyAlgorithms ssh-dss
The newer GoDaddy platform, current as of September 2017, is
CloudLinux OS.
It's really stripped down, with neither
os-release
nor lsb-release
in
/etc
.
CloudLinux OS is fully compatible with CentOS/RHEL packages.
It is also really tightened down.
Your home directory is the only subdirectory
of /home
.
You can only see your own processes in /proc
and with ps
and top
.
The /dev/
directory has very few devices
other than the pseudo-ttys.
No disk devices appear!
It uses
CageFS,
making it similar to a container.
The OpenSSH service is version 5.3, two major releases behind the current, but it's new enough to interoperate with an OpenSSH 7.5 client.
$ ssh -vv myusername@example.com uname -a [... output deleted ...] [... here are versions ...] debug1: Local version string SSH-2.0-OpenSSH_7.5 debug1: Remote protocol version 2.0, remote software version OpenSSH_5.3 [... output deleted ...] [... here are server capabilities ...] debug2: peer server KEXINIT proposal debug2: KEX algorithms: diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 debug2: host key algorithms: ssh-rsa,ssh-dss [... and cipher/MAC offers both ways ...] debug2: ciphers ctos: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se debug2: ciphers stoc: aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour,rijndael-cbc@lysator.liu.se debug2: MACs ctos: hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96 debug2: MACs stoc: hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-sha1-96,hmac-md5-96 [... output deleted ...] [... here's what they agree upon ...] debug1: kex: algorithm: diffie-hellman-group-exchange-sha256 debug1: kex: host key algorithm: ssh-rsa debug1: kex: server->client cipher: aes128-ctr MAC: umac-64@openssh.com compression: none debug1: kex: client->server cipher: aes128-ctr MAC: umac-64@openssh.com compression: none [... output deleted ...]
I had the usual excellent live help over the phone. That's good, because GoDaddy's graphical panels are mysterious.
A roundabout sequence of clicks, copy, paste, and more clicks can get an authorized public key uploaded. That is, the public key matching the private key of a user identity.
Surprisingly, you still can't log in directly even though
you just uploaded a key that was installed in
~/.ssh/authorized_keys
(and, also,
authorized_keys2
for some reason).
You have to go back to the cPanel web interface and do some
clicking that leads to authorizing that new key identity.
You can't see what's going on, but I'm guessing that
you have to get the newly configured user identity listed as
an AllowUsers entry in the sshd_config
file.
CageFS hides the details, in /etc/ssh
you
only see the filese ssh_config
and
moduli
.
Once you can load your private keys into your SSH agent on your client and then make seamless key-based SSH connections, you're ready to move on.
Credit Where Credit Is Due
I created this page so I will have notes on how to do this even if other sites change or disappear.
The deployment steps in the following are based on those on the great TryingToBeAwesome.com site. Some steps on that page came from other contributors. And finally, all this is built on top of work by the authors of the acme.sh tool. That's a shell script implementing the ACME client protocol. ACME or the Automatic Certificate Management Environment is a protocol for automating interactions with certificate authorities.
There is no need for Python or Go or the official
Let's Encrypt client, as acme.sh
is
a pure shell script.
Nor is there any need for root
access.
This gives us an ACME tool for the shared hosting environment.
Step 2: Install acme.sh
Log in to your GoDaddy shared hosting system and run
the following to download and then install
the acme.sh
tool.
$ cd $ curl https://get.acme.sh | sh -s email=my@example.com $ source .bashrc
Two lines have been added to ~/.bashrc
,
referencing a two-line file that
sets an environment variable and defines an alias:
$ cat ~/.bashrc # .bashrc # Source global definitions if [ -f /etc/bashrc ]; then . /etc/bashrc fi # User specific aliases and functions . "/home/myusername/.acme.sh/acme.sh.env" $ cat ~/.acme.sh/acme.sh.env export LE_WORKING_DIR="/home/myusername/.acme.sh" alias acme.sh="/home/myusername/.acme.sh/acme.sh
Step 3: Get a GoDaddy API Key and Secret String
On your local desktop, start a browser and load https://developer.godaddy.com/keys/.
For some unexplained reason you must first generate a test key/secret pair. It's a single button click. Then you can generate a production key and secret.
I used vim
within my SSH session to edit
~/.acme.sh/keys
and store both pairs.
$ cat ~/.acme.sh/keys Test key: Key: qsLLqQyqhu6Lx1q85ctZbNYT90daRvR25 Secret: Bdx4ap8gi0kK8jOosHD1zf Production key: Key: AHoQgGr+qi1qModOIKZTktXzGak6C1MV0K Secret: mkNmVBf/6ZaWkMx0Bi5sX2
Of course those aren't the actual values!
They're the result of using dd
to send
several bytes from /dev/random
into
the base64
command to provide
appropriately sized examples.
Step 4: Issue a Certificate
Set two environment variables and then you're
ready to issue a certificate.
The --help
option explains the pieces
and suggests other options.
The certificate will be good for the domain itself
plus www.example.com.
$ export GD_Key=AHoQgGr+qi1qModOIKZTktXzGak6C1MV0K $ export GD_Secret=mkNmVBf/6ZaWkMx0Bi5sX2 $ acme.sh --help [... 94 lines of output ...] $ acme.sh --issue \ --domain example.com \ --domain www.example.com \ --webroot ~/public_html --dns dns_gd $ ls ~/.acme.sh/example.com ./ fullchain.cer example.com.csr ../ example.com.cer example.com.csr.conf ca.cer example.com.conf example.com.key
There will be a lot of narrative output, including
multiple countdowns for DNS updates to take effect.
A subdirectory of ~/.acme.sh
named for the
domain will appear, and the key and certificate along with
the intermediate CA certificate, full chain certificates,
certificate signing request, and CSR configuration
will be stored there.
Step 5: Upload the Certificate and Private Key to GoDaddy
We can call the cPanel API from our command line.
First, edit cpanel_uapi.sh
to uncomment the DEPLOY_CPANEL_USER
line and
set the variable to your numeric GoDaddycustomer ID.
$ head ~/.acme.sh/deploy/cpanel_uapi.sh
#!/bin/bash
# Here is the script to deploy the cert to your cpanel using the cpanel API.
# Uses command line uapi. --user option is needed only if run as root.
# Returns 0 when success.
# Written by Santeri Kannisto <santeri.kannisto@2globalnomads.info>
# Public domain, 2017
#export DEPLOY_CPANEL_USER=myusername
export DEPLOY_CPANEL_USER=12345678
Now you can deploy the certificate and key:
$ acme.sh --deploy --domain example.com --deploy-hook cpanel_uapi
You should see a message that the certificate was deployed.
Your site should now be available via HTTPS. Test it!
Step 6: Verify Continuing Renewal
The deployment has set up a nightly cron job, scheduled for a random minute between 0000 and 0100. Mine got 0032:
$ crontab -l 32 0 * * * "/home/myusername/.acme.sh"/acme.sh --cron --home "/home/myusername/.acme.sh" > /dev/null
You can run the cron job manually:
$ acme.sh --cron --home ~/.acme.sh ===Starting cron=== Renew: 'example.com' Skip, Next renewal time is: Sat Nov 25 16:34:49 UTC 2017 Add '--force' to force to renew. Skipped example.com ===End cron===
You will see that your certificate is good for 90 days,
and it will be renewed when 30 days remain.
As the command suggests, you can add --force
to make it happen.
You will see that it generates and installs the private key
and certificate and then deploys them.
Step 7: Set Up Redirection
I want to redirect all requests to the "non-www" hostname,
and redirect all HTTP requests to HTTPS.
That is, all of these URLs:
https://www.example.com/some/path
http://www.example.com/some/path
http://example.com/some/path
should be redirected to:
https://example.com/some/path
Add the following block to the end of
~/public_html/.htaccess
.
Don't repeat "RewriteEngine on" if it's already in the file:
$ tail ~/public_html/.htaccess # Remove "www." and redirect to https RewriteEngine on RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC] RewriteRule ^(.*)$ https://%1/$1 [R=301,L] RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Also change "http" to "https" throughout the rest of your
.htaccess
and sitemap.txt
files.
Test everything and make sure that it's working.
Congratulations, you're done! For now...
Step 8: Stay Up To Date
Some time in April 2018, GoDaddy changed their API.
All at once, acme.sh
failed and the
certificate started to count down to expiration.
Update your acme.sh
code.
$ ~/.acme.sh/acme.sh --upgrade
Upgrading to acme.sh
v2.7.9 solved the
April 2018 problem.
acme.sh
RFC 8555
In June 2021, Let's Encrypt phased out
support for ACMEv1,
having replaced it with the ACMEv2 API as described in
RFC 8555.
You need to update your
acme.sh
client.
Save your old ~/.acme.sh/keys
file and
copy it into the new version:
-
$ mv .acme.sh OLD.acme.sh
- Do "Step 2" above.
-
$ cp OLD.acme.sh/keys .acme.sh
- Now repeat Steps 3-6 above.
$ ~/.acme.sh/acme.sh --version v2.7.9
Further Exploration
You can examine certificate details with the
openssl
command:
$ openssl x509 -in ~/.acme.sh/example.com/example.com.cer -text -noout | less Certificate: Data: Version: 3 (0x2) Serial Number: 03:f5:93:67:0e:ca:45:ad:b4:15:4f:3c:94:05:60:31:11:f1 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3 Validity Not Before: Sep 26 15:35:00 2017 GMT Not After : Dec 25 15:35:00 2017 GMT Subject: CN=example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:e9:d1:fd:e4:23:3b:f3:45:41:cc:bd:35:47:1e: 2b:de:3d:da:b9:da:32:75:37:a3:b4:d3:a0:30:0f: ca:cf:c4:3c:36:c3:86:be:56:b6:d2:4a:c7:34:cf: 69:05:ac:c7:31:2d:4c:d1:fb:05:35:73:a0:80:9d: 08:79:6d:59:3b:84:25:68:b6:ae:27:30:d7:21:1d: 60:3b:30:68:3e:56:10:1e:4e:1a:12:11:a2:20:65: 3a:12:b3:ce:e1:88:52:28:11:8f:36:6c:e9:4e:77: 67:a0:cc:f3:7d:0e:1d:30:51:74:b4:2d:f7:de:23: 7e:73:9d:91:36:49:6e:5f:8d:96:c6:77:6e:df:5e: b4:19:ef:8e:16:d0:30:c6:66:03:24:bb:b9:89:5f: c1:13:95:e1:8b:73:e6:b1:b2:77:0b:8c:e9:15:2d: ea:43:26:bb:f7:81:0f:a9:48:03:c4:bd:be:f7:cc: 89:44:94:28:de:f5:52:2c:03:3e:23:1e:fd:c4:25: e7:00:8e:39:2d:5a:2a:d8:7a:6a:39:b9:dd:e5:c6: 35:f6:2a:bf:3f:49:43:47:9a:18:d9:14:68:33:98: b5:4f:8e:50:89:b7:fd:14:14:5d:a1:cc:66:09:62: 04:6e:57:86:2e:a8:81:64:61:34:65:ad:58:98:2b: a8:87 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: 57:87:BF:61:35:65:92:0D:81:51:2D:CF:33:73:49:12:94:F4:20:72 X509v3 Authority Key Identifier: keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 Authority Information Access: OCSP - URI:http://ocsp.int-x3.letsencrypt.org CA Issuers - URI:http://cert.int-x3.letsencrypt.org/ X509v3 Subject Alternative Name: DNS:example.com, DNS:www.example.com X509v3 Certificate Policies: Policy: 2.23.140.1.2.1 Policy: 1.3.6.1.4.1.44947.1.1.1 CPS: http://cps.letsencrypt.org User Notice: Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/ Signature Algorithm: sha256WithRSAEncryption 30:5b:4d:fb:db:de:d8:92:5e:53:14:ec:9b:f9:e6:b9:6f:e5: 4d:02:70:62:88:c7:d5:fa:ae:1e:8d:eb:d0:ed:a5:d7:ae:85: d9:af:02:6a:80:34:ae:f1:12:a1:3d:c7:0e:42:29:3e:13:cc: 38:d5:39:87:1b:a2:a6:d4:18:41:d7:c8:bc:ff:76:10:00:6c: f6:9e:19:be:1a:f5:de:37:82:85:d2:c2:7f:7c:bf:df:1d:17: 37:f9:0d:20:ba:63:cb:d0:4f:08:76:b0:ec:2c:5c:1f:c3:5f: af:cf:3b:e7:a0:ce:4c:b3:de:89:ec:e0:83:7c:bb:e1:b6:48: 3d:a7:8f:24:93:1e:27:72:08:fa:b9:66:70:f2:1f:64:7a:14: 39:ac:22:62:b6:25:0c:be:03:f8:c4:6d:b8:ff:c2:6d:de:04: 81:02:b4:a9:97:3f:c9:bc:95:f2:b6:a0:2e:d2:fc:96:e4:4a: 1e:41:3e:64:11:03:a3:c2:83:6f:34:a1:9e:0a:20:6c:06:13: 6c:df:2c:8d:e0:14:20:3f:25:f4:22:18:c6:45:72:6f:88:b7: 85:eb:76:66:29:be:7e:68:d3:66:b5:da:c6:8d:88:1f:c9:4c: db:42:6e:e5:64:d9:c4:72:c5:97:48:a7:3e:52:33:e5:ad:c6: 7e:9f:a2:0b
You might also try this:
$ openssl s_client -connect example.com:443 -servername example.com