Sendmail – LetsEncrypt and verify=OK

How To Configure LetsEncrypt and Sendmail Properly

This documentation pulls from a form post on FreeBSD from user Kuli.
https://forums.freebsd.org/threads/sendmail-and-letsencrypt.57675/

LetsEncrypt certificates aren’t listed in the main distributed ca-bundle.crt. The confCACERTand confCACERT_PATH configurations are two independent configurations that don’t really have anything to do with each other. When verifying certificates, it looks in the confCACERT_PATH for individual hashed files of root certificates. The confCACERTwill be configured with the intermediary LetsEncrypt chain.pem. Sendmail will then be happy to verify=OK the certificates. Do note that, it appears the majority of mail servers are using certificates that can’t be verified. So you will see a lot of NO. It’s better than FAIL. The script works with Fedora and probably any variant of Linux. Your experience may vary.

Sendmail Configuration

define(CERT_DIR',/etc/letsencrypt/live/<your site>')
define(confCACERT_PATH',/usr/local/etc/ssl/ROOT')
define(confCACERT',CERT_DIR/chain.pem')
define(confSERVER_CERT',CERT_DIR/cert.pem')
define(confSERVER_KEY',CERT_DIR/privkey.pem')
define(confCLIENT_CERT',CERT_DIR/cert.pem')
define(confCLIENT_KEY',CERT_DIR/privkey.pem')

Create the CACERT_PATH files

#!/bin/sh
#Separate the root cert into files each with only one cert and name hashed

RCert=/etc/ssl/certs/ca-bundle.crt
DESTDIR=/usr/local/etc/ssl/ROOT
mkdir -p $DESTDIR
cd $DESTDIR
rm -f *
cat $RCert | sed -E '/^(Certificate:|SHA1 Fingerprint|#| |$)/d' | awk 'BEGIN {c=0;} /BEGIN TRUS/{c++} { print > "cert." c ".pem"}'

for a in ls $DESTDIR
do
    mv $a openssl x509 -hash -noout < $a.0
done

Icecast Now Playing WordPress Widget Script

Before moving to WordPress from BlogEngineDotNet I had a widget that made a call to a specially created Icecast XSL file to display the current playing track title in a Widget.  This worked great, but it was static and wouldn’t update if the track changed.  Migrating to WordPress, I wanted to achieve the same track playing information but up the game with it updating to display new track information.  The latest Icecast server has built in metadata report in JSON format now so I wanted to use this over parsing an XSL file. This took me roughly about a day and a half to complete, mostly due to nearly all examples of parsing JSON with Javascript do not work and I know little Javascript. Here is an overview of the process invovled.

  • Configure Icecast headers and SSL certificates
  • Insert Javascript in headers.php
  • Insert HTML in Widget for display
Configure Icecast

You can skip this part if you are not using SSL. Also note Apache and Icecast are running on the same server. Since my site defaults to SSL (https) I have to configure Icecast for SSL.

The first step is to create the proper SSL certificate file format that Icecast uses. It requires a Public/Private keypair file. I used my Let’s Encrypt certificates and concatenated them together into one file.

cd /usr/share/icecast
cat /etc/letsencrypt/live/autonarcosis.com/cert.pem > icecast.pem
cat /etc/letsencrypt/live/autonarcosis.com/privkey.pem >> icecast.pem
chown icecast.icecast icecast.pem
chmod go-r icecast.pem

Now to edit the /etc/icecast.xml configuration file to enable an SSL port and point to the icecast.pem file. Create a second listen-socket container with a different port and enable SSL. Insert http-headers container before paths container for access control (this gives permission for the javascript to access the JSON data).  Put the ssl-certificate path setting within the existing paths container.

<listen-socket>
     <port>8002</port>
     <ssl>1</ssl>
</listen-socket>

<http-headers>
    <header name="Access-Control-Allow-Origin" value="*" />
</http-headers>
<paths>
     <ssl-certificate>/usr/share/icecast/icecast.pem</ssl-certificate>
</paths

Save the icecast.xml file. Enable the new port in the firewall.

firewall-cmd --permanent --add-port=8002/tcp
firewall-cmd --reload

Restart Icecast, which will now be listening on an SSL port.

systemctl restart icecast.service

You can view Icecast error log file /var/log/icecast/error.log to see if the SSL certificate loaded properly.  You should see something similar to this.

[2016-03-11  15:41:45] INFO connection/get_ssl_certificate SSL certificate found at /usr/share/icecast/icecast.pem
[2016-03-11  15:41:45] INFO connection/get_ssl_certificate SSL using ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA2
56:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-R
SA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SH
A384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA25
6:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-S
HA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-S
HA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
Configure WordPress

Now it is time to configure WordPress.  Below is the Javascript that I hacked together to pull the Icecast metadata from the data feed in JSON format. This is WordPress modified Javascript and will not work outside WordPress.  Place this Javascript in the HEAD section of your themes headers.php.

<script type="text/javascript">
function radioTitle() {
 
jQuery.ajax({
        url: "https://www.autonarcosis.com:8002/status-json.xsl",
        //force to handle it as text
        dataType: "text",
        async: true,
        success: function(data) {
                        
        //data downloaded so we call parseJSON function 
        //and pass downloaded data
        var json = jQuery.parseJSON(data);
        //now json variable contains data in json format
        //let's display a few items
        // this is the element we're updating that will hold the track title
            jQuery('#track-title').text(json.icestats.source.title);
        // this is the element we're updating that will hold the listeners count
            jQuery('#listeners').text(json.icestats.source.listeners);            
       }
     });
   };
   
jQuery(document).ready(function(){

        setTimeout(function(){radioTitle();}, 2000);
        // we're going to update our html elements / player every 15 seconds
        setInterval(function(){radioTitle();}, 15000); 

});

</script>

Add the HTML to the widget so the information will be displayed.

<p>
</br>
Listeners: <span id="listeners">00</span></br>
Current track: <span id="track-title">LIVE</span>
</p>

That’s all there is to it. There could possibly be a better way or function to do this.

I used this blog entry as a base for figuring this out; https://linge-ma.ws/update-listeners-track-on-a-website-using-icecast-jsonp-and-jquery/