How to import and renew SSL certificates on Oracle Cloud (OCI) Load Balancers

Joaquín Marcher
Johnny Bytes | Flutter & Tech
8 min readSep 15, 2022

--

Photo by FLY:D on Unsplash

Plain HTTP is so 90s, do you remember the last time you visited a website or consumed an API that didn’t run over HTTPS? Nowadays this is a MUST and if you want to know how to use and automatically renew SSL certificates on OCI (short for Oracle Cloud Infrastructure) load balancers in a seamless and easy way, read up to the end of this article and find it out.

As of now OCI does not provide the possibility to create trusted issuer public certificates. In order to obtain a root CA with a valid issuer we need to import an already trusted certificate.

This can be done with LetsEncrypt and this article will describe how to do so.

This is what we will do:

  1. Issue a certificate with LetsEncrypt
  2. Import the certificate to OCI
  3. Create a script to automatically renew the certificate

You can execute all commands in this tutorial either on your local machine or on any compute instance within OCI.

Prerequisites:

  1. An OCI account
  2. A compartment and VPC you’re working in
  3. The rights to obtain certificates (follow steps 1–3 here)
  4. A domain for which you are able to maintain DNS records

Issue a certificate with LetsEncrypt

Install ACME Client

An ACME client is a piece of software that can communicate with an ACME (Automatic Certificate Management Environment) enabled Certificate Authority (such as Let’s Encrypt).

There are various ACME Clients out there. On this article we will use getssl which is free, open source and does not require to be installed.

We need to download getssl with the following command:

curl — silent https://raw.githubusercontent.com/srvrco/getssl/latest/getssl > getssl ; chmod 700 getssl

Configure the ACME client

After the installation of the getssl client, a configuration file for the domain you want to request a certificate is required. In our case this will be example-domain.com

Run the following command:

./getssl -c example-domain.com

The output should look something like this:

Making domain directory - /home/opc/.getssl/example-domain.com
creating domain config file in /home/opc/example-domain.com/getssl.cfg
Adding SANS=www.example-domain.com from certificate installed on example-domain.com to new configuration file
created domain config file in /home/opc/example-domain.com/getssl.cfg

This will create a .getssl directory on your user directory that will contain a folder for each domain we request certificates for.

Edit the .getssl/getssl.cfg

Enable the LetsEncrypt production endpoint for generating the certificate and comment the line that mentions the staging endpoint.

The file should look something like this:

# The staging server is best for testing (hence set as default) 
# CA="https://acme-staging-v02.api.letsencrypt.org"
# This server issues full certificates, however has rate limits CA="https://acme-v02.api.letsencrypt.org"

Configure the RENEW_ALLOW=”90" . This ensures we will create a certificate valid for 90 days. Note that LetsEncrypt in its current version only allows to issue certificates for 90 days.

Configure the validation of the domain to be done via the DNS, for this we need to download the dns_add_manual and the dns_del_manual scripts from here.

We put them in the /home/opc/ssl/scripts/dns_scripts folder.

# Use the following 3 variables if you want to validate via DNS VALIDATE_VIA_DNS="true" 
DNS_ADD_COMMAND=/home/opc/ssl/scripts/dns_scripts/dns_add_manual DNS_DEL_COMMAND=/home/opc/ssl/scripts/dns_scripts/dns_del_manual

Edit the .getssl/example-domain.com/getssl.cfg

Edit the domain configuration and make sure you see the following:

CA="https://acme-v02.api.letsencrypt.org"
PRIVATE_KEY_ALG="rsa"
SANS="*.example-domain.com"
PREFERRED_CHAIN="ISRG Root X1"
FULL_CHAIN_INCLUDE_ROOT="true"

Pay attention on the type of chain you are requesting.

The default one is “End-entity certificate ← R3 ← ISRG Root X1 ← DST Root CA X3”, but the “DST Root CA X3” expired in 2021 and is kept there only for compatibility with older devices.

You can read more about the subject here and here.

The default chain will not be accepted by the certificate service because of the expired certificate.

The example above already considers the most common chain.

Generate, verify and import the certificate into OCI (automation in the following chapter)

Generate the certificate

Before starting the generation process, make sure you have access to the DNS records of your domain (which must not necessarily be maintained in OCI).

In order to generate a wildcard certificate the DNS verification is needed.

More details about the challenges can be found here.

Start the request by issuing the certificate. To do so run the following command:

./getssl example-domain.com

The output should look something like this:

./getssl example-domain.com
example-domain.com: no certificate obtained from host
creating key - /home/opc/.getssl/example-domain.com/example-domain.com.key
Generating RSA private key, 4096 bit long modulus (2 primes)
..................................................................................++++
...................................................++++
e is 65537 (0x010001)
creating domain csr - /home/opc/.getssl/example-domain.com/example-domain.com.csr
Registering account
Verify each domain
Verifying example-domain.com
[output to be continued in the next chapter ...]

Create a TXT record on you DNS server for the _acme-challenge

Note that the output will prompt a message with the ACME Challenge TXT, copy the message and create the TXT DNS record named: _acme-challenge

This message will be prompted probably twice, once for the example-domain.com entry of the certificate and the second time for *.example-domain.com

The output should look something like this:

[ ... from last chapter]In the DNS, a new TXT record needs to be created for;
_acme-challenge.example-domain.com
containing the following value
e54h9wAfxOjd0b-xxxxxxxxxxxxxxxxxxxxxxxxxxxx
Press any key to obtain the certificate once the records have been updated...
checking DNS at ns1.p68.dns.aws.amazon.com
sleeping 60 seconds before asking the ACME server to check the dns
sending request to ACME server saying we're ready for challenge
checking if challenge is complete
Pending
checking if challenge is complete
Verified *.example-domain.com

Once verified, you may delete the TXT record, because you don’t need it any longer.

In the DNS, the following DNS record should be deleted ;
_acme-challenge.example-domain.com
Press any key to obtain the certificate once the records have been updated...
Verification completed, obtaining certificate.

At this point the certificate will be created and stored in the .getssl/example-domain.com folder. We will need these files later to upload them to OCI.

Requesting Finalize Link
Requesting Order Link
Requesting certificate
Certificate saved in /home/opc/.getssl/example-domain.com/example-domain.com.crt
/home/opc/.getssl/example-domain.com/example-domain.com.crt not returned by server
getssl: example-domain.com - rsa certificate obtained but not installed on server

Verify the certificate

Verify the certificate by running the following command

openssl x509 -in /home/opc/.getssl/example-domain.com/example-domain.com.crt -noout -text

The output should look something like this:

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
04:d9:0e:88:05:18:58:92:5b:3a:2c:66:d6:76:55:17:78:22
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, O = Let's Encrypt, CN = R3
Validity
Not Before: Sep 11 09:11:13 2022 GMT
Not After : Dec 10 09:11:12 2022 GMT
Subject: CN = example-domain.com

Note the validity dates and the issuer.

Verify the full chain. It should match the production endpoint chain from LetsEncrypt “End-entity certificate ← R3 ← ISRG Root X1”

Run the following command:

openssl storeutl -noout -text -certs /home/opc/.getssl/example-domain.com/fullchain.crt | grep Subject:

The output should look something like this:

Subject: CN=example-domain.com
Subject: C=US, O=Let's Encrypt, CN=R3
Subject: C=US, O=Internet Security Research Group, CN=ISRG Root X1

Verify an individual record checks against the certificate.

Run the following command and the result should be OK:

openssl verify -CAfile /home/opc/.getssl/example-domain.com/fullchain.crt -verify_hostname subdomain.example-domain.com /home/opc/.getssl/example-domain.com/example-domain.com.crt

Note that we are testing against subdomain.example-domain.com

Import the certificate (Only needed once)

We will import the certificate into the OCI Certificate Service. This article will not cover the creation of the CA, it will simply import an external certificate to an existing CA into OCI.

Import the certificate into the Certificate service

Navigate to “Identity and Security > Certificates”, click on the “Create Certificate” and choose “Imported”. Provide the name and description.

Select the files for the certificate, chain (full chain) and private key.

The certificate has now been imported. Note that the version of the certificate is 1.

You can use this certificate now in any endpoint for your domain, e.g. a compute instance or a load balancer.

Renew certificate automation

Once we created a certificate either on our local machine or on a compute instance within OCI the credentials will be saved on said machine (locally). This means we do not need to create an ACME challenge with DNS anymore. This also means we can now automate the creation of new certificates and renew them before they expire.

One caveat: LetsEncrypt only allow us to create a certificate every 168 Hours (1 Week). This needs to be taken into consideration when adding the automation script into the crontab:

Create the renew_certificate.sh script file

Run the following command:

cd ~/ssl && touch renew_certificate.sh && chmod +x renew_certificate.sh

Add the following lines to your script (I will explain what each line does on the script)

#!/bin/bash# This is the certificate ID which comes from OCI in the certificates console paste the ID here
CERT_OCI_ID="ocid1.certificate.oc1.eu-frankfurt-1.aaaaaaaxxxxxxffffeeeeccccceeeeeeeee"
# Issue new certificate
/home/opc/ssl/getssl -q example-domain.com
# Get the new certificate contents and assign each file to a variable
CERT_PATH=$(cat "/home/opc/.getssl/example-domain.com/example-domain.com.crt")
CERT_CHAIN=$(cat "/home/opc/.getssl/example-domain.com/fullchain.crt")
CERT_KEY=$(cat "/home/opc/.getssl/example-domain.com/example-domain.com.key")
# Update the certificate with the new credentials
oci certs-mgmt certificate update-certificate-by-importing-config-details --certificate-id=$CERT_OCI_ID --cert-chain-pem="$CERT_CHAIN" --certificate-pem="$CERT_PATH" --private-key-pem="$CERT_KEY"

Note that this article assumes you already added your credentials for OCI CLI.

Add the renew_certificate.sh script to the crontab

In order to automatically issue new certificates and assign (renew) them on the OCI console we need to add the just created script to the crontab, the line should look something like this:

0 0 * * 4 opc /home/opc/ssl/renew_certificate.sh

Note that the script is only executed once a week on thursday, this ensures we only issue a certificate once a week.

Conclusion

Now you are able to use the just created certificate either directly on any instance you may want, or you can add it to your load balancer under the listeners section.

Once the hurdles of ACME challenges are beaten, LetsEncrypt makes it very easy to issue new certificates. The script created in this article enables you to automatically issue and assign new certificates very easily.

Are you looking for a digital agency or a challenging job? Then visit us at johnnybytes.com.

--

--