Skip to content

Draft: experiments with remote control via MQTT

Ludovic LANGE requested to merge llange/woob:pr-otp-experiments into master

Experimentation with an external process to feed 2FAs.
In some cases, you need to have 2FA sent to your (automated) woob process. You do not want to have to interactively process them (in a terminal, etc...) so this is a mean to decouple the input of the 2FA from the execution of your woob process or application.

Here we use a MQTT server as a mean to decouple things - but there could be other implementations with other mechanisms (local like a pipe, remote, etc...).

To configure the MQTT server used, you need to update (or create) the config file for your application (e.g.: ~/.config/woob/bill) with these values:

[ROOT]
mqtt_server = <hostname of your MQTT server>
mqtt_port = <MQTT server port, i.e. 8883>
mqtt_username = <MQTT server username>
mqtt_password = <MQTT server password, can be an external command, like `pass mqtt/woob`>

When you launch your woob application or command, it will connect to your MQTT server, and subscribe to the (hardcoded) woob/# topic (meaning all topics below woob/). And will logout at the end.
A debug log will confirm the proper connection / deinit.

When a 2FA requests comes from a module (it must be a subclass of OTPQuestion), it will then wait (for a maximum of 20s) for a MQTT message posted on woob/<backend.name>. This message is taken as-is, and used as an answer to the application question.

With this, you can:

  • Manually submit OTP codes to your application. Example:
mosquitto_pub -u <MQTT username> -P <MQTT password> -t "woob/XXXXXXXX" -h localhost -p 8883 -m "123456"
  • Use a mail filter on your IMAP server to extract OTP from mails, and feed them to your application. (Cf example below)
  • Use a phone application, or a mobile modem SMS application, to extract OTP from received SMSes, and feed them to your application (no example at the moment)

IMAP example for dovecot + sieve

/etc/dovecot/conf.d/90-sieve-extprograms.conf:

plugin {
  sieve_execute_bin_dir = /home/USERNAME/SIEVE_EXECUTE_BIN
}

/home/USERNAME/sievescript:

...
# Woob 2FA
if allof (
        address :is :domain "from" "XXXXXXXXXXX.fr",
        address :is :localpart "from" "ne-pas-repondre",
        header :matches "subject" "XXXXXXXXX - Code de sécurité"
) {
        debug_log "Received 2FA from XXXXX Envelope.To=${to} Envelope.From=${from} msgid=${msgid} Address.From=${addr_from} Address.To=${addr_to} Address.Resent-To=${addr_resent_to} Address.Cc=${addr_cc} Address.Bcc=${addr_bcc}";
        execute :pipe "2fa_to_woob.sh" ["XXXXX"];
} 
elsif allof (
        address :is :domain "from" "YYYYYYYY.com",
        address :is :localpart "from" "noreply",
        header :matches "subject" "Votre code d’authentification"
) {
        debug_log "Received 2FA from YYYYYYYYY Envelope.To=${to} Envelope.From=${from} msgid=${msgid} Address.From=${addr_from} Address.To=${addr_to} Address.Resent-To=${addr_resent_to} Address.Cc=${addr_cc} Address.Bcc=${addr_bcc}";
        execute :pipe "2fa_to_woob.sh" ["YYYYYY"];
}
...

/home/USERNAME/SIEVE_EXECUTE_BIN/2fa_to_woob.sh:

#!/bin/bash

DOMAIN="$1"

if [ "${DOMAIN}" == "XXXXXX" ]; then
	CODE=$(cat | htmlq .codeConfidentiel -t)
	TARGET="woob/XXXXXX"

elif [ "${DOMAIN}" == "YYYYYY" ]; then
	CODE=$(cat | htmlq strong -t)
	TARGET="woob/YYYYYY"

else
	exit 255
fi

mosquitto_pub -u <MQTT username> -P <MQTT password> -t "${TARGET}" -h <MQTT Server> -p 8883 -m "${CODE}"

Merge request reports