EM-129: различия между версиями
Sirmax (обсуждение | вклад) (→EM-129) |
Sirmax (обсуждение | вклад) |
||
(не показано 7 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
+ | [[Категория:Linux]] |
||
+ | [[Категория:IoT]] |
||
+ | [[Категория:EM-129]] |
||
+ | [[Категория:Networking]] |
||
+ | [[Категория:Zabbix]] |
||
+ | |||
+ | |||
=EM-129= |
=EM-129= |
||
В связи с нестабильностью питания решил наконец поставить хоть какое-то защитное реле что б не спалить технику <BR> |
В связи с нестабильностью питания решил наконец поставить хоть какое-то защитное реле что б не спалить технику <BR> |
||
Строка 46: | Строка 53: | ||
* Пользователь вводит пароль |
* Пользователь вводит пароль |
||
* Браузер отправляет на устройство запрос что бы получить "соль" (случайное число) и тип устройства (похоже что аналогичный код используется не только в EM-129 но и других реле) |
* Браузер отправляет на устройство запрос что бы получить "соль" (случайное число) и тип устройства (похоже что аналогичный код используется не только в EM-129 но и других реле) |
||
− | * На оснвани полученой соли, имени устройства и пользовательского пароля формируется "секретная строка" и отправляется на сервер |
+ | * На оснвани полученой соли, имени устройства и пользовательского пароля формируется "секретная строка" и отправляется на сервер (основое время было потрачено на то что б найти как формируется эта секретная строка, и я не пишу на JavaScript. Думаю у web-разработчика это заняло бы единицы минут а не пол часа как у меня) |
* Если строка верна то сервер возвращает SID |
* Если строка верна то сервер возвращает SID |
||
* ВСЕ! |
* ВСЕ! |
||
Строка 57: | Строка 64: | ||
* TOKEN_URL - адрес куда послылать запрос на SID |
* TOKEN_URL - адрес куда послылать запрос на SID |
||
* ALL_DATA_URI - суффикс запроса данных |
* ALL_DATA_URI - суффикс запроса данных |
||
+ | <BR> |
||
− | |||
+ | <BR> |
||
Секретная строка формируется следующим образом: |
Секретная строка формируется следующим образом: |
||
Строка 64: | Строка 72: | ||
- Пользовательский пароль закодированный в base64 |
- Пользовательский пароль закодированный в base64 |
||
- "соль" |
- "соль" |
||
+ | <BR> |
||
− | |||
По шагам |
По шагам |
||
− | * A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')" - послать запрос на соль и взять поле с именем "SALT" из полученного json |
+ | * '''A__EM_129_SALT="$(curl ${SALT_URL} | jq -r '.SALT')'''" - послать запрос на соль и взять поле с именем "SALT" из полученного json |
− | * S=$(printf ${EM_129_PASSWORD} | base64) - закодировать пароль |
+ | * '''S=$(printf ${EM_129_PASSWORD} | base64)''' - закодировать пароль |
− | * SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}" - склеять строки |
+ | * '''SUM="${E__EM_129_DEVNAME}${S}${A__EM_129_SALT}"''' - склеять строки |
− | * LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')" - взять sha1 от склейки строк |
+ | * '''LOGIN="$(printf "${SUM}" | sha1sum | awk '{ print $1}')"''' - взять sha1 от склейки строк |
− | * SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')" - отправить запрос на SID и взять из ответа значение поля SID |
+ | * '''SID="$(curl "${TOKEN_URL}${LOGIN}"| jq -r '.SID')"''' - отправить запрос на SID и взять из ответа значение поля SID |
+ | <BR> |
||
− | |||
Получение данных дальше |
Получение данных дальше |
||
− | * ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}" - сформировать адрес куда слать запрос с учетом SID |
+ | * '''ALL_DATA_URL="http://${IP}/${SID}/${ALL_DATA_URI}"''' - сформировать адрес куда слать запрос с учетом SID |
− | * D=$(curl ${ALL_DATA_URL} | jq -r .) получить данные |
+ | * '''D=$(curl ${ALL_DATA_URL} | jq -r .)''' получить данные |
+ | <BR> |
||
− | |||
+ | <BR> |
||
− | Данные получаются в виде json |
||
+ | Данные получаются в виде json (для некоторых полей нужен множитель 0.1 или 0.01 - сопоставить с интерфейсом устройства не сложно) |
||
+ | <BR> |
||
<PRE> |
<PRE> |
||
{ |
{ |
||
Строка 129: | Строка 139: | ||
exit 0 |
exit 0 |
||
</PRE> |
</PRE> |
||
+ | |||
+ | =Интеграция с мониторингом= |
||
+ | Имя возможность получать данные с устройства не составит труда сделать интеграцию за заббиксом или прометеем |
||
+ | <BR> |
||
+ | В моем случае все максимально просто и практически полностью соответвует статье https://serveradmin.ru/parsing-i-peredacha-json-dannyih-v-zabbix/ |
||
+ | |||
+ | * Добавить скрипт в агента (не забыть дать возможность агенту ходить на устройство если они в разных VLAN и между ними есть фильтрация) |
||
+ | <PRE> |
||
+ | cat /etc/zabbix/zabbix_agentd.d/UserParameter-ElectricCounter.conf |
||
+ | </PRE> |
||
+ | <PRE> |
||
+ | UserParameter=electricCounter,/etc/zabbix/scripts/counter.sh |
||
+ | </PRE> |
||
+ | Далее сделать зависимые Items как описано выше по сслыке. Готовый темплейт для особо ленивых ниже =) |
||
+ | |||
+ | =Zabbix template= |
||
+ | {{#spoiler:show=zabbix template| |
||
+ | |||
+ | <PRE> |
||
+ | <?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> |
||
+ | |||
+ | </PRE> |
||
+ | }} |
Текущая версия на 15:59, 24 августа 2022
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 (для некоторых полей нужен множитель 0.1 или 0.01 - сопоставить с интерфейсом устройства не сложно)
{ "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 как описано выше по сслыке. Готовый темплейт для особо ленивых ниже =)