Linux servers.

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. And, within a few months, I moved those sites to the Google Cloud.

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 the /etc/ssh/ssh_config file on your clients from which you want to adminster the site.

$ 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 was version 5.3, two major releases behind the current, but it was 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.

Let's Encrypt, ACMEv2, and 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:

  1. $ mv .acme.sh OLD.acme.sh
  2. Do "Step 2" above.
  3. $ cp OLD.acme.sh/keys .acme.sh
  4. 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. For a local file, while you're logged in on the server:

$ openssl x509 -in ~/.acme.sh/example.com/example.com.cer -text -noout | less

Or, from a remote client, use openssl s_client to retrieve the certificate and openssl x509 to interpret and display it. Do this, replacing cromwell-intl.com with your domain name:

$ openssl s_client -showcerts -connect cromwell-intl.com:443 < /dev/null | openssl x509 -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            04:e9:53:64:88:eb:fd:84:9b:63:47:95:a6:3f:39:87:9b:c0
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = R3
        Validity
            Not Before: Apr  7 07:40:13 2024 GMT
            Not After : Jul  6 07:40:12 2024 GMT
        Subject: CN = cromwell-intl.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (384 bit)
                pub:
                    04:d2:ef:ad:da:c1:7e:4d:4b:72:49:cd:e2:d5:94:
                    94:79:4a:81:09:63:cd:7d:44:07:4e:c3:54:48:fc:
                    4b:f8:5c:d3:46:3e:54:a1:9a:e3:03:68:24:39:61:
                    6c:ac:a8:b6:d1:64:fd:2a:b9:af:79:b8:d5:ef:c0:
                    37:b4:82:28:9a:26:2b:0f:24:85:1f:77:28:9f:e1:
                    f3:ca:77:42:20:49:f7:8c:01:f1:aa:05:66:e4:99:
                    46:09:0a:ca:c4:7e:eb
                ASN1 OID: secp384r1
                NIST CURVE: P-384
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                1F:F4:A0:89:41:AA:65:09:49:F9:BF:AF:72:BF:7A:41:01:72:F9:7A
            X509v3 Authority Key Identifier: 
                14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6
            Authority Information Access: 
                OCSP - URI:http://r3.o.lencr.org
                CA Issuers - URI:http://r3.i.lencr.org/
            X509v3 Subject Alternative Name: 
                DNS:alt.cromwell-intl.com, DNS:cromwell-intl.com, DNS:www.cromwell-intl.com
            X509v3 Certificate Policies: 
                Policy: 2.23.140.1.2.1
            CT Precertificate SCTs: 
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : 48:B0:E3:6B:DA:A6:47:34:0F:E5:6A:02:FA:9D:30:EB:
                                1C:52:01:CB:56:DD:2C:81:D9:BB:BF:AB:39:D8:84:73
                    Timestamp : Apr  7 08:40:14.161 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:46:02:21:00:AA:CD:F2:83:5D:8F:66:04:1E:0E:1D:
                                E1:27:33:84:0B:45:C1:4D:CA:B4:B7:30:D4:B5:A8:DC:
                                81:1A:3E:F8:48:02:21:00:EB:50:6F:8E:3E:64:16:81:
                                0C:59:BB:EE:C6:62:D5:0F:A3:DD:61:22:27:10:52:C2:
                                CC:AB:88:F5:21:0A:FB:AA
                Signed Certificate Timestamp:
                    Version   : v1 (0x0)
                    Log ID    : EE:CD:D0:64:D5:DB:1A:CE:C5:5C:B7:9D:B4:CD:13:A2:
                                32:87:46:7C:BC:EC:DE:C3:51:48:59:46:71:1F:B5:9B
                    Timestamp : Apr  7 08:40:14.163 2024 GMT
                    Extensions: none
                    Signature : ecdsa-with-SHA256
                                30:45:02:21:00:DF:04:CE:73:08:DF:76:4E:36:14:F2:
                                E2:B9:BF:89:49:4D:73:3D:92:B6:C1:B2:0B:89:0A:1E:
                                55:EF:5C:8F:A9:02:20:52:08:35:C0:95:5D:45:63:A8:
                                C4:30:85:4C:E6:94:14:53:07:ED:3E:0F:F6:2F:7D:78:
                                65:33:4F:73:BC:E6:22
            TLS Feature: 
                status_request
    Signature Algorithm: sha256WithRSAEncryption
    Signature Value:
        42:4b:f7:02:f2:01:53:4f:bc:e4:53:0b:3d:ef:ee:f9:5a:5a:
        63:a2:69:dc:4f:eb:e4:38:d7:5f:06:d9:73:1d:8f:d8:2b:ae:
        17:29:d5:36:6c:94:7f:13:12:94:26:09:d1:38:f2:ed:2e:b4:
        2f:7f:85:fe:5e:37:01:d1:5f:00:ab:98:bf:37:8f:a7:1b:6e:
        89:a1:fa:85:d8:0b:99:c6:f3:99:af:d0:9f:ba:ff:7e:ea:7d:
        2e:c9:63:ae:aa:6e:22:87:2b:5a:4c:6b:32:d4:e5:bd:ff:14:
        b4:81:0e:c3:a3:6a:5d:23:dc:2b:17:d6:35:51:06:a2:1a:c3:
        a8:d8:4f:88:2d:88:b4:c6:2b:0d:d4:5f:3d:92:3c:00:ed:8d:
        ea:c5:b9:bc:12:c0:11:8d:81:03:79:2f:c7:7b:ea:1d:e3:bf:
        eb:2c:d4:67:11:6b:a2:31:38:18:35:be:75:0d:5c:05:59:95:
        97:49:1f:b7:eb:5c:cb:cc:63:c1:29:4f:ba:37:fa:13:c7:ce:
        98:b5:4d:0d:b4:d5:8f:71:40:23:9c:14:19:f8:a6:c1:d0:e6:
        2f:9a:02:12:5c:93:7c:7c:4f:d3:15:22:f8:95:01:55:bd:b5:
        c0:e6:82:03:ed:ba:96:08:68:eb:90:b2:dd:f1:ac:90:fa:67:
        bc:81:20:80
-----BEGIN CERTIFICATE-----
MIIEgzCCA2ugAwIBAgISBOlTZIjr/YSbY0eVpj85h5vAMA0GCSqGSIb3DQEBCwUA
MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD
EwJSMzAeFw0yNDA0MDcwNzQwMTNaFw0yNDA3MDYwNzQwMTJaMBwxGjAYBgNVBAMT
EWNyb213ZWxsLWludGwuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE0u+t2sF+
TUtySc3i1ZSUeUqBCWPNfUQHTsNUSPxL+FzTRj5UoZrjA2gkOWFsrKi20WT9Krmv
ebjV78A3tIIomiYrDySFH3con+HzyndCIEn3jAHxqgVm5JlGCQrKxH7ro4ICVTCC
AlEwDgYDVR0PAQH/BAQDAgeAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcD
AjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQf9KCJQaplCUn5v69yv3pBAXL5ejAf
BgNVHSMEGDAWgBQULrMXt1hWy65QCUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcw
IQYIKwYBBQUHMAGGFWh0dHA6Ly9yMy5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYW
aHR0cDovL3IzLmkubGVuY3Iub3JnLzBKBgNVHREEQzBBghVhbHQuY3JvbXdlbGwt
aW50bC5jb22CEWNyb213ZWxsLWludGwuY29tghV3d3cuY3JvbXdlbGwtaW50bC5j
b20wEwYDVR0gBAwwCjAIBgZngQwBAgEwggEFBgorBgEEAdZ5AgQCBIH2BIHzAPEA
dwBIsONr2qZHNA/lagL6nTDrHFIBy1bdLIHZu7+rOdiEcwAAAY63txpRAAAEAwBI
MEYCIQCqzfKDXY9mBB4OHeEnM4QLRcFNyrS3MNS1qNyBGj74SAIhAOtQb44+ZBaB
DFm77sZi1Q+j3WEiJxBSwsyriPUhCvuqAHYA7s3QZNXbGs7FXLedtM0TojKHRny8
7N7DUUhZRnEftZsAAAGOt7caUwAABAMARzBFAiEA3wTOcwjfdk42FPLiub+JSU1z
PZK2wbILiQoeVe9cj6kCIFIINcCVXUVjqMQwhUzmlBRTB+0+D/YvfXhlM09zvOYi
MBEGCCsGAQUFBwEYBAUwAwIBBTANBgkqhkiG9w0BAQsFAAOCAQEAQkv3AvIBU0+8
5FMLPe/u+VpaY6Jp3E/r5DjXXwbZcx2P2CuuFynVNmyUfxMSlCYJ0Tjy7S60L3+F
/l43AdFfAKuYvzePpxtuiaH6hdgLmcbzma/Qn7r/fup9LsljrqpuIocrWkxrMtTl
vf8UtIEOw6NqXSPcKxfWNVEGohrDqNhPiC2ItMYrDdRfPZI8AO2N6sW5vBLAEY2B
A3kvx3vqHeO/6yzUZxFrojE4GDW+dQ1cBVmVl0kft+tcy8xjwSlPujf6E8fOmLVN
DbTVj3FAI5wUGfimwdDmL5oCElyTfHxP0xUi+JUBVb21wOaCA+26lgho65Cy3fGs
kPpnvIEggA==
-----END CERTIFICATE-----