a1a17e81a1
Includes updated JS challenge scripts with Claude-User whitelist, same-site referer bypass, Blackbox-Exporter allowed bot, and all new exporters, cheat sheets, and automation scripts.
400 lines
12 KiB
Bash
400 lines
12 KiB
Bash
#!/bin/bash
|
|
|
|
################################################
|
|
#### Wickr Enterprise Metrics Collector ####
|
|
#### for Prometheus node_exporter textfile ####
|
|
#### ####
|
|
#### Author: Phil Connor ####
|
|
#### Contact: contact@mylinux.work ####
|
|
#### Version: 1.00-030726 ####
|
|
################################################
|
|
|
|
set -o pipefail
|
|
|
|
SCRIPT_NAME=$(basename "$0")
|
|
readonly SCRIPT_NAME
|
|
|
|
# Default configuration
|
|
readonly DEFAULT_NODE_DIR="/var/lib/node_exporter"
|
|
readonly DEFAULT_COLLECTION_INTERVAL=300
|
|
readonly DEFAULT_CURL_TIMEOUT=30
|
|
|
|
# Configuration variables (can be overridden by environment)
|
|
WICKR_ADMIN_URL=${WICKR_ADMIN_URL:-}
|
|
WICKR_API_TOKEN=${WICKR_API_TOKEN:-}
|
|
NODE_DIR=${NODE_DIR:-$DEFAULT_NODE_DIR}
|
|
COLLECTION_INTERVAL=${COLLECTION_INTERVAL:-$DEFAULT_COLLECTION_INTERVAL}
|
|
DEBUG=${DEBUG:-}
|
|
|
|
# Runtime flags
|
|
RUN_MODE="once"
|
|
|
|
# Error tracking
|
|
ERRORS_TOTAL=0
|
|
|
|
handle_error() {
|
|
local exit_code=$1
|
|
local line_number=$2
|
|
echo "Error: $SCRIPT_NAME failed at line $line_number with exit code $exit_code" >&2
|
|
exit "$exit_code"
|
|
}
|
|
|
|
trap 'handle_error $? $LINENO' ERR
|
|
|
|
debug_echo() {
|
|
if [[ -n "$DEBUG" ]]; then
|
|
echo "[DEBUG] $*" >&2
|
|
fi
|
|
}
|
|
|
|
show_help() {
|
|
cat << EOF
|
|
Usage: $SCRIPT_NAME [OPTIONS]
|
|
|
|
Wickr Enterprise metrics collector for Prometheus node_exporter textfile directory.
|
|
|
|
Collects user, bot, device, security group, and network statistics from the
|
|
Wickr Enterprise Admin API and writes them as Prometheus metrics.
|
|
|
|
OPTIONS:
|
|
--once Run collection once and exit (default)
|
|
--daemon Run continuously at COLLECTION_INTERVAL
|
|
--help, -h Show this help message
|
|
|
|
ENVIRONMENT VARIABLES:
|
|
WICKR_ADMIN_URL Wickr Enterprise admin URL (required, e.g. https://wickr.example.com)
|
|
WICKR_API_TOKEN Admin API token (required — generate in Admin Console → API Access)
|
|
NODE_DIR Node exporter textfile directory (default: $DEFAULT_NODE_DIR)
|
|
COLLECTION_INTERVAL Seconds between collections in daemon mode (default: $DEFAULT_COLLECTION_INTERVAL)
|
|
DEBUG Enable debug output
|
|
|
|
EXAMPLES:
|
|
WICKR_ADMIN_URL=https://wickr.example.com WICKR_API_TOKEN=abc123 $SCRIPT_NAME --once
|
|
WICKR_ADMIN_URL=https://wickr.example.com WICKR_API_TOKEN=abc123 $SCRIPT_NAME --daemon
|
|
|
|
OUTPUT:
|
|
Writes metrics to \$NODE_DIR/textfile_collector/wickr_metrics.prom
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--once) RUN_MODE="once"; shift ;;
|
|
--daemon) RUN_MODE="daemon"; shift ;;
|
|
--help|-h) show_help ;;
|
|
*) echo "Unknown option: $1" >&2; show_help ;;
|
|
esac
|
|
done
|
|
|
|
# Validate required variables
|
|
validate_config() {
|
|
if [[ -z "$WICKR_ADMIN_URL" ]]; then
|
|
echo "Error: WICKR_ADMIN_URL is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$WICKR_API_TOKEN" ]]; then
|
|
echo "Error: WICKR_API_TOKEN is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Strip trailing slash
|
|
WICKR_ADMIN_URL="${WICKR_ADMIN_URL%/}"
|
|
|
|
# Check textfile collector directory
|
|
local textfile_dir="${NODE_DIR}/textfile_collector"
|
|
if [[ ! -d "$textfile_dir" ]]; then
|
|
echo "Error: Textfile collector directory not found: $textfile_dir" >&2
|
|
echo "Create it: sudo mkdir -p $textfile_dir" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# API helper — makes authenticated requests to the Wickr Admin API
|
|
wickr_api() {
|
|
local endpoint="$1"
|
|
local url="${WICKR_ADMIN_URL}/api/v1${endpoint}"
|
|
|
|
debug_echo "GET $url"
|
|
|
|
local response
|
|
response=$(curl -sf --max-time "$DEFAULT_CURL_TIMEOUT" \
|
|
-H "Authorization: Bearer ${WICKR_API_TOKEN}" \
|
|
-H "Accept: application/json" \
|
|
"$url" 2>/dev/null)
|
|
|
|
local exit_code=$?
|
|
if [[ $exit_code -ne 0 ]]; then
|
|
debug_echo "API call failed: $url (exit code $exit_code)"
|
|
ERRORS_TOTAL=$((ERRORS_TOTAL + 1))
|
|
return 1
|
|
fi
|
|
|
|
echo "$response"
|
|
}
|
|
|
|
# Count items from a paginated API response
|
|
# Uses jq to count array items, handles pagination via response headers
|
|
wickr_api_count() {
|
|
local endpoint="$1"
|
|
local url="${WICKR_ADMIN_URL}/api/v1${endpoint}"
|
|
|
|
debug_echo "HEAD $url (count)"
|
|
|
|
# Try to get total from response headers or count array items
|
|
local response
|
|
response=$(curl -sf --max-time "$DEFAULT_CURL_TIMEOUT" \
|
|
-H "Authorization: Bearer ${WICKR_API_TOKEN}" \
|
|
-H "Accept: application/json" \
|
|
"$url" 2>/dev/null)
|
|
|
|
if [[ $? -ne 0 ]]; then
|
|
debug_echo "API count failed: $url"
|
|
ERRORS_TOTAL=$((ERRORS_TOTAL + 1))
|
|
echo "0"
|
|
return 1
|
|
fi
|
|
|
|
# Try to count array items
|
|
if command -v jq &>/dev/null; then
|
|
local count
|
|
count=$(echo "$response" | jq 'if type == "array" then length elif .data? and (.data | type == "array") then .data | length elif .total? then .total elif .count? then .count else 0 end' 2>/dev/null)
|
|
echo "${count:-0}"
|
|
else
|
|
echo "0"
|
|
fi
|
|
}
|
|
|
|
# Collect user metrics
|
|
collect_users() {
|
|
debug_echo "Collecting user metrics..."
|
|
|
|
local users_response
|
|
users_response=$(wickr_api "/users") || return 1
|
|
|
|
if command -v jq &>/dev/null && [[ -n "$users_response" ]]; then
|
|
local total active suspended pending
|
|
|
|
total=$(echo "$users_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null)
|
|
active=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "active" or .status? == "Active")] | length' 2>/dev/null)
|
|
suspended=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "suspended" or .status? == "Suspended")] | length' 2>/dev/null)
|
|
pending=$(echo "$users_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "pending" or .status? == "Pending")] | length' 2>/dev/null)
|
|
|
|
cat <<EOF
|
|
# HELP wickr_users_total Total number of Wickr users.
|
|
# TYPE wickr_users_total gauge
|
|
wickr_users_total ${total:-0}
|
|
|
|
# HELP wickr_users_active Number of active Wickr users.
|
|
# TYPE wickr_users_active gauge
|
|
wickr_users_active ${active:-0}
|
|
|
|
# HELP wickr_users_suspended Number of suspended Wickr users.
|
|
# TYPE wickr_users_suspended gauge
|
|
wickr_users_suspended ${suspended:-0}
|
|
|
|
# HELP wickr_users_pending Number of pending Wickr users.
|
|
# TYPE wickr_users_pending gauge
|
|
wickr_users_pending ${pending:-0}
|
|
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Collect bot metrics
|
|
collect_bots() {
|
|
debug_echo "Collecting bot metrics..."
|
|
|
|
local bots_response
|
|
bots_response=$(wickr_api "/bots") || return 1
|
|
|
|
if command -v jq &>/dev/null && [[ -n "$bots_response" ]]; then
|
|
local total active
|
|
|
|
total=$(echo "$bots_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null)
|
|
active=$(echo "$bots_response" | jq '[if type == "array" then .[] elif .data? then .data[] else empty end | select(.status? == "active" or .status? == "Active" or .status? == "online" or .status? == "Online")] | length' 2>/dev/null)
|
|
|
|
cat <<EOF
|
|
# HELP wickr_bots_total Total number of Wickr bots.
|
|
# TYPE wickr_bots_total gauge
|
|
wickr_bots_total ${total:-0}
|
|
|
|
# HELP wickr_bots_active Number of active Wickr bots.
|
|
# TYPE wickr_bots_active gauge
|
|
wickr_bots_active ${active:-0}
|
|
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Collect security group metrics
|
|
collect_security_groups() {
|
|
debug_echo "Collecting security group metrics..."
|
|
|
|
local sg_response
|
|
sg_response=$(wickr_api "/securitygroups") || return 1
|
|
|
|
if command -v jq &>/dev/null && [[ -n "$sg_response" ]]; then
|
|
local total
|
|
total=$(echo "$sg_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null)
|
|
|
|
cat <<EOF
|
|
# HELP wickr_security_groups_total Total number of security groups.
|
|
# TYPE wickr_security_groups_total gauge
|
|
wickr_security_groups_total ${total:-0}
|
|
|
|
EOF
|
|
|
|
# Per-group user counts if available
|
|
echo "$sg_response" | jq -r '
|
|
(if type == "array" then . elif .data? then .data else [] end)[]
|
|
| select(.name? != null)
|
|
| "wickr_security_group_users{group=\"\(.name)\"} \(.userCount // .user_count // 0)"
|
|
' 2>/dev/null | while IFS= read -r line; do
|
|
if [[ -z "$_sg_header_printed" ]]; then
|
|
echo "# HELP wickr_security_group_users Number of users per security group."
|
|
echo "# TYPE wickr_security_group_users gauge"
|
|
_sg_header_printed=1
|
|
fi
|
|
echo "$line"
|
|
done
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# Collect network metrics
|
|
collect_networks() {
|
|
debug_echo "Collecting network metrics..."
|
|
|
|
local net_response
|
|
net_response=$(wickr_api "/networks") || return 1
|
|
|
|
if command -v jq &>/dev/null && [[ -n "$net_response" ]]; then
|
|
local total
|
|
total=$(echo "$net_response" | jq 'if type == "array" then length elif .data? then (.data | length) elif .total? then .total else 0 end' 2>/dev/null)
|
|
|
|
cat <<EOF
|
|
# HELP wickr_networks_total Total number of Wickr networks.
|
|
# TYPE wickr_networks_total gauge
|
|
wickr_networks_total ${total:-0}
|
|
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Collect device metrics (aggregate from users)
|
|
collect_devices() {
|
|
debug_echo "Collecting device metrics..."
|
|
|
|
local users_response
|
|
users_response=$(wickr_api "/users") || return 1
|
|
|
|
if command -v jq &>/dev/null && [[ -n "$users_response" ]]; then
|
|
local device_count
|
|
device_count=$(echo "$users_response" | jq '
|
|
[if type == "array" then .[] elif .data? then .data[] else empty end
|
|
| (.devices // .device_count // 0)
|
|
| if type == "array" then length else . end] | add // 0
|
|
' 2>/dev/null)
|
|
|
|
cat <<EOF
|
|
# HELP wickr_devices_total Total number of registered devices.
|
|
# TYPE wickr_devices_total gauge
|
|
wickr_devices_total ${device_count:-0}
|
|
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Check API reachability
|
|
collect_health() {
|
|
debug_echo "Checking Wickr API health..."
|
|
|
|
local start_time end_time duration_ms
|
|
start_time=$(date +%s%N)
|
|
|
|
if curl -sf --max-time "$DEFAULT_CURL_TIMEOUT" \
|
|
-H "Authorization: Bearer ${WICKR_API_TOKEN}" \
|
|
"${WICKR_ADMIN_URL}/api/v1/networks" &>/dev/null; then
|
|
local up=1
|
|
else
|
|
local up=0
|
|
fi
|
|
|
|
end_time=$(date +%s%N)
|
|
duration_ms=$(( (end_time - start_time) / 1000000 ))
|
|
|
|
cat <<EOF
|
|
# HELP wickr_api_up Whether the Wickr Admin API is reachable (1=up, 0=down).
|
|
# TYPE wickr_api_up gauge
|
|
wickr_api_up $up
|
|
|
|
# HELP wickr_api_response_time_ms Wickr Admin API response time in milliseconds.
|
|
# TYPE wickr_api_response_time_ms gauge
|
|
wickr_api_response_time_ms $duration_ms
|
|
|
|
EOF
|
|
}
|
|
|
|
# Write collection metadata
|
|
collect_metadata() {
|
|
cat <<EOF
|
|
# HELP wickr_exporter_errors_total Total number of errors during collection.
|
|
# TYPE wickr_exporter_errors_total counter
|
|
wickr_exporter_errors_total $ERRORS_TOTAL
|
|
|
|
# HELP wickr_exporter_last_success_timestamp Unix timestamp of last successful collection.
|
|
# TYPE wickr_exporter_last_success_timestamp gauge
|
|
wickr_exporter_last_success_timestamp $(date +%s)
|
|
|
|
EOF
|
|
}
|
|
|
|
# Main collection function
|
|
collect_all() {
|
|
ERRORS_TOTAL=0
|
|
local output_dir="${NODE_DIR}/textfile_collector"
|
|
local output_file="${output_dir}/wickr_metrics.prom"
|
|
local temp_file
|
|
temp_file=$(mktemp "${output_file}.XXXXXX")
|
|
|
|
debug_echo "Starting collection..."
|
|
|
|
{
|
|
collect_health
|
|
collect_users
|
|
collect_bots
|
|
collect_security_groups
|
|
collect_networks
|
|
collect_devices
|
|
collect_metadata
|
|
} > "$temp_file" 2>/dev/null
|
|
|
|
mv "$temp_file" "$output_file"
|
|
|
|
debug_echo "Collection complete. Wrote to $output_file (errors: $ERRORS_TOTAL)"
|
|
}
|
|
|
|
# Main
|
|
main() {
|
|
validate_config
|
|
|
|
case "$RUN_MODE" in
|
|
once)
|
|
collect_all
|
|
;;
|
|
daemon)
|
|
echo "$SCRIPT_NAME running in daemon mode (interval: ${COLLECTION_INTERVAL}s)"
|
|
while true; do
|
|
collect_all
|
|
sleep "$COLLECTION_INTERVAL"
|
|
done
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main
|