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
+409
View File
@@ -0,0 +1,409 @@
#!/usr/bin/env bash
#########################################################################################
#### salt-roster-generator.sh — Generate Salt SSH roster files from external sources ####
#### Supports CSV files, Ansible inventory (INI format), and AWS EC2 instances ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.01 ####
#### ####
#### Usage: ####
#### ./salt-roster-generator.sh --csv hosts.csv -o roster ####
#### ./salt-roster-generator.sh --ansible-inventory /etc/ansible/hosts -o roster ####
#### ./salt-roster-generator.sh --ec2 --region us-east-1 -o roster ####
#### ./salt-roster-generator.sh --ec2 --tag "Environment=production" -o roster ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
VERSION="1.0"
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
INPUT_MODE=""
CSV_FILE=""
ANSIBLE_FILE=""
EC2_MODE=false
EC2_REGION=""
EC2_RUNNING_ONLY=true
declare -a EC2_TAGS=()
OUTPUT_FILE=""
DEFAULT_USER="root"
DEFAULT_PORT="22"
ENABLE_SUDO=false
SSH_PRIV=""
# ── Logging ───────────────────────────────────────────────────────────
err() { echo "[ERROR] $*" >&2; }
die() { err "$@"; exit 1; }
# ══════════════════════════════════════════════════════════════════════
# USAGE
# ══════════════════════════════════════════════════════════════════════
usage() {
cat <<EOF
${SCRIPT_NAME} — Generate Salt SSH roster files from external inventory sources
USAGE:
${SCRIPT_NAME} [OPTIONS]
INPUT SOURCES (pick one):
--csv FILE CSV file (columns: name,host,user,port)
--ansible-inventory FILE Ansible INI inventory file
--ec2 AWS EC2 instances (requires aws cli)
EC2 OPTIONS:
--region REGION AWS region (default: from aws cli config)
--tag KEY=VALUE Filter by tag (can repeat)
--running-only Only include running instances (default: true)
OUTPUT OPTIONS:
-o, --output FILE Output roster file (default: stdout)
SSH OPTIONS (applied to all hosts):
--user USER SSH user (default: root)
--sudo Enable sudo for all hosts
--priv KEY SSH private key path
--port PORT SSH port (default: 22)
OTHER:
--help, -h Show this help
--version Show version
EXAMPLES:
./${SCRIPT_NAME} --csv hosts.csv -o roster
./${SCRIPT_NAME} --ansible-inventory /etc/ansible/hosts -o roster
./${SCRIPT_NAME} --ec2 --region us-east-1 -o roster
./${SCRIPT_NAME} --ec2 --tag "Environment=production" --tag "Role=web" -o roster
EOF
}
# ══════════════════════════════════════════════════════════════════════
# ARGUMENT PARSING
# ══════════════════════════════════════════════════════════════════════
parse_args() {
[[ $# -eq 0 ]] && { usage; exit 1; }
while [[ $# -gt 0 ]]; do
case "$1" in
--csv)
INPUT_MODE="csv"
CSV_FILE="$2"
shift 2
;;
--ansible-inventory)
INPUT_MODE="ansible"
ANSIBLE_FILE="$2"
shift 2
;;
--ec2)
INPUT_MODE="ec2"
EC2_MODE=true
shift
;;
--region)
EC2_REGION="$2"
shift 2
;;
--tag)
EC2_TAGS+=("$2")
shift 2
;;
--running-only)
EC2_RUNNING_ONLY=true
shift
;;
-o|--output)
OUTPUT_FILE="$2"
shift 2
;;
--user)
DEFAULT_USER="$2"
shift 2
;;
--sudo)
ENABLE_SUDO=true
shift
;;
--priv)
SSH_PRIV="$2"
shift 2
;;
--port)
DEFAULT_PORT="$2"
shift 2
;;
--version)
echo "${SCRIPT_NAME} v${VERSION}"
exit 0
;;
--help|-h)
usage
exit 0
;;
-*)
die "Unknown option: $1 — run ${SCRIPT_NAME} --help"
;;
*)
die "Unexpected argument: $1 — run ${SCRIPT_NAME} --help"
;;
esac
done
}
# ══════════════════════════════════════════════════════════════════════
# VALIDATION
# ══════════════════════════════════════════════════════════════════════
validate() {
[[ -z "$INPUT_MODE" ]] && die "No input source specified. Use --csv, --ansible-inventory, or --ec2."
case "$INPUT_MODE" in
csv)
[[ -z "$CSV_FILE" ]] && die "--csv requires a file argument."
[[ ! -f "$CSV_FILE" ]] && die "CSV file not found: $CSV_FILE"
;;
ansible)
[[ -z "$ANSIBLE_FILE" ]] && die "--ansible-inventory requires a file argument."
[[ ! -f "$ANSIBLE_FILE" ]] && die "Ansible inventory file not found: $ANSIBLE_FILE"
;;
ec2)
if ! command -v aws &>/dev/null; then
die "aws cli not found. Install it to use --ec2 mode."
fi
;;
esac
}
# ══════════════════════════════════════════════════════════════════════
# ROSTER GENERATION
# ══════════════════════════════════════════════════════════════════════
generate_header() {
local source_label="$1"
echo "# Salt SSH Roster"
echo "# Generated by ${SCRIPT_NAME} v${VERSION}"
echo "# Source: ${source_label}"
echo "# Date: $(date '+%Y-%m-%d')"
echo ""
}
generate_entry() {
local name="$1"
local host="$2"
local user="${3:-$DEFAULT_USER}"
local port="${4:-$DEFAULT_PORT}"
local sudo="${5:-}"
local priv="${6:-$SSH_PRIV}"
[[ -z "$name" || -z "$host" ]] && return
echo "${name}:"
echo " host: ${host}"
echo " user: ${user}"
echo " port: ${port}"
if [[ "$ENABLE_SUDO" == true || "$sudo" == "true" || "$sudo" == "True" ]]; then
echo " sudo: True"
fi
if [[ -n "$priv" ]]; then
echo " priv: ${priv}"
fi
echo ""
}
# ── CSV Parser ────────────────────────────────────────────────────────
parse_csv() {
local file="$1"
local header_line
header_line=$(head -1 "$file")
# Map column positions from header
local -A col_map
local idx=0
IFS=',' read -ra cols <<< "$header_line"
for c in "${cols[@]}"; do
c=$(echo "$c" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')
col_map["$c"]=$idx
((idx++)) || true
done
# Require name and host columns
[[ -z "${col_map[name]:-}" ]] && die "CSV missing required column: name"
[[ -z "${col_map[host]:-}" ]] && die "CSV missing required column: host"
local name_idx="${col_map[name]}"
local host_idx="${col_map[host]}"
local user_idx="${col_map[user]:-}"
local port_idx="${col_map[port]:-}"
local sudo_idx="${col_map[sudo]:-}"
local priv_idx="${col_map[priv]:-}"
generate_header "$file"
local line_num=0
while IFS= read -r line; do
((line_num++)) || true
[[ $line_num -eq 1 ]] && continue # skip header
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
IFS=',' read -ra fields <<< "$line"
local name="${fields[$name_idx]:-}"
local host="${fields[$host_idx]:-}"
local user="${DEFAULT_USER}"
local port="${DEFAULT_PORT}"
local sudo=""
local priv=""
[[ -n "$user_idx" && -n "${fields[$user_idx]:-}" ]] && user="${fields[$user_idx]}"
[[ -n "$port_idx" && -n "${fields[$port_idx]:-}" ]] && port="${fields[$port_idx]}"
[[ -n "$sudo_idx" && -n "${fields[$sudo_idx]:-}" ]] && sudo="${fields[$sudo_idx]}"
[[ -n "$priv_idx" && -n "${fields[$priv_idx]:-}" ]] && priv="${fields[$priv_idx]}"
# Trim whitespace
name=$(echo "$name" | xargs)
host=$(echo "$host" | xargs)
user=$(echo "$user" | xargs)
port=$(echo "$port" | xargs)
generate_entry "$name" "$host" "$user" "$port" "$sudo" "$priv"
done < "$file"
}
# ── Ansible INI Inventory Parser ──────────────────────────────────────
parse_ansible_inventory() {
local file="$1"
generate_header "$file"
while IFS= read -r line; do
# Strip leading/trailing whitespace
line=$(echo "$line" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
# Skip empty lines, comments, group headers, group:vars, group:children
[[ -z "$line" ]] && continue
[[ "$line" =~ ^# ]] && continue
[[ "$line" =~ ^\[.*\] ]] && continue
# Parse host line: hostname key=value key=value ...
local hostname
hostname=$(echo "$line" | awk '{print $1}')
local host="$hostname"
local user="$DEFAULT_USER"
local port="$DEFAULT_PORT"
local priv=""
# Extract ansible variables
if echo "$line" | grep -q 'ansible_host='; then
host=$(echo "$line" | grep -oP 'ansible_host=\K[^\s]+')
fi
if echo "$line" | grep -q 'ansible_user='; then
user=$(echo "$line" | grep -oP 'ansible_user=\K[^\s]+')
fi
if echo "$line" | grep -q 'ansible_port='; then
port=$(echo "$line" | grep -oP 'ansible_port=\K[^\s]+')
fi
if echo "$line" | grep -q 'ansible_ssh_private_key_file='; then
priv=$(echo "$line" | grep -oP 'ansible_ssh_private_key_file=\K[^\s]+')
fi
generate_entry "$hostname" "$host" "$user" "$port" "" "$priv"
done < "$file"
}
# ── EC2 Parser ────────────────────────────────────────────────────────
parse_ec2() {
local -a aws_args=("ec2" "describe-instances")
# Region
if [[ -n "$EC2_REGION" ]]; then
aws_args+=("--region" "$EC2_REGION")
fi
# Build filters
local -a filters=()
if [[ "$EC2_RUNNING_ONLY" == true ]]; then
filters+=("Name=instance-state-name,Values=running")
fi
for tag in "${EC2_TAGS[@]}"; do
local key="${tag%%=*}"
local val="${tag#*=}"
filters+=("Name=tag:${key},Values=${val}")
done
if [[ ${#filters[@]} -gt 0 ]]; then
aws_args+=("--filters" "${filters[@]}")
fi
aws_args+=(
"--query" "Reservations[].Instances[].[Tags[?Key==\`Name\`].Value | [0], PrivateIpAddress, InstanceId]"
"--output" "text"
)
local ec2_output
ec2_output=$(aws "${aws_args[@]}" 2>/dev/null) || die "aws ec2 describe-instances failed. Check credentials and region."
[[ -z "$ec2_output" ]] && die "No EC2 instances found matching the specified filters."
local source_label="ec2"
[[ -n "$EC2_REGION" ]] && source_label="ec2 (${EC2_REGION})"
generate_header "$source_label"
while IFS=$'\t' read -r name ip instance_id; do
# Use instance ID if no Name tag
if [[ -z "$name" || "$name" == "None" ]]; then
name="$instance_id"
fi
[[ -z "$ip" || "$ip" == "None" ]] && continue
generate_entry "$name" "$ip" "$DEFAULT_USER" "$DEFAULT_PORT" "" ""
done <<< "$ec2_output"
}
# ══════════════════════════════════════════════════════════════════════
# OUTPUT
# ══════════════════════════════════════════════════════════════════════
write_output() {
local content="$1"
if [[ -n "$OUTPUT_FILE" ]]; then
echo "$content" > "$OUTPUT_FILE"
echo "Roster written to ${OUTPUT_FILE} ($(echo "$content" | grep -c ':$' || true) hosts)" >&2
else
echo "$content"
fi
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
main() {
parse_args "$@"
validate
local roster=""
case "$INPUT_MODE" in
csv) roster=$(parse_csv "$CSV_FILE") ;;
ansible) roster=$(parse_ansible_inventory "$ANSIBLE_FILE") ;;
ec2) roster=$(parse_ec2) ;;
esac
write_output "$roster"
}
main "$@"