EM-129
EM-129
В связи с нестабильностью питания решил наконец поставить хоть какое-то защитное реле что б не спалить технику
(в последний раз броски напряжения не пережил свитч Catalyst 3550 и я остался без PoE для все своих железок чем был сильно расстроен)
По EM-129 есть практически исчерпывающий обзор от Андрея, которому нет причин не доверять:
Однако в обзоре не упоминается ничего про программную часть устройства
Зачем?
Я принципиально не хочу отправлять свои данные в любой клауд, если это не мой клауд и привязывать устройство к чему-ьто в интернете мне кажется очень странной идеей
Пару слов об устройстве
Я не могу сказать ничего хорошего про реализацию безопастности EM-129
- Поддержка HTTPS отсутвует
- Кабельного подключения нет
Этих 2 факторов вполне достаточно что бы при желании можно было взломать сеть и отснифить пароль - в качестве "защиты" могу скаазть только что надо подойти достаточно близко что бы сниффить траффик.
Сигнал достаточно слабый хотя расстяние от места установки до точки доступа - примерно 5 метров и одна бетонная стена
/interface/wireless/registration-table/print Columns: INTERFACE, MAC-ADDRESS, AP, SIGNAL-STRENGTH, TX-RATE, UPTIME 2 wlan-2.4Ghz-ssid-infra-1 CC:50:E3:FA:83:55 no -81dBm@1Mbps 18Mbps 1d1h13m32s
Получение данных
О программной реализации интерфейса тоже можо сказать мало хорошего
Из плюсов могу отметить только то что инерфейс работает относительно быстро и не слишком уродлив, впрочем что и не удивительно - под капотом там JavaScript который только отсылает на устройство запросы данных, вся обработка и отрисовка происходит на стороне браузера
Из минусов - авторизация сделана максимально плохо
- Используется только пароль, без логина
- При авторизации создается Session ID (далее SID) который может быть только один и который по сути представляет собой префикс в URL для получения данных
- Прямое следствие этого - на устройство можно зайходить и смотреть только одному пользователю одновременно. При подключении второго SID меняется и первого "отлогинивает"
- BasicAuth был бы лучше в разы
Для того что бы понять как происходит авторизация пришлось немного покопаться в коде JS (что замечу говорит о "удачности" решения)
Авторизация происходит следующим образом
- Пользователь вводит пароль
- Браузер отправляет на устройство запрос что бы получить "соль" (случайное число) и тип устройства (похоже что аналогичный код используется не только в EM-129 но и других реле)
- На оснвани полученой соли, имени устройства и пользовательского пароля формируется "секретная строка" и отправляется на сервер (основое время было потрачено на то что б найти как формируется эта секретная строка, и я не пишу на JavaScript. Думаю у web-разработчика это заняло бы единицы минут а не пол часа как у меня)
- Если строка верна то сервер возвращает SID
- ВСЕ!
Код скрипта
27 строк на bash
- IP - адрес реле
- SALT_URL - адрес куда посылать запрос на "соль"
- TOKEN_URL - адрес куда послылать запрос на SID
- ALL_DATA_URI - суффикс запроса данных
Секретная строка формируется следующим образом:
- sha1 от строки состоящей из 3 частей
- имя устройства - Пользовательский пароль закодированный в base64 - "соль"
По шагам
- A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" - послать запрос на соль и взять поле с именем "SALT" из полученного json
- S=$(printf ${EM_129_PASSWORD} | base64) - закодировать пароль
- SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" - склеять строки
- LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" - взять sha1 от склейки строк
- SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" - отправить запрос на SID и взять из ответа значение поля SID
Получение данных дальше
- ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" - сформировать адрес куда слать запрос с учетом SID
- D=$(curl ${ALL_DATA_URL} | jq -r .) получить данные
Данные получаются в виде json
{ "volt_msr": 2302, "freq_msr": 5000, "cur_msr": 321, "pows_msr": 740, "enrga_msr": 32,. "enrga_d_msr": 0, "enrga_w_msr": 0, "enrga_m_msr": 0, "enrgs_msr": 39, "sys_flag": 1409482896, "faul_flag": 0, "ar_time": 0, "cost_energy": 1700, "cost_unit": 16, "STATUS": "OK" }
Код скрипта
#!/bin/bash set -eu${DEBUG:+x} IP="192.168.31.250" SALT_URL="http://${IP}/api/login?salt" TOKEN_URL="http://${IP}/api/login?login=" ALL_DATA_URI="api/all/get?volt_msr&freq_msr&cur_msr&pows_msr&enrga_msr&enrga_d_msr&enrga_w_msr&enrga_m_msr&enrgs_msr&sys_flag&faul_flag&ar_time&cost_energy&cost_unit" A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" E__EM_129_DEVNAME="EM-129" EM_129_PASSWORD="12345678" S=$(printf ${EM_129_PASSWORD} | base64) SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" D=$(curl ${ALL_DATA_URL} | jq -r .) if [ "${D}X" != "X" ]; then echo "${D}" else echo "{}" exit 1 fi exit 0
Интеграция с мониторингом
Имя возможность получать данные с устройства не составит труда сделать интеграцию за заббиксом или прометеем
В моем случае все максимально просто и практически полностью соответвует статье https://serveradmin.ru/parsing-i-peredacha-json-dannyih-v-zabbix/
- Добавить скрипт в агента (не забыть дать возможность агенту ходить на устройство если они в разных VLAN и между ними есть фильтрация)
cat /etc/zabbix/zabbix_agentd.d/UserParameter-ElectricCounter.conf
UserParameter=electricCounter,/etc/zabbix/scripts/counter.sh
Далее сделать зависимые Items как описано выше по сслыке. Готовый темплейт для особо ленивых ниже =)
Zabbix template
<?xml version="1.0" encoding="UTF-8"?> <zabbix_export> <version>5.0</version> <date>2022-08-14T12:27:09Z</date> <groups> <group> <name>Electric Counter</name> </group> </groups> <templates> <template> <template>Template Electric Counter</template> <name>Template Electric Counter</name> <groups> <group> <name>Electric Counter</name> </group> </groups> <applications> <application> <name>electricCounter</name> </application> </applications> <items> <item> <name>electricCounter</name> <key>electricCounter</key> <history>0</history> <trends>0</trends> <value_type>TEXT</value_type> <applications> <application> <name>electricCounter</name> </application> </applications> </item> <item> <name>electricCounter.current</name> <type>DEPENDENT</type> <key>electricCounter.current</key> <delay>0</delay> <value_type>FLOAT</value_type> <units>A</units> <applications> <application> <name>electricCounter</name> </application> </applications> <preprocessing> <step> <type>JSONPATH</type> <params>$.cur_msr</params> </step> <step> <type>MULTIPLIER</type> <params>0.01</params> </step> </preprocessing> <master_item> <key>electricCounter</key> </master_item> </item> <item> <name>electricCounter.power</name> <type>DEPENDENT</type> <key>electricCounter.powerVA</key> <delay>0</delay> <value_type>FLOAT</value_type> <units>VA</units> <applications> <application> <name>electricCounter</name> </application> </applications> <preprocessing> <step> <type>JSONPATH</type> <params>$.pows_msr</params> </step> </preprocessing> <master_item> <key>electricCounter</key> </master_item> </item> <item> <name>electricCounter.freq</name> <type>DEPENDENT</type> <key>electricCounter.vfreq</key> <delay>0</delay> <value_type>FLOAT</value_type> <units>Hz</units> <applications> <application> <name>electricCounter</name> </application> </applications> <preprocessing> <step> <type>JSONPATH</type> <params>$.freq_msr</params> </step> <step> <type>MULTIPLIER</type> <params>0.01</params> </step> </preprocessing> <master_item> <key>electricCounter</key> </master_item> </item> <item> <name>electricCounter.volt</name> <type>DEPENDENT</type> <key>electricCounter.volt</key> <delay>0</delay> <value_type>FLOAT</value_type> <units>V</units> <applications> <application> <name>electricCounter</name> </application> </applications> <preprocessing> <step> <type>JSONPATH</type> <params>$.volt_msr</params> </step> <step> <type>MULTIPLIER</type> <params>0.1</params> </step> </preprocessing> <master_item> <key>electricCounter</key> </master_item> </item> </items> <screens> <screen> <name>Counters</name> <hsize>1</hsize> <vsize>3</vsize> <screen_items> <screen_item> <resourcetype>0</resourcetype> <style>0</style> <resource> <name>Volts/Ampers</name> <host>Template Electric Counter</host> </resource> <width>1500</width> <height>200</height> <x>0</x> <y>0</y> <colspan>1</colspan> <rowspan>1</rowspan> <elements>0</elements> <valign>0</valign> <halign>0</halign> <dynamic>0</dynamic> <sort_triggers>0</sort_triggers> <url/> <application/> <max_columns>3</max_columns> </screen_item> <screen_item> <resourcetype>0</resourcetype> <style>0</style> <resource> <name>Power</name> <host>Template Electric Counter</host> </resource> <width>1500</width> <height>200</height> <x>0</x> <y>1</y> <colspan>1</colspan> <rowspan>1</rowspan> <elements>0</elements> <valign>0</valign> <halign>0</halign> <dynamic>0</dynamic> <sort_triggers>0</sort_triggers> <url/> <application/> <max_columns>3</max_columns> </screen_item> <screen_item> <resourcetype>0</resourcetype> <style>0</style> <resource> <name>Freq</name> <host>Template Electric Counter</host> </resource> <width>1500</width> <height>200</height> <x>0</x> <y>2</y> <colspan>1</colspan> <rowspan>1</rowspan> <elements>0</elements> <valign>0</valign> <halign>0</halign> <dynamic>0</dynamic> <sort_triggers>0</sort_triggers> <url/> <application/> <max_columns>3</max_columns> </screen_item> </screen_items> </screen> </screens> </template> </templates> <graphs> <graph> <name>Freq</name> <graph_items> <graph_item> <sortorder>1</sortorder> <color>1A7C11</color> <item> <host>Template Electric Counter</host> <key>electricCounter.vfreq</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Power</name> <graph_items> <graph_item> <sortorder>1</sortorder> <color>1A7C11</color> <item> <host>Template Electric Counter</host> <key>electricCounter.powerVA</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Volts/Ampers</name> <graph_items> <graph_item> <sortorder>1</sortorder> <color>F63100</color> <yaxisside>RIGHT</yaxisside> <item> <host>Template Electric Counter</host> <key>electricCounter.volt</key> </item> </graph_item> <graph_item> <sortorder>2</sortorder> <color>2774A4</color> <item> <host>Template Electric Counter</host> <key>electricCounter.current</key> </item> </graph_item> </graph_items> </graph> </graphs> </zabbix_export>