Sync all scripts from website downloads — 352 scripts total

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.
This commit is contained in:
2026-05-25 03:31:08 +02:00
parent dbd6bf0324
commit a1a17e81a1
332 changed files with 174509 additions and 1106 deletions
+755
View File
@@ -0,0 +1,755 @@
#!/bin/bash
################################################################################
# Script Name: rsyslog-metrics-exporter.sh
# Version: 1.0
# Description: Prometheus exporter for rsyslog internal metrics via impstats
# JSON output. Exports queue depths, action success/failure,
# input message counts, process resource usage, and overall
# rsyslog health metrics.
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Prerequisites:
# - rsyslog with impstats module enabled (JSON output to file)
# - jq (JSON parser)
# - netcat (nc) for HTTP mode
# - Standard Unix tools (awk, grep, tail)
#
# Performance:
# The stats file is read once per collection cycle — the last occurrence
# of each named object is extracted in a single pass using jq.
# Typical run time: under one second.
#
# Usage:
# # One-shot output to stdout
# ./rsyslog-metrics-exporter.sh --once
#
# # Textfile collector mode (daemon, writes every COLLECTION_INTERVAL)
# ./rsyslog-metrics-exporter.sh --daemon --textfile
#
# # HTTP server mode
# ./rsyslog-metrics-exporter.sh --http -p 9199
#
# Metrics Exported:
# Core Status:
# - rsyslog_up - rsyslog process status (1=running, 0=down)
# - rsyslog_info{version} - rsyslog version info
#
# Queue Metrics:
# - rsyslog_messages_total - Total messages processed
# - rsyslog_queue_size{queue} - Current queue depth
# - rsyslog_queue_enqueued_total{queue} - Messages enqueued
# - rsyslog_queue_dequeued_total{queue} - Messages dequeued
# - rsyslog_queue_full_total{queue} - Times queue was full
# - rsyslog_queue_max_size{queue} - Configured max queue size
# - rsyslog_queue_disk_usage_bytes{queue} - Disk-assisted queue usage
#
# Action Metrics:
# - rsyslog_action_processed_total{action} - Messages processed per action
# - rsyslog_action_failed_total{action} - Failed action attempts
# - rsyslog_action_suspended{action} - 1 if action is suspended
# - rsyslog_action_resumed_total{action} - Times action resumed
#
# Input Metrics:
# - rsyslog_input_received_total{input} - Messages received per input
#
# Process Metrics:
# - rsyslog_process_memory_bytes - RSS memory of rsyslog
# - rsyslog_process_open_fds - Open file descriptors
#
# Exporter Health:
# - rsyslog_exporter_duration_seconds - Script execution time
# - rsyslog_exporter_last_run_timestamp - Last run timestamp
# - rsyslog_exporter_success - 1 if collection succeeded
#
# Configuration (environment variables):
# NODE_DIR - Textfile collector directory (default: /var/lib/node_exporter)
# STATS_FILE - impstats JSON output file (default: /var/log/rsyslog-stats.log)
# COLLECTION_INTERVAL - Seconds between collections in daemon mode (default: 60)
# HTTP_PORT - HTTP server port (default: 9199)
# DEBUG - Set to any value to enable debug output
#
# impstats Configuration:
# Add to /etc/rsyslog.d/impstats.conf:
# module(load="impstats"
# interval="60"
# severity="7"
# log.syslog="off"
# log.file="/var/log/rsyslog-stats.log"
# format="json"
# )
#
################################################################################
set -euo pipefail
# ============================================================================
# CONFIGURATION VARIABLES
# ============================================================================
NODE_DIR="${NODE_DIR:-/var/lib/node_exporter}"
STATS_FILE="${STATS_FILE:-/var/log/rsyslog-stats.log}"
COLLECTION_INTERVAL="${COLLECTION_INTERVAL:-60}"
HTTP_PORT="${HTTP_PORT:-9199}"
DEBUG="${DEBUG:-}"
OUTPUT_FILE=""
HTTP_MODE=false
DAEMON_MODE=false
ONCE_MODE=false
TEMP_FILE=""
# ============================================================================
# HELPER FUNCTIONS
# ============================================================================
show_usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Export rsyslog impstats metrics as Prometheus metrics (v1.0).
MODES:
--once Run once and output to stdout (default)
--textfile Write to node_exporter textfile collector
--daemon Run continuously (use with --textfile)
--http Run HTTP server on port $HTTP_PORT
OPTIONS:
-p, --port HTTP port (default: $HTTP_PORT)
-o, --output Output file path
--check Verify impstats is configured and show setup instructions
-h, --help Show this help message
ENVIRONMENT VARIABLES:
NODE_DIR Textfile collector dir (default: /var/lib/node_exporter)
STATS_FILE impstats output file (default: /var/log/rsyslog-stats.log)
COLLECTION_INTERVAL Daemon interval in seconds (default: 60)
HTTP_PORT HTTP server port (default: 9199)
DEBUG Enable debug output (unset by default)
EXAMPLES:
$0 --once # One-shot output to stdout
$0 --textfile # Write to textfile collector (once)
$0 --daemon --textfile # Continuous textfile collector mode
$0 --http --port 9199 # Run HTTP server
$0 --check # Verify impstats configuration
$0 -o /tmp/rsyslog.prom # Write to custom file
METRICS:
- Queue depths, enqueue/dequeue counters, full events
- Per-action processed/failed/suspended counts
- Per-input received message counts
- rsyslog process memory and file descriptor usage
- Total messages processed
EOF
exit 0
}
parse_args() {
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help) show_usage ;;
--once) ONCE_MODE=true; shift ;;
--textfile) OUTPUT_FILE="$NODE_DIR/rsyslog_metrics.prom"; shift ;;
--daemon) DAEMON_MODE=true; shift ;;
--http) HTTP_MODE=true; shift ;;
--check) check_impstats_config; exit 0 ;;
-p|--port) HTTP_PORT="$2"; shift 2 ;;
-o|--output) OUTPUT_FILE="$2"; shift 2 ;;
*) echo "Unknown option: $1" >&2; exit 1 ;;
esac
done
}
debug_log() {
[ -n "$DEBUG" ] && echo "DEBUG: $*" >&2
}
# Verify that impstats is configured and the stats file exists
# Shows suggested configuration if not found
check_impstats_config() {
echo "Checking rsyslog impstats configuration..." >&2
echo "" >&2
# Check rsyslog is installed
if ! command -v rsyslogd >/dev/null 2>&1; then
echo "ERROR: rsyslogd not found in PATH" >&2
return 1
fi
local version
version=$(rsyslogd -v 2>/dev/null | head -1 || echo "unknown")
echo "rsyslog: $version" >&2
# Check if rsyslog is running
if pidof rsyslogd >/dev/null 2>&1; then
echo "Status: running (PID $(pidof rsyslogd))" >&2
else
echo "Status: NOT running" >&2
fi
# Check stats file
if [ -f "$STATS_FILE" ]; then
echo "Stats file: $STATS_FILE (exists, $(wc -l < "$STATS_FILE") lines)" >&2
echo "" >&2
# Verify it contains JSON
if tail -1 "$STATS_FILE" 2>/dev/null | jq . >/dev/null 2>&1; then
echo "OK: Stats file contains valid JSON" >&2
else
echo "WARNING: Stats file does not contain valid JSON" >&2
echo " Ensure impstats is configured with format=\"json\"" >&2
fi
else
echo "Stats file: $STATS_FILE (NOT FOUND)" >&2
echo "" >&2
echo "impstats does not appear to be configured." >&2
echo "Add the following to /etc/rsyslog.d/impstats.conf:" >&2
echo "" >&2
cat <<CONF
module(load="impstats"
interval="60"
severity="7"
log.syslog="off"
log.file="$STATS_FILE"
format="json"
)
CONF
echo "" >&2
echo "Then restart rsyslog: systemctl restart rsyslog" >&2
fi
# Check jq
if command -v jq >/dev/null 2>&1; then
echo "jq: installed ($(jq --version 2>/dev/null))" >&2
else
echo "jq: NOT FOUND (required)" >&2
echo " Install with: apt install jq OR yum install jq" >&2
fi
# Check node_exporter textfile dir
if [ -d "$NODE_DIR" ]; then
echo "Textfile dir: $NODE_DIR (exists)" >&2
else
echo "Textfile dir: $NODE_DIR (not found, create for --textfile mode)" >&2
fi
}
# Clean up temp files on exit
cleanup() {
[ -n "$TEMP_FILE" ] && rm -f "$TEMP_FILE"
TEMP_FILE=""
}
# ============================================================================
# DATA COLLECTION FUNCTIONS
# ============================================================================
# Check if rsyslog is running
# Returns: 0 if running, 1 if not
is_rsyslog_running() {
pidof rsyslogd >/dev/null 2>&1
}
# Get rsyslog version string
# Returns: version string (e.g., "8.2312.0")
get_rsyslog_version() {
local version_line
version_line=$(rsyslogd -v 2>/dev/null | head -1)
# Extract version number from lines like "rsyslogd 8.2312.0 (aka 2023.12)"
echo "$version_line" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1
}
# Extract the latest stats for each unique object name from the impstats file
# The stats file grows over time; we only want the most recent entry per object.
# Args: $1 - origin filter (e.g., "core.queue", "core.action")
# Returns: JSON lines, one per unique object name (latest occurrence wins)
get_latest_stats() {
local origin_filter="$1"
if [ ! -f "$STATS_FILE" ] || [ ! -r "$STATS_FILE" ]; then
debug_log "Stats file not readable: $STATS_FILE"
return
fi
# Read the last 500 lines (covers several collection cycles) and
# extract the last occurrence of each named object with the given origin
tail -500 "$STATS_FILE" 2>/dev/null | \
jq -c --arg origin "$origin_filter" \
'select(.origin == $origin)' 2>/dev/null | \
jq -s -c 'group_by(.name) | map(last) | .[]' 2>/dev/null
}
# Get the total messages processed from the main queue stats
# Returns: total enqueued count from "main Q"
get_total_messages() {
if [ ! -f "$STATS_FILE" ] || [ ! -r "$STATS_FILE" ]; then
echo "0"
return
fi
local val
val=$(tail -500 "$STATS_FILE" 2>/dev/null | \
jq -c 'select(.origin == "core.queue" and .name == "main Q")' 2>/dev/null | \
tail -1 | jq -r '.enqueued // 0' 2>/dev/null)
echo "${val:-0}"
}
# Get rsyslog process RSS memory in bytes from /proc
# Returns: RSS in bytes, or 0 if unavailable
get_process_memory() {
local pid
pid=$(pidof rsyslogd 2>/dev/null) || { echo "0"; return; }
# Use the first PID if multiple
pid=${pid%% *}
if [ -f "/proc/$pid/status" ]; then
local vmrss_kb
vmrss_kb=$(awk '/^VmRSS:/ {print $2}' "/proc/$pid/status" 2>/dev/null)
if [ -n "$vmrss_kb" ]; then
echo $((vmrss_kb * 1024))
return
fi
fi
echo "0"
}
# Get rsyslog open file descriptor count from /proc
# Returns: number of open fds, or 0 if unavailable
get_open_fds() {
local pid
pid=$(pidof rsyslogd 2>/dev/null) || { echo "0"; return; }
# Use the first PID if multiple
pid=${pid%% *}
if [ -d "/proc/$pid/fd" ]; then
ls "/proc/$pid/fd" 2>/dev/null | wc -l
return
fi
echo "0"
}
# ============================================================================
# METRIC GENERATION
# ============================================================================
# Generate all Prometheus metrics
# Returns: Prometheus text format metrics on stdout
generate_metrics() {
local script_start
script_start=$(date +%s.%N 2>/dev/null || date +%s)
local success=1
# Verify jq is available
if ! command -v jq >/dev/null 2>&1; then
cat <<EOF
# HELP rsyslog_up rsyslog process status
# TYPE rsyslog_up gauge
rsyslog_up 0
# HELP rsyslog_exporter_success Whether the last collection succeeded
# TYPE rsyslog_exporter_success gauge
rsyslog_exporter_success 0
EOF
echo "ERROR: jq is required but not installed" >&2
return
fi
# ========================================================================
# Core Status
# ========================================================================
local rsyslog_running=0
if is_rsyslog_running; then
rsyslog_running=1
fi
cat <<EOF
# HELP rsyslog_up rsyslog process status (1=running, 0=down)
# TYPE rsyslog_up gauge
rsyslog_up $rsyslog_running
EOF
local version
version=$(get_rsyslog_version)
if [ -n "$version" ]; then
cat <<EOF
# HELP rsyslog_info rsyslog version information
# TYPE rsyslog_info gauge
rsyslog_info{version="$version"} 1
EOF
fi
echo ""
# ========================================================================
# Total Messages
# ========================================================================
local total_messages
total_messages=$(get_total_messages)
cat <<EOF
# HELP rsyslog_messages_total Total messages processed (main queue enqueued)
# TYPE rsyslog_messages_total counter
rsyslog_messages_total $total_messages
EOF
echo ""
# ========================================================================
# Queue Metrics
# ========================================================================
local queue_stats
queue_stats=$(get_latest_stats "core.queue")
if [ -n "$queue_stats" ]; then
cat <<EOF
# HELP rsyslog_queue_size Current queue depth
# TYPE rsyslog_queue_size gauge
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname qsize
qname=$(echo "$line" | jq -r '.name // "unknown"')
qsize=$(echo "$line" | jq -r '.size // 0')
echo "rsyslog_queue_size{queue=\"$qname\"} $qsize"
done
echo ""
cat <<EOF
# HELP rsyslog_queue_enqueued_total Messages enqueued to queue
# TYPE rsyslog_queue_enqueued_total counter
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname val
qname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.enqueued // 0')
echo "rsyslog_queue_enqueued_total{queue=\"$qname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_queue_dequeued_total Messages dequeued from queue
# TYPE rsyslog_queue_dequeued_total counter
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname val
qname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.dequeued // 0')
echo "rsyslog_queue_dequeued_total{queue=\"$qname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_queue_full_total Times queue reached full capacity
# TYPE rsyslog_queue_full_total counter
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname val
qname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.full // 0')
echo "rsyslog_queue_full_total{queue=\"$qname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_queue_max_size Configured maximum queue size
# TYPE rsyslog_queue_max_size gauge
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname val
qname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.maxqsize // 0')
echo "rsyslog_queue_max_size{queue=\"$qname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_queue_disk_usage_bytes Disk-assisted queue disk usage in bytes
# TYPE rsyslog_queue_disk_usage_bytes gauge
EOF
echo "$queue_stats" | while IFS= read -r line; do
local qname val
qname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.["diskspace.used"] // 0')
echo "rsyslog_queue_disk_usage_bytes{queue=\"$qname\"} $val"
done
echo ""
fi
# ========================================================================
# Action Metrics
# ========================================================================
local action_stats
action_stats=$(get_latest_stats "core.action")
if [ -n "$action_stats" ]; then
cat <<EOF
# HELP rsyslog_action_processed_total Messages processed per action
# TYPE rsyslog_action_processed_total counter
EOF
echo "$action_stats" | while IFS= read -r line; do
local aname val
aname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.processed // 0')
echo "rsyslog_action_processed_total{action=\"$aname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_action_failed_total Failed action attempts
# TYPE rsyslog_action_failed_total counter
EOF
echo "$action_stats" | while IFS= read -r line; do
local aname val
aname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.failed // 0')
echo "rsyslog_action_failed_total{action=\"$aname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_action_suspended Action is currently suspended (1=yes, 0=no)
# TYPE rsyslog_action_suspended gauge
EOF
echo "$action_stats" | while IFS= read -r line; do
local aname val
aname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.suspended // 0')
echo "rsyslog_action_suspended{action=\"$aname\"} $val"
done
echo ""
cat <<EOF
# HELP rsyslog_action_resumed_total Times action was resumed from suspended state
# TYPE rsyslog_action_resumed_total counter
EOF
echo "$action_stats" | while IFS= read -r line; do
local aname val
aname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.resumed // 0')
echo "rsyslog_action_resumed_total{action=\"$aname\"} $val"
done
echo ""
fi
# ========================================================================
# Input Metrics
# ========================================================================
# Input modules use various origin values; collect them all
local input_stats=""
for input_origin in "imuxsock" "imjournal" "imtcp" "imudp" "imptcp" "imrelp"; do
local stats
stats=$(get_latest_stats "$input_origin")
if [ -n "$stats" ]; then
if [ -n "$input_stats" ]; then
input_stats="${input_stats}"$'\n'"${stats}"
else
input_stats="$stats"
fi
fi
done
if [ -n "$input_stats" ]; then
cat <<EOF
# HELP rsyslog_input_received_total Messages received per input
# TYPE rsyslog_input_received_total counter
EOF
echo "$input_stats" | while IFS= read -r line; do
local iname val
iname=$(echo "$line" | jq -r '.name // "unknown"')
val=$(echo "$line" | jq -r '.submitted // .received // 0')
echo "rsyslog_input_received_total{input=\"$iname\"} $val"
done
echo ""
fi
# ========================================================================
# Process Metrics
# ========================================================================
if is_rsyslog_running; then
local mem_bytes open_fds
mem_bytes=$(get_process_memory)
open_fds=$(get_open_fds)
cat <<EOF
# HELP rsyslog_process_memory_bytes RSS memory of rsyslog process in bytes
# TYPE rsyslog_process_memory_bytes gauge
rsyslog_process_memory_bytes $mem_bytes
# HELP rsyslog_process_open_fds Number of open file descriptors
# TYPE rsyslog_process_open_fds gauge
rsyslog_process_open_fds $open_fds
EOF
echo ""
fi
# ========================================================================
# Exporter Health
# ========================================================================
local script_end script_duration
script_end=$(date +%s.%N 2>/dev/null || date +%s)
script_duration=$(awk "BEGIN {printf \"%.3f\", $script_end - $script_start}" 2>/dev/null || echo "0")
cat <<EOF
# HELP rsyslog_exporter_duration_seconds Time to generate all metrics
# TYPE rsyslog_exporter_duration_seconds gauge
rsyslog_exporter_duration_seconds $script_duration
# HELP rsyslog_exporter_last_run_timestamp Unix timestamp of last successful run
# TYPE rsyslog_exporter_last_run_timestamp gauge
rsyslog_exporter_last_run_timestamp $(date +%s)
# HELP rsyslog_exporter_success Whether the last collection succeeded
# TYPE rsyslog_exporter_success gauge
rsyslog_exporter_success $success
EOF
echo ""
}
# ============================================================================
# HTTP SERVER MODE
# ============================================================================
# Run simple HTTP server using netcat
# Serves metrics on /metrics endpoint
run_http_server() {
echo "Starting rsyslog metrics exporter on port $HTTP_PORT..." >&2
if ! command -v nc >/dev/null 2>&1; then
echo "ERROR: netcat (nc) required for HTTP mode" >&2
exit 1
fi
# Infinite loop accepting HTTP requests
while true; do
{
read -r request
# Check if request is for /metrics endpoint
if [[ "$request" =~ ^GET\ /metrics ]]; then
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\n\r"
generate_metrics
else # Serve HTML landing page for other requests
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r"
cat <<EOF
<!DOCTYPE html>
<html>
<head><title>Rsyslog Metrics Exporter v1.0</title></head>
<body>
<h1>Rsyslog Metrics Exporter v1.0</h1>
<p><a href="/metrics">Metrics</a></p>
<h2>Metric Categories</h2>
<ul>
<li>Core Status: rsyslog up/down, version info</li>
<li>Queue Metrics: depth, enqueued/dequeued, full events, disk usage</li>
<li>Action Metrics: processed/failed/suspended per action</li>
<li>Input Metrics: messages received per input module</li>
<li>Process Metrics: memory usage, open file descriptors</li>
<li>Exporter Health: runtime, last run timestamp, success flag</li>
</ul>
</body>
</html>
EOF
fi
} | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null
done
}
# ============================================================================
# TEXTFILE WRITER (with atomic rename)
# ============================================================================
# Write metrics to textfile collector file atomically
# Uses temp file + rename to avoid partial reads
write_textfile() {
local output_dir
output_dir="$(dirname "$OUTPUT_FILE")"
mkdir -p "$output_dir"
# Create temp file in SAME directory for atomic rename (same filesystem)
TEMP_FILE=$(mktemp "${output_dir}/.rsyslog_metrics.XXXXXX")
# Generate metrics to temp file
if ! generate_metrics > "$TEMP_FILE" 2>/dev/null; then
rm -f "$TEMP_FILE"
TEMP_FILE=""
echo "ERROR: Failed to generate metrics" >&2
return 1
fi
# Validate: file must have content
local file_lines
file_lines=$(wc -l < "$TEMP_FILE" 2>/dev/null || echo 0)
if [ "$file_lines" -lt 5 ]; then
rm -f "$TEMP_FILE"
TEMP_FILE=""
echo "ERROR: Metrics file too small ($file_lines lines), keeping previous" >&2
return 1
fi
# Set permissions before move
chmod 644 "$TEMP_FILE"
# Atomic rename - no gap where file is missing
mv -f "$TEMP_FILE" "$OUTPUT_FILE"
TEMP_FILE=""
debug_log "Metrics written to $OUTPUT_FILE ($file_lines lines)"
return 0
}
# ============================================================================
# MAIN EXECUTION
# ============================================================================
# Main entry point - routes to appropriate output mode
main() {
parse_args "$@"
trap cleanup EXIT
if [ "$HTTP_MODE" = true ]; then
# Run HTTP server (blocks until killed)
run_http_server
elif [ -n "$OUTPUT_FILE" ] && [ "$DAEMON_MODE" = true ]; then
# Daemon mode: write textfile on a loop
echo "Starting rsyslog metrics exporter (daemon, interval=${COLLECTION_INTERVAL}s)..." >&2
echo "Writing to: $OUTPUT_FILE" >&2
while true; do
write_textfile || true
sleep "$COLLECTION_INTERVAL"
done
elif [ -n "$OUTPUT_FILE" ]; then
# Textfile collector mode: write once
write_textfile
echo "Metrics written to $OUTPUT_FILE" >&2
else
# Default: output to stdout
generate_metrics
fi
}
# Execute main function with all script arguments
main "$@"