Mqtt in go with paho

PUBLISHED ON FEB 18, 2017 — DEV, GOLANG

But why?

I wanted to play with MQTT and TLS and that’s about it… I decided to go with golang. Mainly because I never tried the language before and because I wanted a single binary with no dependencies and C was not even an option (I’m too old for this shit).

Why not using client cert authentication?

While client authentication using public key is nice, I didn’t feel it brings enough added security in order for me to go through the hassle of having to manage a full PKI (as well as delivering client certificates) for a pet project. Authenticating the server using public key and the client using a login and password is plenty enough for a simple “I’m alive” mqtt publisher. However, there is a hmac function in order to ensure that a rogue client cannot impersonate another one (this needs to be set before each go build, obviously…). This requires some provisionning on server side obviously so both parties use the same psk for the hmac.

Using the paho library

The paho library is pretty straightforward in go… Because I wanted a standalone binary, I decided to hardcode the Certificate Autority public key directly into the source code instead of using io.ReadFile…

/*
 * Yokai, a simple MQTT "I'm alive" publisher for Dodomeki"
 * (c) jme@opium.io for crashdump.net
 * BSD LICENCE
 */

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"fmt"
	MQTT "github.com/eclipse/paho.mqtt.golang"
	"time"
)

const mqtt_protocol string = "tls://" // mqtt protocol to use (tls:// is prefered)
const mqtt_host string = "mqtt_server_ip"  // mqtt server fqdn or ip
const mqtt_port string = "8883"  // mqtt server port
const mqtt_login string = "yokai"  // mqtt server login
const mqtt_passwd string = "password" // mqtt server password
const mqtt_topic string = "dodomeki/alive"  //topic to publish
const mqtt_id string = "yokai-arkham" //mqtt iD and topic trailing id.
const hmac_secret string = "my_secret_hmac" //hmac secret to sign msg 

const broker = mqtt_protocol + mqtt_host + ":" + mqtt_port
const mqtt_subscribe = mqtt_topic + "/" + mqtt_id

var message_string string = ""

//yes that is a self signed certificate but nothing wrong with them
//yes, I am aware that the fqdn is in the Subject…
const mqtt_tls_ca = `-----BEGIN CERTIFICATE-----
MIIDozCCAougAwIBAgIJAKdfNTvWh5tmMA0GCSqGSIb3DQEBDQUAMGgxFjAUBgNV
BAMMDUEgTVFUVCBicm9rZXIxFjAUBgNVBAoMDWNyYXNoZHVtcC5uZXQxFDASBgNV
BAsMC2dlbmVyYXRlLUNBMSAwHgYJKoZIhvcNAQkBFhFub2NAY3Jhc2hkdW1wLm5l
dDAeFw0xNzAyMTYyMDUwMjJaFw0zMjAyMTMyMDUwMjJaMGgxFjAUBgNVBAMMDUEg
TVFUVCBicm9rZXIxFjAUBgNVBAoMDWNyYXNoZHVtcC5uZXQxFDASBgNVBAsMC2dl
bmVyYXRlLUNBMSAwHgYJKoZIhvcNAQkBFhFub2NAY3Jhc2hkdW1wLm5ldDCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPVaQ8Ut20IpEEnBETJEzDBe51n
X3UMZe/2ZFmJBWai2mpvnG3Tcqfd8JdEFsqHBlUZ2F5DuKOsZjpiRgFyRhh4tW2Q
qPKIp+rMJOJdTvSU/ct/gD4STVAQFBQSWdrHm+qWmzztTQTpGuTjoG0gQBGj/n/8
CAL6Er3cMnncwzVScTRdjbU8Al4eio4zRRNo0bg9tj8zf9uXxViLKCbOXVenDY0v
cAfj9eSbLwh5b3wGlSo1amOBb8E/xgoq86RcduT71vare8puSSPCOwSjEnRfuco/
tIuEaERTrYPp8czGBo2pA5z0Lp0jvJ3d5DK2jF/g2LZyvMsZdg/yI0OTrOkCAwEA
AaNQME4wHQYDVR0OBBYEFFhKhZO96OYLn3mjBwdANnEwykYmMB8GA1UdIwQYMBaA
FFhKhZO96OYLn3mjBwdANnEwykYmMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEN
BQADggEBABYdNzUcvTX891sE6lrHAxw2NwgiMh45ERYZD12WyrDCJIAp+h5DP1sI
BvCakNTEbqXTEX2R7/5gQSMmgfnbGMDdf/qkFfB3qzx9VsJ1SYXR+FqUZ19ykD6Y
CJW+KrN5+hGtVv6HiNQrs5o8eaW4sD94HD1nV3Lt0/UFeL2hbbxC2HPAmRG3813/
fSSGg7MkQRAD2Wjt1hgfEG09wQJY9U9iCmgr9nAQpzN+5kQLZLjGksAG53HWV0sf
6UQ33NcofpL6njX+3mLNYOaKI808n4yorX/ffPBsZ+SJYYCdXyJmws2sCpQHymGg
F0NxUmaPTAifrf7Ia9JOg02Hh4cJlTo=
-----END CERTIFICATE-----`

func build_message() string {
// return the I'm alive message in the form of [current_epoch_time]:[hmac_verification]
// the [hmac_verification] is a sha256 hmac of [mqtt_id]:[the alive message itself]
// the hmac is to prevent a rogue device to send alive messages in place of another one
// the current time in epoch format provide the nonce against replay attacks
	now := time.Now()
	var my_msg string = fmt.Sprintf("%d", now.Unix())
	var my_verification string = mqtt_id + ":" + my_msg
	message_hmac := hmac.New(sha256.New, []byte(hmac_secret))
	message_hmac.Write([]byte(my_verification))
	myHmac := fmt.Sprintf("%s", base64.StdEncoding.EncodeToString(message_hmac.Sum(nil)))

	my_msg += ":" + myHmac
	return my_msg
} //message

func main() {
	root_ca := x509.NewCertPool()
	load_ca := root_ca.AppendCertsFromPEM([]byte(mqtt_tls_ca))
	if !load_ca {
		panic("failed to parse root certificate")
	}

	tlsConfig := &tls.Config{RootCAs: root_ca}

	opts := MQTT.NewClientOptions()
	opts.SetTLSConfig(tlsConfig)  //we set the tls configuration
	opts.AddBroker(broker)        //we add the broker
	opts.SetClientID(mqtt_id)     //we set the mqtt id
	opts.SetCleanSession(true)    // Sets true to client and server should remember state across restarts and reconnect
	opts.SetUsername(mqtt_login)  // Set the mqtt server login
	opts.SetPassword(mqtt_passwd) // Set the mqtt server password
	c := MQTT.NewClient(opts)     // Launch the client using the set options
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}

	var message string = build_message()
	text := fmt.Sprintf("%s", message)
	token := c.Publish(mqtt_subscribe, 0, false, text)
	token.Wait()
	c.Disconnect(250)
} //main

Cross compiling the final mqtt client

Because I’m coding on my Mac, I need to cross compile the final client for Linux. This can be easily achieved with the following command: env GOOS=linux GOARCH=amd64 go build ./yokai.go

Then we can launch the client (I’m using a simple crontab). Then if we subscribe to the mqtt server, we can see the following output:

$root@mordor:/etc/mosquitto# mosquitto_sub -h 45.63.115.108 --cafile /etc/CA/ca.crt -t "#" -p 8883 -u login -P password -v

dodomeki/alive/yokai-arkham 1487375041:sadIYNsoyVHr28uLegpwn5YScpm9x+xA3PV73ygVzVw=
dodomeki/alive/yokai-arkham 1487375101:Q2fSHQ61Uf7UGR94PKecHqONBKBFUPB3Z83Tfi/PIPA=

Next step will be to have a cookbook to use templating in order to customise the client before compiling it and then uploading it to any host I want to monitor.

TAGS: GOLAND, MQTT