#!/bin/bash # ABOUT softether vpn for zabbix # author mikedmorto 2026 year export LC_ALL="" export LANG="en_US.UTF-8" version="3.3" . /etc/zabbix/scripts/sevpn3.cfg JSON="" ##### PARAMETERS##### METRIC="$1" ITEM="$2" API_URL="https://${URL}/api/" CURL_OPTS="-s -k" [ "$API_INSECURE" = "1" ] && CURL_OPTS="$CURL_OPTS -k" api_call() { local method=$1 local params=$2 [ -z "$params" ] && params="{}" local id="1" local data=$(cat </dev/null } case "$METRIC" in discovery) hubs_json=$(api_call "EnumHub") # ... проверка ошибок hubs=$(echo "$hubs_json" | jq -r '.result.HubList[].HubName_str' 2>/dev/null) JSON='{"data":[' first_hub=1 for hub in $hubs; do # Добавляем сам хаб if [ $first_hub -eq 1 ]; then first_hub=0; else JSON="${JSON},"; fi JSON="${JSON}{\"{#HUB_NAME}\":\"$hub\"}" # Пользователи хаба users_json=$(api_call "EnumUser" "{\"HubName_str\":\"$hub\"}") if ! echo "$users_json" | jq -e '.error' >/dev/null 2>&1; then users=$(echo "$users_json" | jq -r '.result.UserList[].Name_str' 2>/dev/null) for user in $users; do JSON="${JSON},{\"{#HUB_USER_NAME}\":\"$hub,$user\"}" done fi # Каскады хаба links_json=$(api_call "EnumLink" "{\"HubName_str\":\"$hub\"}") if ! echo "$links_json" | jq -e '.error' >/dev/null 2>&1; then links=$(echo "$links_json" | jq -r '.result.LinkList[].AccountName_utf' 2>/dev/null) for link in $links; do JSON="${JSON},{\"{#HUB_CASCADE_NAME}\":\"$hub,$link\"}" done fi done # --- Локальные мосты (один раз после цикла по хабам) --- bridges_json=$(api_call "EnumLocalBridge") if ! echo "$bridges_json" | jq -e '.error' >/dev/null 2>&1; then while IFS= read -r bridge_entry; do # bridge_entry имеет формат "hub,device" JSON="${JSON},{\"{#HUB_BRIDGE_NAME}\":\"$bridge_entry\"}" done < <(echo "$bridges_json" | jq -r '.result.LocalBridgeList[] | "\(.HubNameLB_str),\(.DeviceName_str)"' 2>/dev/null) fi JSON="${JSON}]}" echo "$JSON" ;; script.version) echo ${version} ;; srv.uptime) resp=$(api_call "GetServerStatus") # Проверка на ошибку API if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi # Извлекаем дату запуска из поля StartTime_dt start_date_iso=$(echo "$resp" | jq -r '.result.StartTime_dt' 2>/dev/null) if [ -z "$start_date_iso" ] || [ "$start_date_iso" = "null" ]; then echo 0 exit 1 fi # Преобразуем ISO8601 в timestamp start_ts=$(date -d "$start_date_iso" +%s 2>/dev/null) if [ -z "$start_ts" ]; then echo 0 exit 1 fi now=$(date +%s) uptime=$((now - start_ts)) echo "$uptime" ;; srv.number_of_users) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi # Извлекаем количество пользователей (поле Users) users=$(echo "$resp" | jq -r '.result.NumUsers_u32' 2>/dev/null) # Если поле отсутствует или null, выводим 0 if [ -z "$users" ] || [ "$users" = "null" ]; then echo 0 else echo "$users" fi ;; srv.number_of_ip_address_tables) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi echo "$resp" | jq -r '.result.NumIpTables_u32' 2>/dev/null || echo 0 ;; srv.number_of_mac_address_tables) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi echo "$resp" | jq -r '.result.NumMacTables_u32' 2>/dev/null || echo 0 ;; srv.number_of_sessions) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi echo "$resp" | jq -r '.result.NumSessionsTotal_u32' 2>/dev/null || echo 0 ;; srv.number_of_virtual_hubs) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi echo "$resp" | jq -r '.result.NumHubTotal_u32' 2>/dev/null || echo 0 ;; srv.number_of_active_sockets) resp=$(api_call "GetServerStatus") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi echo "$resp" | jq -r '.result.NumTcpConnections_u32' 2>/dev/null || echo 0 ;; srv.l2tp_status) echo `$VCMD /SERVER ${URL} /PASSWORD:${PWD} /CMD IPsecGet | grep "L2TP over IPsec Server Function Enabled" | awk -F"|" '{print $2}'` ;; srv.openvpn_status) echo `$VCMD /SERVER ${URL} /PASSWORD:${PWD} /CMD openvpnget | grep "OpenVPN Clone Server Enabled" | awk -F"|" '{print $2}'` ;; srv.version) resp=$(api_call "GetServerInfo") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /PASSWORD:${PWD} /CMD ServerStatusGet | awk 'FNR==3' else version=$(echo "$resp" | jq -r '.result.ServerVersionString_str' 2>/dev/null) [ -n "$version" ] && [ "$version" != "null" ] && echo "$version" || $VCMD /SERVER ${URL} /PASSWORD:${PWD} /CMD ServerStatusGet | awk 'FNR==3' fi ;; # hub.if.out.broad hub.if.out.broad) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result."Send.BroadcastBytes_u64"' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.if.in.broad hub.if.in.broad) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result."Recv.BroadcastBytes_u64"' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.if.out.uni hub.if.out.uni) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result."Send.UnicastBytes_u64"' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.if.in.uni hub.if.in.uni) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result."Recv.UnicastBytes_u64"' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.sessions-bridge hub.sessions-bridge) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result.NumSessionsBridge_u32' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.sessions-client hub.sessions-client) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result.NumSessionsClient_u32' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.sessions-all hub.sessions-all) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result.NumSessions_u32' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # hub.securenat hub.securenat) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo "false" exit 1 fi val=$(echo "$resp" | jq -r '.result.SecureNATEnabled_bool' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo "false" else # Приводим к строке true/false (Zabbix ожидает строку) echo "$val" fi ;; # hub.type hub.type) resp=$(api_call "GetHubStatus" "{\"HubName_str\":\"$ITEM\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then echo 0 exit 1 fi val=$(echo "$resp" | jq -r '.result.HubType_u32' 2>/dev/null) if [ -z "$val" ] || [ "$val" = "null" ]; then echo 0 else echo "$val" fi ;; # br.status br.status) HUB=${ITEM%,*} BR=${ITEM##*,} resp=$(api_call "EnumLocalBridge") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then # Fallback на старый метод $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD bridgelist | tr -d ' ' | awk '-F|' -v VH="${HUB}" -v VB="${BR}" '{if ($2==VH && $3==VB) print $4}' else # Ищем в списке мостов запись с нужным хабом и именем устройства status=$(echo "$resp" | jq -r --arg hub "$HUB" --arg br "$BR" '.result.LocalBridgeList[] | select(.HubNameLB_str == $hub and .DeviceName_str == $br) | .Online_bool' 2>/dev/null | head -1) if [ -n "$status" ] && [ "$status" != "null" ]; then # Выводим true/false (оригинал выводил то же) echo "$status" else # Если не нашли, пробуем старый метод $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD bridgelist | tr -d ' ' | awk '-F|' -v VH="${HUB}" -v VB="${BR}" '{if ($2==VH && $3==VB) print $4}' fi fi ;; usr.if.out.broad) HUB=${ITEM%,*} USER=${ITEM##*,} resp=$(api_call "GetUser" "{\"HubName_str\":\"$HUB\",\"Name_str\":\"$USER\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD UserGet ${USER} | grep "Outgoing Broadcast Total Size" | sed -e 's/[^0-9]//g' else val=$(echo "$resp" | jq -r '.result."Send.BroadcastBytes_u64"' 2>/dev/null) echo "${val:-0}" fi ;; # usr.if.in.broad usr.if.in.broad) HUB=${ITEM%,*} USER=${ITEM##*,} resp=$(api_call "GetUser" "{\"HubName_str\":\"$HUB\",\"Name_str\":\"$USER\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD UserGet ${USER} | grep "Incoming Broadcast Total Size" | sed -e 's/[^0-9]//g' else val=$(echo "$resp" | jq -r '.result."Recv.BroadcastBytes_u64"' 2>/dev/null) echo "${val:-0}" fi ;; # usr.if.out.uni usr.if.out.uni) HUB=${ITEM%,*} USER=${ITEM##*,} resp=$(api_call "GetUser" "{\"HubName_str\":\"$HUB\",\"Name_str\":\"$USER\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD UserGet ${USER} | grep "Outgoing Unicast Total Size" | sed -e 's/[^0-9]//g' else val=$(echo "$resp" | jq -r '.result."Send.UnicastBytes_u64"' 2>/dev/null) echo "${val:-0}" fi ;; # usr.if.in.uni usr.if.in.uni) HUB=${ITEM%,*} USER=${ITEM##*,} resp=$(api_call "GetUser" "{\"HubName_str\":\"$HUB\",\"Name_str\":\"$USER\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /PASSWORD:${PWD} /adminhub:${HUB} /CMD UserGet ${USER} | grep "Incoming Unicast Total Size" | sed -e 's/[^0-9]//g' else val=$(echo "$resp" | jq -r '.result."Recv.UnicastBytes_u64"' 2>/dev/null) echo "${val:-0}" fi ;; # usr.lastlogin usr.lastlogin) HUB=${ITEM%,*} USER=${ITEM##*,} resp=$(api_call "GetUser" "{\"HubName_str\":\"$HUB\",\"Name_str\":\"$USER\"}") if echo "$resp" | jq -e '.error' >/dev/null 2>&1; then $VCMD /SERVER ${URL} /adminhub:"$HUB" /PASSWORD:"${PWD}" /CMD UserGet "${USER}" | grep "Updated on" | awk -F"|" '{print $2}' else updated=$(echo "$resp" | jq -r '.result.UpdatedTime_dt' 2>/dev/null) if [ -n "$updated" ] && [ "$updated" != "null" ]; then # Преобразуем ISO дату в timestamp ts=$(date -d "$updated" +%s 2>/dev/null) echo "${ts:-0}" else echo 0 fi fi ;; help) echo "please use these params { discovery srv.uptime srv.number_of_users srv.number_of_ip_address_tables srv.number_of_mac_address_tables srv.number_of_sessions srv.number_of_virtual_hubs srv.number_of_active_sockets srv.l2tp_status srv.openvpn_status srv.version hub.if.out.broad hub.if.in.broad hub.if.out.uni hub.if.in.uni hub.sessions-bridge hub.sessions-client hub.sessions-all hub.securenat hub.type cas.status cas.if.in.broad cas.if.out.broad cas.if.out cas.if.in cas.if.in.uni cas.if.out.uni br.status usr.lastlogin usr.if.out.broad usr.if.in.broad usr.if.in.uni usr.if.out.uni script.version }" ;; *) echo "" ;; esac