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.
396 lines
15 KiB
Bash
396 lines
15 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
#########################################################################################
|
|
#### freeradius-exporter.sh — Prometheus metrics exporter for FreeRADIUS ####
|
|
#### Exports authentication, accounting, and proxy statistics from the ####
|
|
#### FreeRADIUS status server as Prometheus metrics ####
|
|
#### Requires: bash 4+, radclient ####
|
|
#### ####
|
|
#### Author: Phil Connor ####
|
|
#### Contact: contact@mylinux.work ####
|
|
#### License: MIT ####
|
|
#### Version 1.00 ####
|
|
#### ####
|
|
#### Usage: ####
|
|
#### ./freeradius-exporter.sh --http --port 9620 ####
|
|
#### ####
|
|
#### See --help for all options. ####
|
|
#########################################################################################
|
|
|
|
set -uo pipefail
|
|
|
|
# ============================================================================
|
|
# CONFIGURATION VARIABLES
|
|
# ============================================================================
|
|
|
|
RADIUS_HOST="${RADIUS_HOST:-localhost}"
|
|
RADIUS_STATUS_PORT="${RADIUS_STATUS_PORT:-18121}"
|
|
RADIUS_SECRET="${RADIUS_SECRET:-adminsecret}"
|
|
TEXTFILE_DIR="/var/lib/node_exporter"
|
|
OUTPUT_FILE=""
|
|
HTTP_MODE=false
|
|
HTTP_PORT=9620
|
|
|
|
EXPORTER_VERSION="1.00"
|
|
|
|
# ============================================================================
|
|
# HELPER FUNCTIONS
|
|
# ============================================================================
|
|
|
|
show_usage() {
|
|
cat <<EOF
|
|
Usage: $0 [OPTIONS]
|
|
|
|
Export FreeRADIUS statistics as Prometheus metrics via the status server.
|
|
|
|
MODES:
|
|
--textfile Write to node_exporter textfile collector
|
|
--http Run HTTP server on port $HTTP_PORT
|
|
|
|
OPTIONS:
|
|
-p, --port HTTP port (default: 9620)
|
|
-o, --output Output file path
|
|
-H, --host RADIUS host (default: localhost)
|
|
-s, --secret RADIUS status secret (default: adminsecret)
|
|
--status-port RADIUS status port (default: 18121)
|
|
|
|
ENVIRONMENT VARIABLES:
|
|
RADIUS_HOST RADIUS server host (default: localhost)
|
|
RADIUS_STATUS_PORT Status server port (default: 18121)
|
|
RADIUS_SECRET Status server secret (default: adminsecret)
|
|
|
|
EXAMPLES:
|
|
$0 --textfile # Write to textfile collector
|
|
$0 --http --port 9620 # Run HTTP server
|
|
$0 -o /tmp/freeradius.prom # Write to custom file
|
|
RADIUS_SECRET=mysecret $0 --http # Custom secret via env
|
|
|
|
NOTE:
|
|
FreeRADIUS must have the status server enabled. See:
|
|
/etc/freeradius/3.0/sites-enabled/status (Debian/Ubuntu)
|
|
/etc/raddb/sites-enabled/status (RHEL/CentOS)
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help) show_usage ;;
|
|
--textfile) OUTPUT_FILE="$TEXTFILE_DIR/freeradius.prom"; shift ;;
|
|
--http) HTTP_MODE=true; shift ;;
|
|
-p|--port) HTTP_PORT="$2"; shift 2 ;;
|
|
-o|--output) OUTPUT_FILE="$2"; shift 2 ;;
|
|
-H|--host) RADIUS_HOST="$2"; shift 2 ;;
|
|
-s|--secret) RADIUS_SECRET="$2"; shift 2 ;;
|
|
--status-port) RADIUS_STATUS_PORT="$2"; shift 2 ;;
|
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# Query the FreeRADIUS status server via radclient
|
|
# Returns: raw attribute-value pair output, or empty on failure
|
|
query_status_server() {
|
|
echo "Message-Authenticator = 0x00, FreeRADIUS-Statistics-Type = All" \
|
|
| radclient "${RADIUS_HOST}:${RADIUS_STATUS_PORT}" status "${RADIUS_SECRET}" 2>/dev/null
|
|
}
|
|
|
|
# Extract a numeric value from radclient output
|
|
# Args: $1 - attribute name, $2 - radclient output
|
|
# Returns: numeric value or 0 if not found
|
|
extract_value() {
|
|
local attr="$1"
|
|
local data="$2"
|
|
local val
|
|
val=$(echo "$data" | grep -F "$attr" | awk -F'= ' '{print $2}' | tr -d '[:space:]')
|
|
echo "${val:-0}"
|
|
}
|
|
|
|
# ============================================================================
|
|
# METRIC GENERATION
|
|
# ============================================================================
|
|
|
|
# Generate all Prometheus metrics
|
|
# Returns: Prometheus text format metrics on stdout
|
|
generate_metrics() {
|
|
local script_start
|
|
script_start=$(date +%s)
|
|
|
|
# Check radclient is available
|
|
if ! command -v radclient >/dev/null 2>&1; then
|
|
echo "ERROR: radclient command not found" >&2
|
|
cat <<EOF
|
|
# HELP freeradius_up FreeRADIUS status server reachable
|
|
# TYPE freeradius_up gauge
|
|
freeradius_up 0
|
|
EOF
|
|
return
|
|
fi
|
|
|
|
# Query the status server
|
|
local raw_output
|
|
raw_output=$(query_status_server)
|
|
|
|
if [[ -z "$raw_output" ]]; then
|
|
cat <<EOF
|
|
# HELP freeradius_up FreeRADIUS status server reachable
|
|
# TYPE freeradius_up gauge
|
|
freeradius_up 0
|
|
EOF
|
|
return
|
|
fi
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_up FreeRADIUS status server reachable
|
|
# TYPE freeradius_up gauge
|
|
freeradius_up 1
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
# ========================================================================
|
|
# ACCESS / AUTHENTICATION METRICS
|
|
# ========================================================================
|
|
|
|
local access_requests access_accepts access_rejects access_challenges
|
|
access_requests=$(extract_value "FreeRADIUS-Total-Access-Requests" "$raw_output")
|
|
access_accepts=$(extract_value "FreeRADIUS-Total-Access-Accepts" "$raw_output")
|
|
access_rejects=$(extract_value "FreeRADIUS-Total-Access-Rejects" "$raw_output")
|
|
access_challenges=$(extract_value "FreeRADIUS-Total-Access-Challenges" "$raw_output")
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_access_requests_total Total Access-Request packets received
|
|
# TYPE freeradius_access_requests_total counter
|
|
freeradius_access_requests_total $access_requests
|
|
|
|
# HELP freeradius_access_accepts_total Total Access-Accept packets sent
|
|
# TYPE freeradius_access_accepts_total counter
|
|
freeradius_access_accepts_total $access_accepts
|
|
|
|
# HELP freeradius_access_rejects_total Total Access-Reject packets sent
|
|
# TYPE freeradius_access_rejects_total counter
|
|
freeradius_access_rejects_total $access_rejects
|
|
|
|
# HELP freeradius_access_challenges_total Total Access-Challenge packets sent
|
|
# TYPE freeradius_access_challenges_total counter
|
|
freeradius_access_challenges_total $access_challenges
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
local auth_responses auth_duplicate auth_malformed auth_invalid auth_dropped auth_unknown
|
|
auth_responses=$(extract_value "FreeRADIUS-Total-Auth-Responses" "$raw_output")
|
|
auth_duplicate=$(extract_value "FreeRADIUS-Total-Auth-Duplicate-Requests" "$raw_output")
|
|
auth_malformed=$(extract_value "FreeRADIUS-Total-Auth-Malformed-Requests" "$raw_output")
|
|
auth_invalid=$(extract_value "FreeRADIUS-Total-Auth-Invalid-Requests" "$raw_output")
|
|
auth_dropped=$(extract_value "FreeRADIUS-Total-Auth-Dropped-Requests" "$raw_output")
|
|
auth_unknown=$(extract_value "FreeRADIUS-Total-Auth-Unknown-Types" "$raw_output")
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_auth_responses_total Total authentication responses sent
|
|
# TYPE freeradius_auth_responses_total counter
|
|
freeradius_auth_responses_total $auth_responses
|
|
|
|
# HELP freeradius_auth_duplicate_requests_total Total duplicate authentication requests
|
|
# TYPE freeradius_auth_duplicate_requests_total counter
|
|
freeradius_auth_duplicate_requests_total $auth_duplicate
|
|
|
|
# HELP freeradius_auth_malformed_requests_total Total malformed authentication requests
|
|
# TYPE freeradius_auth_malformed_requests_total counter
|
|
freeradius_auth_malformed_requests_total $auth_malformed
|
|
|
|
# HELP freeradius_auth_invalid_requests_total Total invalid authentication requests
|
|
# TYPE freeradius_auth_invalid_requests_total counter
|
|
freeradius_auth_invalid_requests_total $auth_invalid
|
|
|
|
# HELP freeradius_auth_dropped_requests_total Total dropped authentication requests
|
|
# TYPE freeradius_auth_dropped_requests_total counter
|
|
freeradius_auth_dropped_requests_total $auth_dropped
|
|
|
|
# HELP freeradius_auth_unknown_types_total Total unknown authentication packet types
|
|
# TYPE freeradius_auth_unknown_types_total counter
|
|
freeradius_auth_unknown_types_total $auth_unknown
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
# ========================================================================
|
|
# ACCOUNTING METRICS
|
|
# ========================================================================
|
|
|
|
local acct_requests acct_responses acct_duplicate acct_malformed acct_invalid acct_dropped
|
|
acct_requests=$(extract_value "FreeRADIUS-Total-Accounting-Requests" "$raw_output")
|
|
acct_responses=$(extract_value "FreeRADIUS-Total-Accounting-Responses" "$raw_output")
|
|
acct_duplicate=$(extract_value "FreeRADIUS-Total-Acct-Duplicate-Requests" "$raw_output")
|
|
acct_malformed=$(extract_value "FreeRADIUS-Total-Acct-Malformed-Requests" "$raw_output")
|
|
acct_invalid=$(extract_value "FreeRADIUS-Total-Acct-Invalid-Requests" "$raw_output")
|
|
acct_dropped=$(extract_value "FreeRADIUS-Total-Acct-Dropped-Requests" "$raw_output")
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_accounting_requests_total Total Accounting-Request packets received
|
|
# TYPE freeradius_accounting_requests_total counter
|
|
freeradius_accounting_requests_total $acct_requests
|
|
|
|
# HELP freeradius_accounting_responses_total Total Accounting-Response packets sent
|
|
# TYPE freeradius_accounting_responses_total counter
|
|
freeradius_accounting_responses_total $acct_responses
|
|
|
|
# HELP freeradius_accounting_duplicate_requests_total Total duplicate accounting requests
|
|
# TYPE freeradius_accounting_duplicate_requests_total counter
|
|
freeradius_accounting_duplicate_requests_total $acct_duplicate
|
|
|
|
# HELP freeradius_accounting_malformed_requests_total Total malformed accounting requests
|
|
# TYPE freeradius_accounting_malformed_requests_total counter
|
|
freeradius_accounting_malformed_requests_total $acct_malformed
|
|
|
|
# HELP freeradius_accounting_invalid_requests_total Total invalid accounting requests
|
|
# TYPE freeradius_accounting_invalid_requests_total counter
|
|
freeradius_accounting_invalid_requests_total $acct_invalid
|
|
|
|
# HELP freeradius_accounting_dropped_requests_total Total dropped accounting requests
|
|
# TYPE freeradius_accounting_dropped_requests_total counter
|
|
freeradius_accounting_dropped_requests_total $acct_dropped
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
# ========================================================================
|
|
# PROXY METRICS
|
|
# ========================================================================
|
|
|
|
local proxy_access_requests proxy_access_accepts proxy_access_rejects
|
|
local proxy_acct_requests proxy_acct_responses
|
|
proxy_access_requests=$(extract_value "FreeRADIUS-Total-Proxy-Access-Requests" "$raw_output")
|
|
proxy_access_accepts=$(extract_value "FreeRADIUS-Total-Proxy-Access-Accepts" "$raw_output")
|
|
proxy_access_rejects=$(extract_value "FreeRADIUS-Total-Proxy-Access-Rejects" "$raw_output")
|
|
proxy_acct_requests=$(extract_value "FreeRADIUS-Total-Proxy-Accounting-Requests" "$raw_output")
|
|
proxy_acct_responses=$(extract_value "FreeRADIUS-Total-Proxy-Accounting-Responses" "$raw_output")
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_proxy_access_requests_total Total proxied Access-Request packets
|
|
# TYPE freeradius_proxy_access_requests_total counter
|
|
freeradius_proxy_access_requests_total $proxy_access_requests
|
|
|
|
# HELP freeradius_proxy_access_accepts_total Total proxied Access-Accept packets
|
|
# TYPE freeradius_proxy_access_accepts_total counter
|
|
freeradius_proxy_access_accepts_total $proxy_access_accepts
|
|
|
|
# HELP freeradius_proxy_access_rejects_total Total proxied Access-Reject packets
|
|
# TYPE freeradius_proxy_access_rejects_total counter
|
|
freeradius_proxy_access_rejects_total $proxy_access_rejects
|
|
|
|
# HELP freeradius_proxy_accounting_requests_total Total proxied Accounting-Request packets
|
|
# TYPE freeradius_proxy_accounting_requests_total counter
|
|
freeradius_proxy_accounting_requests_total $proxy_acct_requests
|
|
|
|
# HELP freeradius_proxy_accounting_responses_total Total proxied Accounting-Response packets
|
|
# TYPE freeradius_proxy_accounting_responses_total counter
|
|
freeradius_proxy_accounting_responses_total $proxy_acct_responses
|
|
EOF
|
|
|
|
echo ""
|
|
|
|
# ========================================================================
|
|
# EXPORTER RUNTIME
|
|
# ========================================================================
|
|
|
|
local script_end script_duration
|
|
script_end=$(date +%s)
|
|
script_duration=$((script_end - script_start))
|
|
|
|
cat <<EOF
|
|
# HELP freeradius_exporter_duration_seconds Time to generate all metrics
|
|
# TYPE freeradius_exporter_duration_seconds gauge
|
|
freeradius_exporter_duration_seconds $script_duration
|
|
|
|
# HELP freeradius_exporter_last_run_timestamp Unix timestamp of last successful run
|
|
# TYPE freeradius_exporter_last_run_timestamp gauge
|
|
freeradius_exporter_last_run_timestamp $script_end
|
|
EOF
|
|
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================================
|
|
# HTTP SERVER MODE
|
|
# ============================================================================
|
|
|
|
# Run simple HTTP server using netcat
|
|
# Serves metrics on /metrics endpoint
|
|
run_http_server() {
|
|
echo "Starting FreeRADIUS 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
|
|
|
|
while true; do
|
|
{
|
|
read -r request
|
|
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
|
|
echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r"
|
|
cat <<EOF
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head><title>FreeRADIUS Exporter v${EXPORTER_VERSION}</title></head>
|
|
<body>
|
|
<h1>FreeRADIUS Prometheus Exporter v${EXPORTER_VERSION}</h1>
|
|
<p><a href="/metrics">Metrics</a></p>
|
|
<p>Authentication, accounting, and proxy statistics from the FreeRADIUS status server.</p>
|
|
</body>
|
|
</html>
|
|
EOF
|
|
fi
|
|
} | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# MAIN EXECUTION
|
|
# ============================================================================
|
|
|
|
main() {
|
|
parse_args "$@"
|
|
|
|
if [[ "$HTTP_MODE" = true ]]; then
|
|
run_http_server
|
|
elif [[ -n "$OUTPUT_FILE" ]]; then
|
|
local output_dir
|
|
output_dir="$(dirname "$OUTPUT_FILE")"
|
|
mkdir -p "$output_dir"
|
|
|
|
local temp_file
|
|
temp_file=$(mktemp "${output_dir}/.freeradius_metrics.XXXXXX")
|
|
|
|
if ! generate_metrics > "$temp_file" 2>/dev/null; then
|
|
rm -f "$temp_file"
|
|
echo "ERROR: Failed to generate metrics" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local file_lines
|
|
file_lines=$(wc -l < "$temp_file" 2>/dev/null || echo 0)
|
|
|
|
if [[ "$file_lines" -lt 5 ]]; then
|
|
rm -f "$temp_file"
|
|
echo "ERROR: Metrics file too small ($file_lines lines), keeping previous" >&2
|
|
exit 1
|
|
fi
|
|
|
|
chmod 644 "$temp_file"
|
|
mv -f "$temp_file" "$OUTPUT_FILE"
|
|
|
|
echo "Metrics written to $OUTPUT_FILE ($file_lines lines)" >&2
|
|
else
|
|
generate_metrics
|
|
fi
|
|
}
|
|
|
|
main "$@"
|