Update to new mqtt generation (#442)

This commit is contained in:
Pascal Vizeli
2018-11-05 00:50:29 +01:00
committed by GitHub
parent efb08ed09c
commit f3ed1ccc29
7 changed files with 298 additions and 63 deletions

View File

@@ -1,5 +1,11 @@
# Changelog
## 3
- Use auto setup (discovery) on Home Assistant
- Publish his service to Hass.io
- Attach to Home Assistant user system
- Set anonymous default to false
## 2.0
- Update mosquitto to 1.4.15
- New options to allow clients to connect through websockets

View File

@@ -4,13 +4,30 @@ FROM $BUILD_FROM
# Add env
ENV LANG C.UTF-8
# Setup base
RUN apk add --no-cache jq mosquitto
# Install mosquitto + auth plugin
WORKDIR /usr/src
ARG MOSQUITTO_AUTH_VERSION
RUN apk add --no-cache \
mosquitto curl libressl musl socat \
&& apk add --no-cache --virtual .build-dependencies \
gcc make git mosquitto-dev curl-dev libressl-dev musl-dev \
&& git clone --depth 1 https://github.com/jpmens/mosquitto-auth-plug \
&& cd mosquitto-auth-plug \
&& cp config.mk.in config.mk \
&& sed -i "s/?= yes/?= no/g" config.mk \
&& sed -i "s/HTTP ?= no/HTTP ?= yes/g" config.mk \
&& make \
&& mkdir -p /usr/share/mosquitto \
&& cp -f auth-plug.so /usr/share/mosquitto \
&& apk del .build-dependencies \
&& rm -fr /usr/src/mosquitto-auth-plug
# Copy data
COPY run.sh /
COPY auth_srv.sh /bin/
COPY mosquitto.conf /etc/
RUN chmod a+x /run.sh
RUN chmod a+x /run.sh /bin/auth_srv.sh
WORKDIR /
CMD [ "/run.sh" ]

116
mosquitto/auth_srv.sh Normal file
View File

@@ -0,0 +1,116 @@
#!/bin/bash
set -e
CONFIG_PATH=/data/options.json
SYSTEM_USER=/data/system_user.json
REQUEST=()
REQUEST_BODY=""
declare -A LOCAL_DB
## Functions
function http_ok() {
echo -e "HTTP/1.1 200 OK\n"
exit 0
}
function http_error() {
echo -e "HTTP/1.1 400 Bad Request\n"
exit 0
}
function create_userdb() {
local logins=0
local username=""
local password=""
local hass_pw=""
local addons_pw=""
logins=$(jq --raw-output '.logins | length' $CONFIG_PATH)
for (( i=0; i < "$logins"; i++ )); do
username="$(jq --raw-output ".logins[$i].username" $CONFIG_PATH)"
password="$(jq --raw-output ".logins[$i].password" $CONFIG_PATH)"
LOCAL_DB["${username}"]="${password}"
done
# Add system user to DB
hass_pw=$(jq --raw-output '.homeassistant.password' $SYSTEM_USER)
addons_pw=$(jq --raw-output '.addons.password' $SYSTEM_USER)
LOCAL_DB['homeassistant']="${hass_pw}"
LOCAL_DB['addons']="${addons_pw}"
}
function read_request() {
local content_length=0
while read -r line; do
line="${line%%[[:cntrl:]]}"
if [[ "${line}" =~ Content-Length ]]; then
content_length="${line//[!0-9]/}"
fi
if [ -z "$line" ]; then
if [ "${content_length}" -gt 0 ]; then
read -r -n "${content_length}" REQUEST_BODY
fi
break
fi
REQUEST+=("$line")
done
}
function get_var() {
local variable=$1
local value=""
urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; }
# shellcheck disable=SC2001
value="$(echo "$REQUEST_BODY" | sed "s/.*$variable=\([^&]*\).*/\1/g")"
urldecode "${value}"
}
## MAIN ##
read_request
# This feature currently not implemented, we response with 200
if [[ "${REQUEST[0]}" =~ /superuser ]] || [[ "${REQUEST[0]}" =~ /acl ]]; then
http_ok
fi
# We read now the user data
create_userdb
username="$(get_var username)"
password="$(get_var password)"
# If local user
if [ "${LOCAL_DB["${username}"]}" == "${password}" ]; then
echo "[INFO] found ${username} on local database" >&2
http_ok
elif [ ${LOCAL_DB["${username}"]+_} ]; then
echo "[WARN] Not found ${username} on local database" >&2
http_error
fi
# Ask HomeAssistant Auth
auth_header="X-Hassio-Key: ${HASSIO_TOKEN}"
content_type="Content-Type: application/x-www-form-urlencoded"
if curl -s -f -X POST -d "${REQUEST_BODY}" -H "${content_type}" -H "${auth_header}" http://hassio/auth > /dev/null; then
echo "[INFO] found ${username} on Home Assistant" >&2
http_ok
fi
echo "[ERROR] Auth error with ${username}" >&2
http_error

5
mosquitto/build.json Normal file
View File

@@ -0,0 +1,5 @@
{
"args": {
"MOSQUITTO_AUTH_VERSION": "0.1.3"
}
}

View File

@@ -1,25 +1,24 @@
{
"name": "Mosquitto broker",
"version": "2.0",
"version": "3",
"slug": "mosquitto",
"description": "An Open Source MQTT broker",
"url": "https://home-assistant.io/addons/mosquitto/",
"startup": "system",
"boot": "auto",
"map": ["ssl", "share"],
"discovery": ["mqtt"],
"services": ["mqtt:provide"],
"auth_api": true,
"ports": {
"1883/tcp": 1883,
"1884/tcp": 1884,
"8883/tcp": 8883,
"8884/tcp": 8884
},
"map": ["ssl", "share"],
"options": {
"plain": true,
"plain_websockets": false,
"ssl": false,
"ssl_websockets": false,
"anonymous": true,
"logins": [],
"anonymous": false,
"customize": {
"active": false,
"folder": "mosquitto"
@@ -28,14 +27,10 @@
"keyfile": "privkey.pem"
},
"schema": {
"plain": "bool",
"plain_websockets": "bool",
"ssl": "bool",
"ssl_websockets": "bool",
"anonymous": "bool",
"logins": [
{"username": "str", "password": "str"}
],
"anonymous": "bool",
"customize": {
"active": "bool",
"folder": "str"

View File

@@ -14,6 +14,22 @@ persistence_location /data/
##
# User settings
#password_file /data/users.db
#allow_anonymous false
auth_plugin /usr/share/mosquitto/auth-plug.so
auth_opt_backends http
auth_opt_http_ip 127.0.0.1
auth_opt_http_port 8080
auth_opt_http_getuser_uri /login
auth_opt_http_superuser_uri /superuser
auth_opt_http_aclcheck_uri /acl
allow_anonymous %%ANONYMOUS%%
#include_dir /share/mosquitto
listener 1883
protocol mqtt
listener 1884
protocol websockets
# Follow SSL listener if a certificate exists

View File

@@ -2,26 +2,16 @@
set -e
CONFIG_PATH=/data/options.json
SYSTEM_USER=/data/system_user.json
PLAIN=$(jq --raw-output ".plain" $CONFIG_PATH)
PLAIN_WS=$(jq --raw-output ".plain_websockets" $CONFIG_PATH)
SSL=$(jq --raw-output ".ssl" $CONFIG_PATH)
SSL_WS=$(jq --raw-output ".ssl_websockets" $CONFIG_PATH)
LOGINS=$(jq --raw-output ".logins | length" $CONFIG_PATH)
ANONYMOUS=$(jq --raw-output ".anonymous" $CONFIG_PATH)
KEYFILE=$(jq --raw-output ".keyfile" $CONFIG_PATH)
CERTFILE=$(jq --raw-output ".certfile" $CONFIG_PATH)
CUSTOMIZE_ACTIVE=$(jq --raw-output ".customize.active" $CONFIG_PATH)
PLAIN_CONFIG="
listener 1883
protocol mqtt
"
PLAIN_WS_CONFIG="
listener 1884
protocol websockets
"
HOMEASSISTANT_PW=
ADDONS_PW=
WAIT_PIDS=()
SSL_CONFIG="
listener 8883
@@ -29,9 +19,7 @@ protocol mqtt
cafile /ssl/$CERTFILE
certfile /ssl/$CERTFILE
keyfile /ssl/$KEYFILE
"
SSL_WS_CONFIG="
listener 8884
protocol websockets
cafile /ssl/$CERTFILE
@@ -39,25 +27,71 @@ certfile /ssl/$CERTFILE
keyfile /ssl/$KEYFILE
"
# Add plain configs
if [ "$PLAIN" == "true" ]; then
echo "$PLAIN_CONFIG" >> /etc/mosquitto.conf
fi
if [ "$PLAIN_WS" == "true" ]; then
echo "$PLAIN_WS_CONFIG" >> /etc/mosquitto.conf
fi
function create_password() {
strings /dev/urandom | tr -dc _A-Z-a-z-0-9 | head -c32
}
# Add ssl configs
if [ "$SSL" == "true" ]; then
function write_system_users() {
(
echo "{\"homeassistant\": {\"password\": \"$HOMEASSISTANT_PW\"}, \"addons\": {\"password\": \"$ADDONS_PW\"}}"
) > "${SYSTEM_USER}"
}
function call_hassio() {
local method=$1
local path=$2
local data="${3}"
local token=
token="X-Hassio-Key: ${HASSIO_TOKEN}"
url="http://hassio/${path}"
# Call API
if [ -n "${data}" ]; then
curl -f -s -X "${method}" -d "${data}" -H "${token}" "${url}"
else
curl -f -s -X "${method}" -H "${token}" "${url}"
fi
return $?
}
function constrain_host_config() {
local user=$1
local password=$2
echo "{"
echo " \"host\": \"$(hostname)\","
echo " \"port\": 1883,"
echo " \"ssl\": false,"
echo " \"protocol\": \"3.1.1\","
echo " \"username\": \"${user}\","
echo " \"password\": \"${password}\""
echo "}"
}
function constrain_discovery() {
local user=$1
local password=$2
local config=
config="$(constrain_host_config "${user}" "${password}")"
echo "{"
echo " \"service\": \"mqtt\","
echo " \"config\": ${config}"
echo "}"
}
## Main ##
echo "[INFO] Setup mosquitto configuration"
sed -i "s/%%ANONYMOUS%%/$ANONYMOUS/g" /etc/mosquitto.conf
# Enable SSL if exists configs
if [ -e "/ssl/$CERTFILE" ] && [ -e "/ssl/$KEYFILE" ]; then
echo "$SSL_CONFIG" >> /etc/mosquitto.conf
fi
if [ "$SSL_WS" == "true" ]; then
echo "$SSL_WS_CONFIG" >> /etc/mosquitto.conf
fi
# Allow anonymous connections
if [ "$ANONYMOUS" == "false" ]; then
sed -i "s/#allow_anonymous/allow_anonymous/g" /etc/mosquitto.conf
else
echo "[WARN] SSL not enabled - No valid certs found!"
fi
# Allow customize configs from share
@@ -66,19 +100,65 @@ if [ "$CUSTOMIZE_ACTIVE" == "true" ]; then
sed -i "s|#include_dir .*|include_dir /share/$CUSTOMIZE_FOLDER|g" /etc/mosquitto.conf
fi
# Generate user data
# Handle local users
if [ "$LOGINS" -gt "0" ]; then
sed -i "s/#password_file/password_file/g" /etc/mosquitto.conf
rm -f /data/users.db || true
touch /data/users.db
for (( i=0; i < "$LOGINS"; i++ )); do
USERNAME=$(jq --raw-output ".logins[$i].username" $CONFIG_PATH)
PASSWORD=$(jq --raw-output ".logins[$i].password" $CONFIG_PATH)
mosquitto_passwd -b /data/users.db "$USERNAME" "$PASSWORD"
done
echo "[INFO] Found local users inside config"
else
echo "[INFO] No local user available"
fi
# start server
exec mosquitto -c /etc/mosquitto.conf < /dev/null
# Prepare System Accounts
if [ ! -e "${SYSTEM_USER}" ]; then
HOMEASSISTANT_PW="$(create_password)"
ADDONS_PW="$(create_password)"
echo "[INFO] Initialize system configuration."
write_system_users
else
HOMEASSISTANT_PW=$(jq --raw-output '.homeassistant.password' $SYSTEM_USER)
ADDONS_PW=$(jq --raw-output '.addons.password' $SYSTEM_USER)
fi
# Initial Service
if call_hassio GET "services/mqtt" | jq --raw-output ".data.host" | grep -v "$(hostname)" > /dev/null; then
echo "[WARN] There is allready a MQTT services running!"
else
echo "[INFO] Initialize Hass.io Add-on services"
if ! call_hassio POST "services/mqtt" "$(constrain_host_config addons "${ADDONS_PW}")" > /dev/null; then
echo "[ERROR] Can't setup Hass.io service mqtt"
fi
echo "[INFO] Initialize Home Assistant discovery"
if ! call_hassio POST "discovery" "$(constrain_discovery homeassistant "${HOMEASSISTANT_PW}")" > /dev/null; then
echo "[ERROR] Can't setup Home Assistant discovery mqtt"
fi
fi
echo "[INFO] Start Mosquitto daemon"
# Start Auth Server
socat TCP-LISTEN:8080,fork,reuseaddr SYSTEM:/bin/auth_srv.sh &
WAIT_PIDS+=($!)
# Start Mosquitto Server
mosquitto -c /etc/mosquitto.conf &
WAIT_PIDS+=($!)
# Handling Closing
function stop_mqtt() {
echo "[INFO] Shutdown mqtt system"
kill -15 "${WAIT_PIDS[@]}"
# Remove service
if call_hassio GET "services/mqtt" | jq --raw-output ".data.host" | grep "$(hostname)" > /dev/null; then
if ! call_hassio DELETE "services/mqtt"; then
echo "[Warn] Service unregister fails!"
fi
fi
wait "${WAIT_PIDS[@]}"
}
trap "stop_mqtt" SIGTERM SIGHUP
# Wait and hold Add-on running
wait "${WAIT_PIDS[@]}"