Let’s Encrypt when your server is behind a firewall and you can’t use DNS Challenge

Sometimes you want to give a server that is  behind a firewall a valid SSL certificate. Let’s Encrypt provides a nice solution for this called DNS Challenge. The problem with DNS Challenge is it may require some manual configuring to on your server to set it up. That is all fine and good but sometimes the server won’t allow you to do that configure. A classic example is the hass.io (Home Assistant) appliance or FreePBX

I couldn’t find a coherent set of instructions for setting up Let’s Encrypt DNS Challenges with hassio, especially as my DNS provider requires custom scripts in order to achieve this.

However, I came up with a work around. First let’s think about how Let’s Encrypt (LE) usually works. Suppose you want to get a certificate for my.example.com (MEC), but my.example.com is NATed behind example.com’s (EC) IP address. When you run the LE client on MEC, it requests the LE servers to do a challenge request to MEC. MEC will have the same public IP as EC. the client on MEC will some information on the MEC server at the <webroot>/.well-known/acme-challenge. The LE servers will try and query it. If they find what they are looking for, they issue you the certificate.

So I got to thinking, as this request takes place on port 80, it must contain the domain name header, and so, if you run an web server on port 80 of EC and A entry for  MEC the same public IP address as EC, EC could simply proxy that request to the MEC server.

I looked up how to do this and set it up on my server and low and behold, I was able to get a valid certificate.

In my case I did it with nginx , so the config looked like this:

On my server on port 80 (example.com) of the firewall in /etc/nginx/sites-available/my.example.com:

server {
       server_name my.example.com;
       location / {
       		proxy_pass http://192.168.1.9:80/;
		proxy_set_header Host $http_host;
	}
}

Then symlink it to /etc/nginx/sites-enabled/my.example.com and test the nginx config:

$ sudo ln -s /etc/nginx/sites-available/my.example.com  /etc/nginx/sites-enabled/my.example.com
$ sudo nginx -t
[sudo] password for jason: 
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Restart nginx:

sudo systemctl restart nginx.service

Now start your Let’s Encrypt certificate request on your server and you should be good to go.

MSSQL ODBC Client on Debian 9 Stretch

Many years ago fREW Schmidt wrote a very handy article on installing MSSQL ODBC drivers in debian. Since then Microsoft have improved their support of debian somewhat and things have changed making it easier to install, however they still (as at 2017-11-06) have not released proper drivers for Debian Stretch.

Add the microsoft repo

echo  'deb [arch=amd64] https://packages.microsoft.com/debian/8/prod jessie main' > /etc/apt/sources.list.d/mssql-release.list

Add the key

curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -

then update and install

sudo apt update
sudo apt install msodbcsql mssql-tools

If you try and connect to sql now you get an obscure error like this:

/opt/mssql-tools/bin/sqlcmd -S 10.0.2.13 -U xxx -P yyy
Sqlcmd: Error: Microsoft ODBC Driver 13 for SQL Server : Can't open lib '/opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.1.so.9.1' : file not found.

But the file exists:

$ file /opt/mssql-tools/bin/sqlcmd
/opt/mssql-tools/bin/sqlcmd: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=21a353af470e7849544daab892ec9b1bfc36dc87, not stripped

This somewhat misleading error is actually due to the libmsodbcsql lib being linked against a very specific version of  libssl. Check the output of ldd:

$ ldd /opt/microsoft/msodbcsql/lib64/libmsodbcsql-13.1.so.9.1 | grep 'not found'
libcrypto.so.1.0.0 => not found
libssl.so.1.0.0 => not found

But it turns out this can easily be solved by manually installing the libssl package from Debian Jessie:

wget "http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u7_amd64.deb"
sudo apt install ./libssl1.0.0_1.0.1t-1+deb8u7_amd64.deb

then you should be back in business.

I made a ukulele tuner web app

close up of a ukulele
Ukulele by texasfeel  (CC BY-ND 2.0)

I got sick of all the ukulele tuner apps so I decided to make my own. The best ukulele tuner app I’ve found on the iPhone works by repeatedly playing the note you are tuning to rather than trying to analyse the audio and display the frequency of the note it detects. The human ear is still better than software at doing that in my opinion.

It’s pure html and javascript. I used the howlerjs javascript framework for playing the audio as I found the basic javascript audio functions not precise enough for the looping.

The audio samples came from freesound.org courtesy stomachache.

Ukulele Tuner

convert mp3 to m4b on the linux command line

I quite often want to convert mp3 files to m4b, Apple’s proprietary nastiness. There is pacpl of course but that has issues these days, particularly with copying the id3 tags to the new file.

FFmpeg can do it when you build from source (I just found a nice script that will build it from source for you). So I came up with this way to do it from the command line. It uses an awesome feature of xargs which will run it in parallel, so one for each core.

find -type f -name \*.mp3 \
| xargs -n 1 -P $(getconf _NPROCESSORS_ONLN || echo 1) -i \
  bash -c \
 'i="{}"; ffmpeg -y -i "$i" -map_metadata 0 \
        -c:a libfdk_aac -b:a 128k\
        -map_metadata:s:a 0:s:a -f ipod "${i%.*}.m4b"'

What that does is:

  1. find all the mp3 files and pipe their names into xargs
  2. then get the number of cores available and pass that to the -P argument
  3. and run ffmpeg and map all the metadata to the new file
  4. and strip off the mp3 file extension and add m4b to it

There! Nothing to it!

Update 2017-03-23: Added double quotes around the bash variable $i to cope with spaces. You should always do this (and so should I)

Wan’t to help organise a Perl conference in Sydney in 2017? Apply within!

Some of us at the Sydney PM group and the #australia  Perl IRC channel have
been discussing holding an perl conference in Sydney in 2017.

If this is something you are interested in attending or organising,
please get in touch.

You can find us on #australia on irc.perl.org or Join our conference
organising mailing list:

https://groups.google.com/forum/#!forum/sydney-perl-conf-2017

First thing we need to do is form an organising committee. Let me know
if you are interested to be a part of that?

Looking forward to hearing from you.

Automatically lookup amazon books on booko

amazon logo loves booko logo

I love books and I love booko. So I decided to help Amazon help me check prices on booko with a little Tampermonkey script. It makes the ISBN-13 on Amazon a clickable link that will open a new tab to booko and search for that ISBN. Give it a go!

// ==UserScript==
// @name         booko amazon ISBN link
// @namespace    https://emacstragic.net/
// @version      0.1
// @description  Make the ISBN-13 a clickable link to booko.
// @author       Jason Lewis jason@NOdicksonSPAM.st
// @match        https://www.amazon.com/*
// @copyright 2016+, emacstragic.net
// @grant        none
// ==/UserScript==
 
(function() {
    'use strict';
 
    //var hrefs = new Array();
    var elements = $('#productDetailsTable &gt; tbody &gt; tr &gt; td &gt; div &gt; ul &gt; li');
    elements.each(function() {
        console.log($(this));
        //console.log($(this)[0].innerText);
        if ($(this)[0].innerText.indexOf('ISBN-13') !== -1 ) {
            var isbn=$(this)[0].innerText.replace(/ISBN-13:\s*(\d{3}-\d{10})/,"$1");
            console.log('isbn is: ' + isbn);
            var url = 'https://booko.com.au/' + isbn;
            var a = document.createElement("a");
            var text = document.createTextNode('ISBN-13: ' + isbn);
            a.setAttribute('href',url);
            a.setAttribute('target','_blank');
            a.appendChild(text);
            var li = document.createElement("li");
            li.appendChild(a);
 
            $(this)[0].replaceWith(li);
        }
 
 
    });
})();