Create a symbolic link in Linux using the ‘ln -s’ command

Writing this mostly because it’s just about impossible to find a simple answer to the question of “how to create a symbolic link” in Google without wading through page after page of ads. Yes, I know I’m being a hypocrite as I’ve got ads as well – but the placement and number are hopefully less offensive!

Command Syntax

If you need one file to be available in multiple locations in Linux, you can create a symbolic link to make this happen. The command syntax is:

ln -s {actual file location} {symbolic link location}

Example

After managing to hose my Node.js install for Homebridge again, I decided the quickest fix would be to just install Node.js directly from the system package manager. Once installed, I figured I could convince Homebridge to use the system version of Node.js, rather than having its own.

First, I deleted the broken built-in Node executable: sudo rm /opt/homebridge/bin/node

Then I installed the system version of Node.js, which places the Node executable at: /usr/bin/node

Because Homebridge expects the Node executable to be available at the original location, I needed to create a link from the system version to the location it wanted:

sudo ln -s /usr/bin/node /opt/homebridge/bin/node

(To get it working I had to change ownership of the symbolic link to the Homebridge user, but that’s a topic for another post!)

Move WooCommerce Coupon Code on the Checkout Page

For WooCommerce stores that offer coupons to their customers, there could be a problem with the location of the coupon code box on the checkout page. It’s hiding down the bottom of the page, and helpfully – it’s not visible by default. This can lead to lost orders, or customers emailing in for help – not an ideal situation!

When researching this problem, I couldn’t find any good (free) WordPress or WooCommerce plugins that provided a fix. There are a few code fixes that are out there, but they’re mostly quite complicated and prone to doing weird stuff in the UI. 🙁

The solution is very simple – it requires a bit of additional code in the current themes’ functions.php file. What the code below does is use a bit of simple JavaScript that moves the original coupon code section to a better location, then expands the box so it’s always displayed:

/**
 * Move coupon block to before payment block on checkout page
 * Set coupon code box to display by default (needs a short delay)
 */
add_action( 'woocommerce_before_checkout_form', 'move_checkout_coupon_form', 5 );
function move_checkout_coupon_form() {
  if ( is_checkout() && ! is_wc_endpoint_url() ) :
  ?>
  <script type="text/javascript">
    document.addEventListener("DOMContentLoaded", function(event) {
      let coupon = document.querySelector('section.coupon-wrapper');
      let payment = document.querySelector('div#payment');
      let parent = document.querySelector('div#order_review');
      parent.insertBefore(coupon, payment);
      setTimeout(function () {
        document.querySelector('form.checkout_coupon.woocommerce-form-coupon').style.display = 'block';
        document.querySelector('form.checkout_coupon.woocommerce-form-coupon').style.width = '100%';
      }, 10);
    });		
  </script>
  <?php
  endif;
}

For custom checkout pages, some of the JavaScript selectors may need a couple adjustments. Then again, if it’s custom – the coupon box is probably already in a better spot!

Padlock with Chain Header

Let’s Encrypt wildcard certificates with Hurricane Electric DNS

Renewing Let’s Encrypt wildcard certificates is generally a massive pain. You need to be able to automatically update DNS records for the domain – which is fine if you use a DNS provider that has an official Let’s Encrypt DNS plugin, but less so if you use a DNS provider that doesn’t – such as Hurricane Electric.

Side note – I’m not particularly interested in arguments for and against wildcard certs. If you’re reading this, you’ve obviously come to the conclusion that they’re probably fine and you just want them automated like the rest of your Let’s Encrypt certs!

This post assumes the following:

  • You are obtaining wildcard certificates from Let’s Encrypt
  • Your DNS is hosted with Hurricane Electric
  • Your ACME client for Let’s Encrypt is Certbot
  • You have shell access to your server

Obtain a new Let’s Encrypt wildcard certificate

Note: if you’ve already got a wildcard certificate, you can mostly skip this bit – but skim this section to make sure you’ve done everything you need to do!

1. Request wildcard certificate

Here’s the command-line incantation to request a new wildcard certificate:

sudo certbot certonly --cert-name example.com-wildcard -d '*.example.com' --manual --preferred-challenges dns 

Couple things to note here:

  • I’m not including the base domain in the certificate here – I wouldn’t be able to automate it if I did, as I’d end up needing two TXT records for the same hostname. My solution was to separate the base domain out into its own certificate, which works perfectly for me.
  • I’m specifying a certificate name. This is important, as otherwise it ends up trying to name it the same as the base domain – which is no good if you have a cert for the base domain as well.

Run the Certbot wizard – it will soon ask you to create a TXT record!

2. Create TXT record

The Certbot wizard will ask you to create a record, something like the following:

Please deploy a DNS TXT record under the name
_acme-challenge.example.com with the following value:

qwertyuiop-1234567890

Log into the Hurricane Electric DNS console, select your domain and create a new TXT record with the following settings:

Name_acme-challenge.example.com
Text dataqwertyuiop-1234567890
TTL (Time to live)5 minutes (300)

Don’t check the Enable entry for dynamic dns box yet! Save the record and complete the Certbot wizard. Your certificate should now be issued, and you can configure your Apache / Nginx / etc server as appropriate.

Set up renewal scripts

1. Create DDNS TXT record key

As of current writing, the various Hurricane Electric DNS plugins for Certbot that I’ve seen all log into the actual account – which is horrendous from a security perspective. You don’t want a script to have full control over all of your domain records!

Thankfully, Hurricane Electric now allow TXT records to be updated with a key that only has control over just that one record.

Go back to the _acme-challenge TXT record for your domain, check the box to enable the entry for dynamic dns and Update. This enables the DDNS feature – you should now see an “arrow circle” symbol for that record:

Hurricane Electric DDNS record

Click the arrow circle symbol to generate a new DDNS key (save this key somewhere – this is the last time you’ll see it in the Hurricane Electric interface!)

For the purposes of this example, I’ll assume that the generated key looks something like ‘qwertyuiop123456’.

2. Add manual authentication script

Copy and paste the following into /etc/letsencrypt/he-dns-update.sh:

#!/bin/bash

# Do we have everything we need?
if [[ -z "$CERTBOT_DOMAIN" ]] || [[ -z "$CERTBOT_VALIDATION" ]]; then
    echo '$CERTBOT_DOMAIN and $CERTBOT_VALIDATION environment variables required.'
    exit 1
fi

# Add all HE TXT record DDNS keys to the txt_key object
# Remember to protect this script file - chmod 700!
declare -A txt_key
txt_key['_acme-challenge.example.com']='qwertyuiop123456'

# Create a FQDN based on $CERTBOT_DOMAIN
HE_DOMAIN="_acme-challenge.$CERTBOT_DOMAIN"

# Update HE DNS record
curl -s -X POST "https://dyn.dns.he.net/nic/update" -d "hostname=$HE_DOMAIN" -d "password=${txt_key[$HE_DOMAIN]}" -d "txt=$CERTBOT_VALIDATION"

# Sleep to make sure the change has time to propagate over to DNS
sleep 30

As the comment suggests – protect this file from prying eyes by using chmod 700. If you have multiple wildcard certificates, you can add in extra entries to the txt_key object.

3. Add post deployment script

As much as you can manually restart services after the new certificate has been issued, you can automate that as well. Copy and paste the following into /etc/letsencrypt/renewal-hooks/deploy/restart-services.sh:

#!/bin/sh

set -e

for domain in $RENEWED_DOMAINS; do
  case $domain in
    *.example.com)
      systemctl restart apache2
      ;;
    *.example.com.au)
      systemctl restart nginx
      systemctl restart postfix
      ;;
    *)
  esac
done

Update the script to restart specific services for particular domains as required. I recommend chmod’ing this file to 755 – unlike the previous script, there’s nothing sensitive here!

Request Let’s Encrypt wildcard renewal

We’re going to force a renewal of the certificate here – this will test the two scripts above, plus update the renewal config so that the certificate will automatically renew in the future. If your certificate is due for renewal already, you don’t need to include the –force-renewal flag. Run the following command:

sudo certbot renew --cert-name example.com-wildcard --manual --manual-auth-hook /etc/letsencrypt/he-dns-update.sh --preferred-challenges dns --force-renewal

Check that:

  • There are no errors in the Certbot logs,
  • The certificate renewed successfully, and
  • All services restarted appropriately

If everything worked – your Let’s Encrypt wildcard certificates should now renew automagically!