Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions bin/get_ips
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
[ "$IOTEMPOWER_ACTIVE" = "yes" ] || { echo "IoTempower not active, aborting." 1>&2;exit 1; }
source <( iot_env ignore_system )

source "$IOTEMPOWER_ROOT/bin/mqtt_client_options"

if [[ "$1" ]]; then
topic="iotempower/_cfg_/$1/ip"
result=$(timeout --foreground 2 mosquitto_sub -C 1 $filter -v \
-h "$IOTEMPOWER_MQTT_HOST" -t "$topic"|cut -d/ -f3-|cut -d\ -f2)
result=$(env "${mqtt_client_env[@]}" timeout --foreground 2 mosquitto_sub -C 1 $filter -v \
-h "$IOTEMPOWER_MQTT_HOST" -p "$mqtt_port" "${mqtt_client_options[@]}" \
-t "$topic"|cut -d/ -f3-|cut -d\ -f2)
if [[ "$result" ]]; then
echo "IP for $1:" >&2
echo "$result"
Expand All @@ -25,8 +28,9 @@ else # find all ending in /ip
filter=""
while true; do
echo -n "."
timeout --foreground 2 mosquitto_sub -C 1 $filter -v \
-h "$IOTEMPOWER_MQTT_HOST" -t "$topic" | grep "/ip " > "$iplog"
env "${mqtt_client_env[@]}" timeout --foreground 2 mosquitto_sub -C 1 $filter -v \
-h "$IOTEMPOWER_MQTT_HOST" -p "$mqtt_port" "${mqtt_client_options[@]}" \
-t "$topic" | grep "/ip " > "$iplog"
t=$(cat "$iplog"|cut -d\ -f1)
ip=$(cat "$iplog"|cut -d\ -f2)
if [[ ! "$t" ]]; then
Expand All @@ -40,4 +44,3 @@ else # find all ending in /ip
cat "$iplogall" | cut -d/ -f3-
rm "$iplog" "$iplogall"
fi

4 changes: 3 additions & 1 deletion bin/iot_env
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ else
fi

# output and quote result
env|grep -E "^IOTEMPOWER"|sed 's/^\([^=]*\)=\(.*\)$/export\ \1=\"\2\"/g'
for var_name in $(compgen -A variable IOTEMPOWER); do
printf 'export %s=%q\n' "$var_name" "${!var_name}"
done
13 changes: 3 additions & 10 deletions bin/mqtt_action
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,15 @@ else
fi
fi

ca_file_option=""
if [[ "$IOTEMPOWER_MQTT_USE_TLS" == 1 ]]; then
if [[ ! "$IOTEMPOWER_MQTT_CERT_FOLDER" ]]; then
echo "MQTT TLS enabled, but no certificate folder set. Aborting." 1>&2
exit 1
fi
ca_file_option=(--cafile "$IOTEMPOWER_MQTT_CERT_FOLDER/ca.crt")
fi
source "$IOTEMPOWER_ROOT/bin/mqtt_client_options"

last_data="123ulno.net321"


echo "Subscribing to mqtt://$IOTEMPOWER_MQTT_HOST/$topic " >&2
echo "Subscribing to mqtt://$IOTEMPOWER_MQTT_HOST:$mqtt_port/$topic " >&2
echo "with trigger $trigger_type for data $trigger_data" >&2
echo "executing: $@ <received payload>" >&2
mosquitto_sub -h "$IOTEMPOWER_MQTT_HOST" "${ca_file_option[@]}" -t "$topic" \
env "${mqtt_client_env[@]}" mosquitto_sub -h "$IOTEMPOWER_MQTT_HOST" -p "$mqtt_port" "${mqtt_client_options[@]}" -t "$topic" \
| while read data; do
echo "mqtt_action: received $data" >&2
changed=""
Expand Down
204 changes: 198 additions & 6 deletions bin/mqtt_broker
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,161 @@ APTEMP="$IOTEMPOWER_LOCAL/tmp/mosquitto"
rm -rf "$APTEMP" &> /dev/null
mkdir -p "$APTEMP"

mqtt_auth_enabled=false

mqtt_fail() {
echo "$1" 1>&2
exit 1
}

mqtt_create_password_file() {
local password_file="$1"
local password_file_tmp="${password_file}.tmp"
local old_umask=""
local result=0

rm -f "$password_file_tmp"
old_umask="$(umask)"
umask 077
if printf '%s\n%s\n' "$IOTEMPOWER_MQTT_PW" "$IOTEMPOWER_MQTT_PW" \
| mosquitto_passwd -c "$password_file_tmp" "$IOTEMPOWER_MQTT_USER" >/dev/null; then
chmod 600 "$password_file_tmp" || result=1
mv -f "$password_file_tmp" "$password_file" || result=1
chmod 600 "$password_file" || result=1
else
result=1
fi
umask "$old_umask"
if [[ "$result" != 0 ]]; then
rm -f "$password_file_tmp"
return 1
fi
}

mqtt_write_acl_file() {
local acl_file="$1"
local system_dir=""
local node_conf=""
local node_topic=""

{
echo "user $IOTEMPOWER_MQTT_USER"
echo "topic read #"
echo "topic write iotempower/_cfg_/#"
if [[ "$IOTEMPOWER_MQTT_DISCOVERY_PREFIX" ]]; then
echo "topic write $IOTEMPOWER_MQTT_DISCOVERY_PREFIX/#"
fi

if [[ "$IOTEMPOWER_SYSTEM_CONFIG" && -e "$IOTEMPOWER_SYSTEM_CONFIG" ]]; then
system_dir="$(dirname "$IOTEMPOWER_SYSTEM_CONFIG")"
while IFS= read -r -d '' node_conf; do
node_topic="$(mqtt_node_topic_from_path "$system_dir" "$node_conf")" || exit 1
if [[ "$node_topic" ]]; then
echo "topic write $node_topic/#"
fi
done < <(find "$system_dir" -name node.conf -print0)
fi
} > "$acl_file"
}

mqtt_node_topic_from_path() {
local system_dir="$1"
local node_conf="$2"
local node_dir=""
local rel_topic=""
local configured_topic=""

system_dir="$(cd "$system_dir" && pwd -P)" || return 1
node_dir="$(cd "$(dirname "$node_conf")" && pwd -P)" || return 1

if [[ "$node_dir" == "$system_dir" ]]; then
rel_topic=""
else
case "$node_dir" in
"$system_dir"/*)
rel_topic="${node_dir#"$system_dir"/}"
;;
*)
return 1
;;
esac
fi

configured_topic="$(mqtt_read_node_topic "$node_conf")" || return 1
if [[ "$configured_topic" ]]; then
printf '%s' "$configured_topic"
return 0
fi

printf '%s' "$rel_topic"
}

mqtt_read_node_topic() {
local node_conf="$1"
local line=""
local value=""
local parsed_topic=""

while IFS= read -r line || [[ "$line" ]]; do
[[ "$line" =~ ^[[:space:]]*topic[[:space:]]*= ]] || continue
value="${line#*=}"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
parsed_topic="$(mqtt_parse_config_value "$value")" || return 1
done < "$node_conf"

printf '%s' "$parsed_topic"
}

mqtt_parse_config_value() {
local value="$1"
local result=""
local char=""
local next_char=""
local rest=""

if [[ "$value" == \"* ]]; then
value="${value:1}"
while [[ "$value" ]]; do
char="${value:0:1}"
value="${value:1}"
if [[ "$char" == "\\" && "$value" ]]; then
next_char="${value:0:1}"
value="${value:1}"
result+="$next_char"
elif [[ "$char" == '"' ]]; then
rest="$value"
rest="${rest#"${rest%%[![:space:]]*}"}"
[[ ! "$rest" || "$rest" == \#* ]] || return 1
printf '%s' "$result"
return 0
else
result+="$char"
fi
done
return 1
elif [[ "$value" == \'* ]]; then
value="${value:1}"
[[ "$value" == *"'"* ]] || return 1
result="${value%%\'*}"
rest="${value#*\'}"
rest="${rest#"${rest%%[![:space:]]*}"}"
[[ ! "$rest" || "$rest" == \#* ]] || return 1
printf '%s' "$result"
else
case "$value" in
*[[:space:]]*|*[\`\$\\]*)
return 1
;;
*)
value="${value%%#*}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
;;
esac
fi
}

while true; do
IF1="$1"
IP1="$2"
Expand Down Expand Up @@ -61,6 +216,22 @@ while true; do
echo "Listening on $IOTEMPOWER_MQTT_HOST."
[[ "$IOTEMPOWER_MQTT_HOST2" ]] && echo "Also listening on $IOTEMPOWER_MQTT_HOST2."

mqtt_auth_enabled=false
if [[ "$IOTEMPOWER_MQTT_USER" || "$IOTEMPOWER_MQTT_PW" ]]; then
mqtt_auth_enabled=true
[[ "$IOTEMPOWER_MQTT_USER" && "$IOTEMPOWER_MQTT_PW" ]] \
|| mqtt_fail "MQTT user and password must both be set when MQTT auth is configured. Aborting."
[[ "$IOTEMPOWER_MQTT_USE_TLS" == 1 ]] || mqtt_fail "MQTT auth requires TLS. Aborting."
[[ "$IOTEMPOWER_MQTT_USER" != *[[:space:]]* ]] || mqtt_fail "MQTT username may not contain whitespace. Aborting."
[[ "$IOTEMPOWER_MQTT_USER" != *:* ]] || mqtt_fail "MQTT username may not contain colon. Aborting."
command -v mosquitto_passwd >/dev/null || mqtt_fail "MQTT auth requires mosquitto_passwd. Aborting."

mqtt_create_password_file "$APTEMP/password_file" \
|| mqtt_fail "Failed to generate Mosquitto password file. Aborting."
mqtt_write_acl_file "$APTEMP/acl_file" || mqtt_fail "Failed to generate Mosquitto ACL file. Aborting."
chmod 600 "$APTEMP/acl_file"
fi

# Create mosquitto config - user is needed for root on docker
MQTT_BROKER_USER="${USER:-$(id -un 2>/dev/null)}"
MQTT_BROKER_USER_LINE=""
Expand All @@ -73,14 +244,17 @@ persistence_location $APTEMP/mosquitto.db
log_dest stdout
log_type error
log_type warning
listener 1883 127.0.0.1
listener 1883 $IOTEMPOWER_MQTT_HOST
allow_anonymous true
EOF

if [[ "$IOTEMPOWER_MQTT_HOST2" ]]; then
if [[ "$mqtt_auth_enabled" == true ]]; then
cat << EOF >> "$APTEMP/mosquitto.conf"
listener 1883 $IOTEMPOWER_MQTT_HOST2
allow_anonymous false
password_file $APTEMP/password_file
acl_file $APTEMP/acl_file
EOF
else
cat << EOF >> "$APTEMP/mosquitto.conf"
allow_anonymous true
EOF
fi

Expand All @@ -97,8 +271,26 @@ cafile $IOTEMPOWER_MQTT_CERT_FOLDER/ca.crt
certfile $IOTEMPOWER_MQTT_CERT_FOLDER/server.crt
keyfile $IOTEMPOWER_MQTT_CERT_FOLDER/server.key
require_certificate false
allow_anonymous true
EOF
if [[ "$IOTEMPOWER_MQTT_HOST2" ]]; then
cat << EOF >> "$APTEMP/mosquitto.conf"
listener 8883 $IOTEMPOWER_MQTT_HOST2
cafile $IOTEMPOWER_MQTT_CERT_FOLDER/ca.crt
certfile $IOTEMPOWER_MQTT_CERT_FOLDER/server.crt
keyfile $IOTEMPOWER_MQTT_CERT_FOLDER/server.key
require_certificate false
EOF
fi
else
cat << EOF >> "$APTEMP/mosquitto.conf"
listener 1883 127.0.0.1
listener 1883 $IOTEMPOWER_MQTT_HOST
EOF
if [[ "$IOTEMPOWER_MQTT_HOST2" ]]; then
cat << EOF >> "$APTEMP/mosquitto.conf"
listener 1883 $IOTEMPOWER_MQTT_HOST2
EOF
fi
fi

# Create bridge if needed
Expand Down
84 changes: 84 additions & 0 deletions bin/mqtt_client_options
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash

# Source after IoTempower MQTT configuration has been loaded.
# Produces:
# mqtt_port
# mqtt_client_options
# mqtt_client_env

mqtt_port=1883
mqtt_client_options=()
mqtt_client_env=()
_iotempower_mqtt_client_config_dir=""

_iotempower_mqtt_client_cleanup() {
local status=$?
if [[ "$_iotempower_mqtt_client_config_dir" ]]; then
rm -rf -- "$_iotempower_mqtt_client_config_dir"
_iotempower_mqtt_client_config_dir=""
fi
return "$status"
}

_iotempower_mqtt_client_signal_exit() {
local status="$1"
trap - INT TERM
exit "$status"
}

_iotempower_mqtt_write_client_config() {
local config_file="$1"

( umask 077; : > "$config_file" ) || return 1
{
printf -- '--username %s\n' "$IOTEMPOWER_MQTT_USER"
printf -- '--pw %s\n' "$IOTEMPOWER_MQTT_PW"
} > "$config_file" || return 1
chmod 600 "$config_file" || return 1
}

if [[ "$IOTEMPOWER_MQTT_USE_TLS" == 1 ]]; then
if [[ ! "$IOTEMPOWER_MQTT_CERT_FOLDER" ]]; then
echo "MQTT TLS enabled, but no certificate folder set. Aborting." 1>&2
exit 1
fi
mqtt_port=8883
mqtt_client_options+=(--cafile "$IOTEMPOWER_MQTT_CERT_FOLDER/ca.crt")
fi

if [[ "$IOTEMPOWER_MQTT_USER" || "$IOTEMPOWER_MQTT_PW" ]]; then
if [[ ! "$IOTEMPOWER_MQTT_USER" || ! "$IOTEMPOWER_MQTT_PW" ]]; then
echo "MQTT user and password must both be set when MQTT auth is configured. Aborting." 1>&2
exit 1
fi
if [[ "$IOTEMPOWER_MQTT_USE_TLS" != 1 ]]; then
echo "MQTT auth requires TLS. Aborting." 1>&2
exit 1
fi
_iotempower_mqtt_client_config_dir="$(mktemp -d "${TMPDIR:-/tmp}/iotempower-mqtt-client.XXXXXXXXXX")" || {
echo "Failed to create private MQTT client config directory. Aborting." 1>&2
exit 1
}
trap _iotempower_mqtt_client_cleanup EXIT
trap '_iotempower_mqtt_client_signal_exit 130' INT
trap '_iotempower_mqtt_client_signal_exit 143' TERM

chmod 700 "$_iotempower_mqtt_client_config_dir" || {
echo "Failed to secure MQTT client config directory. Aborting." 1>&2
exit 1
}

_iotempower_mqtt_write_client_config "$_iotempower_mqtt_client_config_dir/mosquitto_pub" || {
echo "Failed to write private mosquitto_pub config. Aborting." 1>&2
exit 1
}
_iotempower_mqtt_write_client_config "$_iotempower_mqtt_client_config_dir/mosquitto_sub" || {
echo "Failed to write private mosquitto_sub config. Aborting." 1>&2
exit 1
}
mqtt_client_env=(
IOTEMPOWER_MQTT_USER=
IOTEMPOWER_MQTT_PW=
XDG_CONFIG_HOME="$_iotempower_mqtt_client_config_dir"
)
fi
Loading
Loading