#!/usr/bin/env sh # shellcheck shell=dash set -e : << =cut =head1 NAME synapse_ - Monitor synapse matrix homeserver over admin API =head1 APPLICABLE SYSTEMS Synapse matrix homeserver =head1 CONFIGURATION Requires installed curl and jq, a command-line json processor. This is a wildcard plugin. It monitors some simple values over the admin API of synapse matrix homeserver. Link synapse_ to this file. The admin endpoint has to be reachable from the plugin. If matrix authentication service is used, env.disable_users has to be set to false and the env.auth_token has to be a compatibility token from the matrix authentication service. ln -s /usr/share/munin/plugins/synapse_ \ /etc/munin/plugins/synapse_domain.tld Set parameters in your munin-node configuration [synapse_homeserverurl] env.auth_token env.disable_users env.interval env.port env.admin_api_path env.scheme env.timeout env.reports_warning To monitor a synapse instance on localhost you need following: ln -s /usr/share/munin/plugins/synapse_ \ /etc/munin/plugins/synapse_localhost [synapse_localhost] env.auth_token env.port 8008 env.scheme http It's advised to use a dedicated munin bot account (user_type bot) with admin rights on your matrix synapse server for this plugin. If matrix authentication service is enabled on the matrix synapse server, disable the users check and use a compatibility token. =head1 AUTHOR Copyright (C) 2024 Sebastian L. (https://momou.ch), =head1 LICENSE GPLv2 =head1 MAGIC MARKERS #%# family=manual #%# capabilities=autoconf =cut # shellcheck disable=SC1090 . "$MUNIN_LIBDIR/plugins/plugin.sh" if [ "${MUNIN_DEBUG:-0}" = 1 ]; then set -x fi AUTH_TOKEN="${auth_token:-}" DISABLE_USERS="${disable_users:-false}" INTERVAL="${interval:-300}" PORT="${port:-443}" ADMIN_API_PATH="${admin_api_path:-/_synapse/admin}" QUERY_LIMIT="${query_limit:-10000}" HOMESERVER="${0##*synapse_}" SCHEME="${scheme:-https}://" TIMEOUT="${timeout:-2}" REPORTS_WARNING="${reports_warning:-1}" CLEANHOMESERVER="$(clean_fieldname "${HOMESERVER}")" fetch_url () { curl -s -f -m "${TIMEOUT}" "$@" } case $1 in autoconf) if [ ! -x "$(command -v curl)" ]; then echo "no (curl not found)" elif [ ! -x "$(command -v jq)" ]; then echo "no (jq not found)" else fetch_url -I -H "Authorization: Bearer ${AUTH_TOKEN}" -I "${SCHEME}${HOMESERVER}:${PORT}${ADMIN_API_PATH}" \ | grep -iq "Content-Type: application/json" \ && echo "yes" \ || echo "no (invalid or empty response from synapse admin api)" fi exit 0 ;; config) USERS_CONFIG="" if [ "$DISABLE_USERS" != "true" ]; then USERS_CONFIG=" multigraph synapse_users_${CLEANHOMESERVER} graph_title Synapse users on ${HOMESERVER} graph_args --base 1000 -l 0 graph_printf %.0lf graph_vlabel users graph_info users graph_category chat total_registered.label total registered users total_registered.info total registered users total_registered.min 0 active_users.label active users active_users.info active users active_users.min 0 bots.label active bots bots.info active bots bots.min 0 online_users.label online users online_users.info online users online_users.min 0 online_users.draw LINE2 deactivated_users.label deactivated users deactivated_users.info deactivated users deactivated_users.min 0 erased_users.label erased users erased_users.info erased users erased_users.min 0" fi cat << EOM $USERS_CONFIG multigraph synapse_rooms_${CLEANHOMESERVER} graph_title Synapse spaces and rooms on ${HOMESERVER} graph_args --base 1000 -l 0 graph_printf %.0lf graph_vlabel rooms graph_info rooms graph_category chat rooms_local.draw AREASTACK rooms_local.label rooms on ${HOMESERVER} rooms_local.info rooms on ${HOMESERVER} rooms_local.min 0 rooms_external.draw AREASTACK rooms_external.label external rooms rooms_external.info external rooms rooms_external.min 0 spaces.label spaces spaces.info spaces spaces.min 0 rooms_total.label total rooms rooms_total.info total rooms rooms_total.min 0 rooms_total.draw LINE2 multigraph synapse_reports_${CLEANHOMESERVER} graph_title Synapse event reports on ${HOMESERVER} graph_args --base 1000 -l 0 graph_printf %.0lf graph_vlabel reports graph_info reports graph_category chat event_reports.label reports event_reports.info reports event_reports.min 0 event_reports.warning ${REPORTS_WARNING} EOM exit 0 ;; esac if [ "$DISABLE_USERS" != "true" ]; then mktemp_command="${mktemp_command:-/usr/bin/mktemp}" USERS_FILE=$($mktemp_command) || exit 73 trap 'rm -f "$USERS_FILE"' EXIT fetch_url -H "Authorization: Bearer ${AUTH_TOKEN}" "${SCHEME}${HOMESERVER}:${PORT}${ADMIN_API_PATH}/v2/users?deactivated=true&limit=${QUERY_LIMIT}" > "$USERS_FILE" fi ROOMS=$(fetch_url -H "Authorization: Bearer ${AUTH_TOKEN}" "${SCHEME}${HOMESERVER}:${PORT}${ADMIN_API_PATH}/v1/rooms?limit=${QUERY_LIMIT}") REPORTS=$(fetch_url -H "Authorization: Bearer ${AUTH_TOKEN}" "${SCHEME}${HOMESERVER}:${PORT}${ADMIN_API_PATH}/v1/event_reports" | jq .total) if [ "$DISABLE_USERS" != "true" ]; then echo "multigraph synapse_users_${CLEANHOMESERVER}" if total_number="$(jq -r .total "$USERS_FILE" | grep -E "^[0-9]+$")"; then puppets="$(jq -r '.users[] | select(.user_type!="bot") | select(.user_type!="support") | select(.last_seen_ts==null)' "$USERS_FILE")" bots="$(jq -r '.users[] | select(.user_type=="bot")' "$USERS_FILE")" users="$(jq -r '.users[] | select(.user_type!="support") | select(.user_type!="bot") | select(.last_seen_ts!=null)' "$USERS_FILE")" puppets_number="$(echo "$puppets" | grep -c '"last_seen_ts": null')" bots_number="$(echo "$bots" | grep -c '"user_type": "bot"')" virtual_users_number=$(( puppets_number + bots_number )) total_registered_number=$(( total_number - virtual_users_number )) # Convert to miliseconds time_ms=$(($(date +%s) * 1000)) interval_ms=$((INTERVAL * 1000)) time_interval_ago=$(( time_ms - interval_ms )) last_seen_times_ms=$(echo "$users" | jq -r 'select(.deactivated==false)' | grep -E "\"last_seen_ts\": [0-9]+") online_users_number="$(echo "$last_seen_times_ms" | awk -v "count=0" -F": " '$2 > "'$time_interval_ago'" {count++} END {print count}')" echo total_registered.value "$total_registered_number" echo active_users.value "$(echo "$users" | grep -c '"deactivated": false')" echo bots.value "$(echo "$bots" | grep -c '"deactivated": false')" echo online_users.value "$online_users_number" echo deactivated_users.value "$(echo "$users" | grep -c '"deactivated": true')" echo erased_users.value "$(echo "$users" | grep -c '"erased": true')" else echo "total_registered.value U" echo "active_users.value U" echo "bots.value U" echo "online_users.value U" echo "deactivated_users.value U" echo "erased_users.value U" fi fi echo multigraph synapse_rooms_"${CLEANHOMESERVER}" if ROOMS="$(echo "$ROOMS" | jq -r)"; then total_rooms="$(echo "$ROOMS" | jq -r .total_rooms)" rooms_local="$(echo "$ROOMS" | grep -cE "\"room_id\": \"\!.+:${HOMESERVER}\"")" rooms_external=$(( total_rooms - rooms_local )) echo rooms_local.value "$rooms_local" echo rooms_external.value "$rooms_external" echo spaces.value "$(echo "$ROOMS" | grep -c '"room_type": "m.space"')" echo rooms_total.value "$total_rooms" else echo "rooms_local.value U" echo "rooms_external.value U" echo "spaces.value U" echo "rooms_total.value U" fi echo multigraph synapse_reports_"${CLEANHOMESERVER}" if [ -n "$REPORTS" ] && [ "$REPORTS" -eq "$REPORTS" ]; then echo event_reports.value "$REPORTS" else echo "event_reports.value U" fi