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:
@@ -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 "$@"
|
||||
Reference in New Issue
Block a user