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
+523
View File
@@ -0,0 +1,523 @@
#!/bin/bash
################################################################################
# Script Name: password-expiry-exporter.sh
# Version: 1.0
# Description: Prometheus textfile collector exporter for password expiry
# Monitors password expiry for local and AD (domain) accounts
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
# Date: 2026-03-10
#
# Prerequisites:
# - node_exporter with textfile collector enabled
# - /var/lib/node_exporter directory exists
# - Run as root (to read /etc/shadow and query all users)
# - For AD accounts: SSSD/realm joined or ldapsearch + Kerberos ticket
#
# Usage:
# # Export metrics for all non-system local users
# sudo ./password-expiry-exporter.sh
#
# # Export for specific users
# USER_LIST="jsmith,admin,svc-backup" sudo ./password-expiry-exporter.sh
#
# # Include domain (AD) users
# INCLUDE_DOMAIN=1 sudo ./password-expiry-exporter.sh
#
# # Dry run (output to stdout)
# sudo ./password-expiry-exporter.sh --dry-run
#
# # Debug mode
# DEBUG=1 sudo ./password-expiry-exporter.sh
#
# Metrics Exported:
# - linux_password_expiry_days{user,type} - Days until password expires (-1 = expired, -2 = never)
# - linux_password_expired{user,type} - Whether the password has expired (1/0)
# - linux_password_never_expires{user,type} - Whether the password is set to never expire (1/0)
# - linux_password_last_change_days{user,type} - Days since the password was last changed
# - linux_password_expiry_warning_days{user,type} - Warning period in days before expiry
#
# Configuration:
# Environment: USER_LIST (comma-separated), INCLUDE_DOMAIN, MIN_UID
# Config file: /etc/password-expiry-exporter.conf (one user per line)
# Textfile directory: /var/lib/node_exporter
#
################################################################################
set -o pipefail
# ============================================================================
# CONFIGURATION
# ============================================================================
readonly VERSION="1.0"
readonly SCRIPT_NAME="${0##*/}"
readonly TEXTFILE_DIR="${TEXTFILE_DIR:-/var/lib/node_exporter}"
readonly OUTPUT_FILE="${TEXTFILE_DIR}/password_expiry.prom"
readonly CONFIG_FILE="${CONFIG_FILE:-/etc/password-expiry-exporter.conf}"
readonly TMP_FILE="${OUTPUT_FILE}.$$"
readonly MIN_UID="${MIN_UID:-1000}"
readonly INCLUDE_DOMAIN="${INCLUDE_DOMAIN:-0}"
# Runtime flags
DRY_RUN=false
DEBUG=${DEBUG:-}
# ============================================================================
# HELPER FUNCTIONS
# ============================================================================
debug_echo() {
if [[ -n "$DEBUG" ]]; then
echo "[DEBUG] $*" >&2
fi
}
log_error() {
echo "[ERROR] $*" >&2
}
cleanup() {
rm -f "$TMP_FILE"
}
trap cleanup EXIT
show_help() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]
Prometheus textfile collector exporter for password expiry.
Monitors password expiry for local and AD (domain) accounts.
OPTIONS:
--dry-run Output metrics to stdout instead of writing to file
--debug Enable debug output
--help Show this help message
--version Show version
CONFIGURATION:
Users can be configured in three ways (in priority order):
1. Environment variable (comma-separated):
USER_LIST="jsmith,admin" $SCRIPT_NAME
2. Config file (one user per line):
/etc/password-expiry-exporter.conf
3. Auto-discovery (default):
All local users with UID >= $MIN_UID
ENVIRONMENT VARIABLES:
USER_LIST Comma-separated list of users to monitor
CONFIG_FILE Path to config file (default: /etc/password-expiry-exporter.conf)
TEXTFILE_DIR Textfile collector directory (default: /var/lib/node_exporter)
MIN_UID Minimum UID for auto-discovery (default: 1000)
INCLUDE_DOMAIN Set to 1 to include domain/AD users in auto-discovery
DEBUG Enable debug output when set to any value
EXAMPLES:
sudo $SCRIPT_NAME
sudo $SCRIPT_NAME --dry-run
USER_LIST="admin,deploy" sudo $SCRIPT_NAME
INCLUDE_DOMAIN=1 sudo $SCRIPT_NAME
DEBUG=1 sudo $SCRIPT_NAME --dry-run
EOF
exit 0
}
show_version() {
echo "$SCRIPT_NAME version $VERSION"
exit 0
}
# ============================================================================
# ACCOUNT DETECTION
# ============================================================================
is_domain_joined() {
if command -v realm &>/dev/null && realm list 2>/dev/null | grep -q "configured:"; then
return 0
elif [[ -f /etc/sssd/sssd.conf ]] && systemctl is-active sssd &>/dev/null; then
return 0
elif grep -qE "^passwd:.*sss|winbind" /etc/nsswitch.conf 2>/dev/null; then
return 0
fi
return 1
}
strip_domain() {
local user="$1"
user="${user##*\\}"
user="${user%%@*}"
echo "$user"
}
get_account_type() {
local user="$1"
local uid
uid=$(id -u "$user" 2>/dev/null) || return 1
if grep -q "^${user}:" /etc/passwd 2>/dev/null; then
echo "local"
elif (( uid >= 100000 )); then
echo "domain"
else
echo "local"
fi
}
# ============================================================================
# USER DISCOVERY
# ============================================================================
load_users() {
local users=()
if [[ -n "${USER_LIST:-}" ]]; then
debug_echo "Loading users from USER_LIST environment variable"
local raw_users=()
IFS=',' read -ra raw_users <<< "$USER_LIST"
for u in "${raw_users[@]}"; do
users+=("$(strip_domain "$u")")
done
elif [[ -f "$CONFIG_FILE" ]]; then
debug_echo "Loading users from config file: $CONFIG_FILE"
while IFS= read -r line; do
line="${line%%#*}"
line="${line// /}"
if [[ -n "$line" ]]; then
users+=("$(strip_domain "$line")")
fi
done < "$CONFIG_FILE"
else
debug_echo "Auto-discovering local users with UID >= $MIN_UID"
while IFS=: read -r username _ uid _; do
if (( uid >= MIN_UID )) && (( uid < 65534 )); then
users+=("$username")
fi
done < /etc/passwd
if [[ "$INCLUDE_DOMAIN" == "1" ]] && is_domain_joined; then
debug_echo "Including domain users from getent"
while IFS=: read -r username _ uid _; do
if (( uid >= 100000 )); then
users+=("$username")
fi
done < <(getent passwd 2>/dev/null)
fi
# Fall back to root if no regular users found (e.g. containers, minimal installs)
if [[ ${#users[@]} -eq 0 ]]; then
debug_echo "No users with UID >= $MIN_UID found, falling back to root"
users+=("root")
fi
fi
if [[ ${#users[@]} -eq 0 ]]; then
log_error "No users found to monitor"
exit 1
fi
debug_echo "Monitoring ${#users[@]} users: ${users[*]}"
printf '%s\n' "${users[@]}"
}
# ============================================================================
# PASSWORD EXPIRY QUERIES
# ============================================================================
get_local_expiry_info() {
local user="$1"
if ! chage_output=$(chage -l "$user" 2>/dev/null); then
debug_echo "chage failed for $user"
return 1
fi
local expiry_str last_change_str warn_str
expiry_str=$(echo "$chage_output" | grep "Password expires" | cut -d: -f2- | xargs)
last_change_str=$(echo "$chage_output" | grep "Last password change" | cut -d: -f2- | xargs)
warn_str=$(echo "$chage_output" | grep "Number of days of warning" | cut -d: -f2- | xargs)
local now_epoch
now_epoch=$(date +%s)
# Password expiry
local expiry_days=-2 # default: never
local expired=0
local never_expires=1
if [[ -n "$expiry_str" && "$expiry_str" != "never" ]]; then
local expiry_epoch
expiry_epoch=$(date -d "$expiry_str" +%s 2>/dev/null) || true
if [[ -n "$expiry_epoch" ]]; then
expiry_days=$(( (expiry_epoch - now_epoch) / 86400 ))
never_expires=0
if (( expiry_days < 0 )); then
expired=1
expiry_days=-1
fi
fi
fi
# Last password change
local last_change_days=-1
if [[ -n "$last_change_str" && "$last_change_str" != "never" ]]; then
local last_change_epoch
last_change_epoch=$(date -d "$last_change_str" +%s 2>/dev/null) || true
if [[ -n "$last_change_epoch" ]]; then
last_change_days=$(( (now_epoch - last_change_epoch) / 86400 ))
fi
fi
# Warning days
local warning_days=0
if [[ -n "$warn_str" && "$warn_str" =~ ^[0-9]+$ ]]; then
warning_days="$warn_str"
fi
echo "${expiry_days}|${expired}|${never_expires}|${last_change_days}|${warning_days}"
}
get_domain_expiry_info() {
local user="$1"
# Try chage first (works via SSSD PAM integration)
if chage_output=$(chage -l "$user" 2>/dev/null); then
local expiry_str
expiry_str=$(echo "$chage_output" | grep "Password expires" | cut -d: -f2- | xargs)
if [[ -n "$expiry_str" && "$expiry_str" != "never" ]]; then
get_local_expiry_info "$user"
return
fi
fi
# Fall back to ldapsearch
if ! command -v ldapsearch &>/dev/null; then
debug_echo "ldapsearch not available for domain user $user"
return 1
fi
local domain dc_host base_dn
domain=$(hostname -d 2>/dev/null || dnsdomainname 2>/dev/null || echo "")
if [[ -z "$domain" ]]; then
debug_echo "Cannot determine domain for ldapsearch"
return 1
fi
dc_host=$(host -t SRV "_ldap._tcp.${domain}" 2>/dev/null | head -1 | awk '{print $NF}' | sed 's/\.$//')
base_dn=$(echo "$domain" | sed 's/\./,DC=/g; s/^/DC=/')
if [[ -z "$dc_host" ]]; then
debug_echo "Cannot find domain controller for $domain"
return 1
fi
local result
result=$(ldapsearch -LLL -H "ldap://${dc_host}" -Y GSSAPI -Q \
-b "$base_dn" \
"(sAMAccountName=${user})" \
msDS-UserPasswordExpiryTimeComputed pwdLastSet 2>/dev/null)
local expiry_ticks last_set_ticks
expiry_ticks=$(echo "$result" | grep "msDS-UserPasswordExpiryTimeComputed:" | awk '{print $2}')
last_set_ticks=$(echo "$result" | grep "pwdLastSet:" | awk '{print $2}')
local now_epoch
now_epoch=$(date +%s)
# Password expiry
local expiry_days=-2
local expired=0
local never_expires=1
if [[ -n "$expiry_ticks" && "$expiry_ticks" != "0" && "$expiry_ticks" != "9223372036854775807" ]]; then
local expiry_epoch
expiry_epoch=$(echo "($expiry_ticks - 116444736000000000) / 10000000" | bc 2>/dev/null)
if [[ -n "$expiry_epoch" ]]; then
expiry_days=$(( (expiry_epoch - now_epoch) / 86400 ))
never_expires=0
if (( expiry_days < 0 )); then
expired=1
expiry_days=-1
fi
fi
fi
# Last password change
local last_change_days=-1
if [[ -n "$last_set_ticks" && "$last_set_ticks" != "0" ]]; then
local last_set_epoch
last_set_epoch=$(echo "($last_set_ticks - 116444736000000000) / 10000000" | bc 2>/dev/null)
if [[ -n "$last_set_epoch" ]]; then
last_change_days=$(( (now_epoch - last_set_epoch) / 86400 ))
fi
fi
echo "${expiry_days}|${expired}|${never_expires}|${last_change_days}|0"
}
# ============================================================================
# METRICS COLLECTION
# ============================================================================
collect_metrics() {
local users=()
while IFS= read -r u; do
users+=("$u")
done < <(load_users)
local output=""
local has_data=false
# Collect data for all users first
declare -A user_data
declare -A user_types
for user in "${users[@]}"; do
local account_type
account_type=$(get_account_type "$user") || continue
user_types["$user"]="$account_type"
local info
if [[ "$account_type" == "domain" ]]; then
info=$(get_domain_expiry_info "$user") || continue
else
info=$(get_local_expiry_info "$user") || continue
fi
user_data["$user"]="$info"
has_data=true
debug_echo "User $user ($account_type): $info"
done
if [[ "$has_data" == "false" ]]; then
log_error "Could not collect expiry data for any users"
exit 1
fi
# --- Expiry days ---
output+="# HELP linux_password_expiry_days Days until password expires (-1=expired, -2=never)\n"
output+="# TYPE linux_password_expiry_days gauge\n"
for user in "${users[@]}"; do
[[ -z "${user_data[$user]:-}" ]] && continue
local type="${user_types[$user]}"
local days
days=$(echo "${user_data[$user]}" | cut -d'|' -f1)
output+="linux_password_expiry_days{user=\"${user}\",type=\"${type}\"} ${days}\n"
done
# --- Expired flag ---
output+="# HELP linux_password_expired Whether the password has expired (1=yes, 0=no)\n"
output+="# TYPE linux_password_expired gauge\n"
for user in "${users[@]}"; do
[[ -z "${user_data[$user]:-}" ]] && continue
local type="${user_types[$user]}"
local expired
expired=$(echo "${user_data[$user]}" | cut -d'|' -f2)
output+="linux_password_expired{user=\"${user}\",type=\"${type}\"} ${expired}\n"
done
# --- Never expires flag ---
output+="# HELP linux_password_never_expires Whether the password is set to never expire (1=yes, 0=no)\n"
output+="# TYPE linux_password_never_expires gauge\n"
for user in "${users[@]}"; do
[[ -z "${user_data[$user]:-}" ]] && continue
local type="${user_types[$user]}"
local never
never=$(echo "${user_data[$user]}" | cut -d'|' -f3)
output+="linux_password_never_expires{user=\"${user}\",type=\"${type}\"} ${never}\n"
done
# --- Last change days ---
output+="# HELP linux_password_last_change_days Days since the password was last changed\n"
output+="# TYPE linux_password_last_change_days gauge\n"
for user in "${users[@]}"; do
[[ -z "${user_data[$user]:-}" ]] && continue
local type="${user_types[$user]}"
local last_change
last_change=$(echo "${user_data[$user]}" | cut -d'|' -f4)
output+="linux_password_last_change_days{user=\"${user}\",type=\"${type}\"} ${last_change}\n"
done
# --- Warning days ---
output+="# HELP linux_password_expiry_warning_days Warning period in days before password expiry\n"
output+="# TYPE linux_password_expiry_warning_days gauge\n"
for user in "${users[@]}"; do
[[ -z "${user_data[$user]:-}" ]] && continue
local type="${user_types[$user]}"
local warn
warn=$(echo "${user_data[$user]}" | cut -d'|' -f5)
output+="linux_password_expiry_warning_days{user=\"${user}\",type=\"${type}\"} ${warn}\n"
done
printf '%b' "$output"
}
# ============================================================================
# OUTPUT
# ============================================================================
write_metrics() {
local metrics
metrics=$(collect_metrics)
if [[ "$DRY_RUN" == "true" ]]; then
echo "$metrics"
return
fi
if [[ ! -d "$TEXTFILE_DIR" ]]; then
log_error "Textfile collector directory does not exist: $TEXTFILE_DIR"
exit 1
fi
echo "$metrics" > "$TMP_FILE"
mv "$TMP_FILE" "$OUTPUT_FILE"
debug_echo "Metrics written to $OUTPUT_FILE"
}
# ============================================================================
# MAIN
# ============================================================================
main() {
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run)
DRY_RUN=true
shift
;;
--debug)
DEBUG=1
shift
;;
--help|-h)
show_help
;;
--version|-v)
show_version
;;
*)
log_error "Unknown option: $1"
echo "Use --help for usage information" >&2
exit 1
;;
esac
done
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root to read password expiry for all users"
exit 1
fi
write_metrics
}
main "$@"