mirror of
https://github.com/aljazceru/addons.git
synced 2025-12-18 13:44:20 +01:00
Update to new mqtt generation (#442)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
116
mosquitto/auth_srv.sh
Normal 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
5
mosquitto/build.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"args": {
|
||||
"MOSQUITTO_AUTH_VERSION": "0.1.3"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
172
mosquitto/run.sh
172
mosquitto/run.sh
@@ -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[@]}"
|
||||
|
||||
Reference in New Issue
Block a user