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
+636
View File
@@ -0,0 +1,636 @@
#!/usr/bin/env bash
#########################################################################################
#### jenkins-backup.sh — Backup and restore Jenkins configuration and jobs ####
#### Supports JENKINS_HOME tar, job XML export, and credential backup ####
#### Requires: bash 4+, tar, curl ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 1.01 ####
#### ####
#### Usage: ####
#### export JENKINS_HOME="/var/lib/jenkins" ####
#### ./jenkins-backup.sh --backup ####
#### ####
#### See --help for all options. ####
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
JENKINS_HOME="${JENKINS_HOME:-/var/lib/jenkins}"
BACKUP_DIR="${BACKUP_DIR:-/var/backups/jenkins}"
RETENTION_COUNT="${RETENTION_COUNT:-7}"
JENKINS_URL="${JENKINS_URL:-}"
JENKINS_USER="${JENKINS_USER:-}"
JENKINS_TOKEN="${JENKINS_TOKEN:-}"
BACKUP_TYPE="${BACKUP_TYPE:-full}"
EXCLUDE_PLUGINS="${EXCLUDE_PLUGINS:-false}"
CURL_TIMEOUT="${CURL_TIMEOUT:-30}"
CURL_INSECURE="${CURL_INSECURE:-false}"
VERBOSE="${VERBOSE:-false}"
COLOR="${COLOR:-auto}"
# ── State ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
readonly SCRIPT_NAME
RUN_MODE="backup"
RESTORE_DIR=""
DRY_RUN="false"
CONFIG_ONLY="false"
API_BACKUP="false"
TMPDIR_WORK=""
START_TIME=""
# ── Colors ────────────────────────────────────────────────────────────
setup_colors() {
if [[ "$COLOR" == "never" ]]; then
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
return
fi
if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
RESET='\033[0m'
else
RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
fi
}
# ── Logging ───────────────────────────────────────────────────────────
log() { echo -e "${BLUE}[INFO]${RESET} $*"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
err() { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[DEBUG]${RESET} $*"; fi; }
# ── Cleanup ───────────────────────────────────────────────────────────
cleanup() {
if [[ -n "$TMPDIR_WORK" && -d "$TMPDIR_WORK" ]]; then
verbose "Cleaning up temp directory: $TMPDIR_WORK"
rm -rf "$TMPDIR_WORK"
fi
}
trap cleanup EXIT
# ── Helpers ───────────────────────────────────────────────────────────
human_size() {
local bytes="$1"
if [[ "$bytes" -ge 1073741824 ]]; then
echo "$(( bytes / 1073741824 ))G"
elif [[ "$bytes" -ge 1048576 ]]; then
echo "$(( bytes / 1048576 ))M"
elif [[ "$bytes" -ge 1024 ]]; then
echo "$(( bytes / 1024 ))K"
else
echo "${bytes}B"
fi
}
elapsed_time() {
local end_time
end_time=$(date +%s)
local duration=$(( end_time - START_TIME ))
local mins=$(( duration / 60 ))
local secs=$(( duration % 60 ))
if [[ "$mins" -gt 0 ]]; then
echo "${mins}m ${secs}s"
else
echo "${secs}s"
fi
}
jenkins_api() {
local endpoint="$1"
local output="${2:-}"
local url="${JENKINS_URL%/}${endpoint}"
local curl_opts=(-s -S --max-time "$CURL_TIMEOUT" -f)
[[ "$CURL_INSECURE" == "true" ]] && curl_opts+=(-k)
if [[ -n "$JENKINS_USER" && -n "$JENKINS_TOKEN" ]]; then
curl_opts+=(-u "${JENKINS_USER}:${JENKINS_TOKEN}")
fi
if [[ -n "$output" ]]; then
curl "${curl_opts[@]}" "$url" -o "$output"
else
curl "${curl_opts[@]}" "$url"
fi
}
# ── Show Help ─────────────────────────────────────────────────────────
show_help() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
Backup and restore Jenkins configuration, jobs, credentials, and plugins.
Supports full JENKINS_HOME archives, config-only snapshots, and remote
API exports with automatic retention.
OPTIONS:
--backup Run a backup (default)
--config-only Backup configuration files only (no workspaces/builds)
--api-backup Export job configs via Jenkins API (remote, no filesystem)
--restore DIR Restore from the specified backup directory or archive
--dry-run With --restore, show what would be restored without changes
--list List available backups
--verify DIR Verify backup integrity via checksums
--help, -h Show this help message
ENVIRONMENT VARIABLES:
JENKINS_HOME Jenkins home directory (default: /var/lib/jenkins)
BACKUP_DIR Root backup directory (default: /var/backups/jenkins)
RETENTION_COUNT Number of backups to retain (default: 7)
JENKINS_URL Jenkins base URL (required for --api-backup)
JENKINS_USER Jenkins username (required for --api-backup)
JENKINS_TOKEN Jenkins API token (required for --api-backup)
BACKUP_TYPE Backup type: full, config, api (default: full)
EXCLUDE_PLUGINS Skip plugin JPI files in config backup (default: false)
CURL_TIMEOUT HTTP timeout in seconds (default: 30)
CURL_INSECURE Allow self-signed certs (default: false)
VERBOSE Enable verbose output (default: false)
COLOR Color output: auto, always, never (default: auto)
EXAMPLES:
# Full JENKINS_HOME backup
JENKINS_HOME=/var/lib/jenkins ./jenkins-backup.sh --backup
# Config-only backup (XML configs + plugins, no build data)
./jenkins-backup.sh --config-only
# Remote API backup (no filesystem access needed)
JENKINS_URL=http://localhost:8080 JENKINS_USER=admin JENKINS_TOKEN=xxxx \\
./jenkins-backup.sh --api-backup
# Restore from a backup
./jenkins-backup.sh --restore /var/backups/jenkins/20260404-120000-full
# Dry-run restore
./jenkins-backup.sh --restore /var/backups/jenkins/20260404-120000-full --dry-run
# List available backups
./jenkins-backup.sh --list
# Verify a backup
./jenkins-backup.sh --verify /var/backups/jenkins/20260404-120000-full
EOF
exit 0
}
# ── Parse Arguments ───────────────────────────────────────────────────
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
--backup) RUN_MODE="backup"; shift ;;
--config-only) RUN_MODE="backup"; CONFIG_ONLY="true"; BACKUP_TYPE="config"; shift ;;
--api-backup) RUN_MODE="backup"; API_BACKUP="true"; BACKUP_TYPE="api"; shift ;;
--restore)
RUN_MODE="restore"
if [[ $# -lt 2 ]]; then
err "--restore requires a directory argument"
exit 1
fi
RESTORE_DIR="$2"; shift 2
;;
--dry-run) DRY_RUN="true"; shift ;;
--list) RUN_MODE="list"; shift ;;
--verify)
RUN_MODE="verify"
if [[ $# -lt 2 ]]; then
err "--verify requires a directory argument"
exit 1
fi
RESTORE_DIR="$2"; shift 2
;;
--help|-h) show_help ;;
*)
err "Unknown option: $1"
echo "Run '$SCRIPT_NAME --help' for usage." >&2
exit 1
;;
esac
done
}
# ── Validation ────────────────────────────────────────────────────────
validate_backup() {
if [[ "$API_BACKUP" == "true" ]]; then
if [[ -z "$JENKINS_URL" ]]; then
err "JENKINS_URL is required for --api-backup"
exit 1
fi
if [[ -z "$JENKINS_USER" || -z "$JENKINS_TOKEN" ]]; then
err "JENKINS_USER and JENKINS_TOKEN are required for --api-backup"
exit 1
fi
if ! command -v curl &>/dev/null; then
err "curl is required but not installed"
exit 1
fi
else
if [[ ! -d "$JENKINS_HOME" ]]; then
err "JENKINS_HOME does not exist: $JENKINS_HOME"
exit 1
fi
for cmd in tar sha256sum; do
if ! command -v "$cmd" &>/dev/null; then
err "$cmd is required but not installed"
exit 1
fi
done
fi
mkdir -p "$BACKUP_DIR"
}
# ── Full Backup ───────────────────────────────────────────────────────
do_full_backup() {
local timestamp
timestamp=$(date +%Y%m%d-%H%M%S)
local dest="$BACKUP_DIR/${timestamp}-full"
local archive="$dest/jenkins-full-${timestamp}.tar.gz"
mkdir -p "$dest"
log "Starting full backup of $JENKINS_HOME"
verbose "Destination: $dest"
local tar_excludes=(
--exclude='workspace'
--exclude='*/builds/*/archive'
--exclude='*.log'
)
tar czf "$archive" \
"${tar_excludes[@]}" \
-C "$(dirname "$JENKINS_HOME")" \
"$(basename "$JENKINS_HOME")" 2>/dev/null || {
err "Failed to create tar archive"
rm -rf "$dest"
exit 1
}
sha256sum "$archive" > "$dest/checksums.sha256"
echo "full" > "$dest/.backup-type"
local size
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
log "Full backup complete: $(human_size "$size") in $(elapsed_time)"
log "Archive: $archive"
}
# ── Config-Only Backup ────────────────────────────────────────────────
do_config_backup() {
local timestamp
timestamp=$(date +%Y%m%d-%H%M%S)
local dest="$BACKUP_DIR/${timestamp}-config"
local archive="$dest/jenkins-config-${timestamp}.tar.gz"
mkdir -p "$dest"
log "Starting config-only backup of $JENKINS_HOME"
verbose "Destination: $dest"
TMPDIR_WORK=$(mktemp -d)
local staging="$TMPDIR_WORK/jenkins-config"
mkdir -p "$staging"
# Core config files
for f in config.xml credentials.xml hudson.model.UpdateCenter.xml jenkins.model.JenkinsLocationConfiguration.xml; do
if [[ -f "$JENKINS_HOME/$f" ]]; then
cp "$JENKINS_HOME/$f" "$staging/"
verbose " Copied $f"
fi
done
# Directories: users, secrets, nodes
for d in users secrets nodes; do
if [[ -d "$JENKINS_HOME/$d" ]]; then
cp -r "$JENKINS_HOME/$d" "$staging/"
verbose " Copied $d/"
fi
done
# Job configs (config.xml only)
if [[ -d "$JENKINS_HOME/jobs" ]]; then
find "$JENKINS_HOME/jobs" -name config.xml -type f | while IFS= read -r jobxml; do
local relpath
relpath="${jobxml#"$JENKINS_HOME"/}"
local target_dir
target_dir="$staging/$(dirname "$relpath")"
mkdir -p "$target_dir"
cp "$jobxml" "$target_dir/"
done
verbose " Copied job configs"
fi
# Plugin JPI files
if [[ "$EXCLUDE_PLUGINS" != "true" && -d "$JENKINS_HOME/plugins" ]]; then
mkdir -p "$staging/plugins"
find "$JENKINS_HOME/plugins" -maxdepth 1 -name '*.jpi' -exec cp {} "$staging/plugins/" \;
local plugin_count
plugin_count=$(find "$staging/plugins" -name '*.jpi' 2>/dev/null | wc -l)
verbose " Copied $plugin_count plugin files"
fi
tar czf "$archive" -C "$TMPDIR_WORK" "jenkins-config" 2>/dev/null || {
err "Failed to create config archive"
rm -rf "$dest"
exit 1
}
sha256sum "$archive" > "$dest/checksums.sha256"
echo "config" > "$dest/.backup-type"
local size
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
log "Config backup complete: $(human_size "$size") in $(elapsed_time)"
log "Archive: $archive"
}
# ── API Backup ────────────────────────────────────────────────────────
do_api_backup() {
local timestamp
timestamp=$(date +%Y%m%d-%H%M%S)
local dest="$BACKUP_DIR/${timestamp}-api"
mkdir -p "$dest/jobs" "$dest/plugins"
log "Starting API backup from $JENKINS_URL"
verbose "Destination: $dest"
# Get job list — parse JSON without jq
local job_list_json
job_list_json=$(jenkins_api "/api/json?tree=jobs[name]") || {
err "Failed to fetch job list from Jenkins API"
rm -rf "$dest"
exit 1
}
# Extract job names from JSON using grep/sed
local job_names
job_names=$(echo "$job_list_json" | { grep -o '"name" *: *"[^"]*"' || true; } | sed 's/.*: *"//;s/"$//')
local job_count=0
local job_fail=0
if [[ -n "$job_names" ]]; then
while IFS= read -r job_name; do
[[ -z "$job_name" ]] && continue
local encoded_name
encoded_name="${job_name// /%20}"
if jenkins_api "/job/${encoded_name}/config.xml" "$dest/jobs/${job_name}.xml" 2>/dev/null; then
verbose " Exported job: $job_name"
job_count=$((job_count + 1))
else
warn "Failed to export job: $job_name"
job_fail=$((job_fail + 1))
fi
done <<< "$job_names"
fi
log "Exported $job_count jobs ($job_fail failed)"
# Plugin list
local plugin_json
if plugin_json=$(jenkins_api "/pluginManager/api/json?depth=1&tree=plugins[shortName,version,enabled,active]"); then
echo "$plugin_json" > "$dest/plugins/plugin-list.json"
local plugin_count
plugin_count=$(echo "$plugin_json" | { grep -o '"shortName"' || true; } | wc -l)
log "Exported plugin list: $plugin_count plugins"
else
warn "Failed to fetch plugin list"
fi
# Create checksum file for all exported files
if command -v sha256sum &>/dev/null; then
find "$dest" -type f ! -name 'checksums.sha256' -exec sha256sum {} + > "$dest/checksums.sha256" 2>/dev/null || true
fi
echo "api" > "$dest/.backup-type"
local size
size=$(du -sb "$dest" 2>/dev/null | cut -f1)
size="${size:-0}"
log "API backup complete: $(human_size "$size") in $(elapsed_time)"
log "Backup directory: $dest"
}
# ── Backup Entry Point ───────────────────────────────────────────────
do_backup() {
START_TIME=$(date +%s)
validate_backup
if [[ "$API_BACKUP" == "true" ]]; then
do_api_backup
elif [[ "$CONFIG_ONLY" == "true" ]]; then
do_config_backup
else
do_full_backup
fi
prune_backups
}
# ── Prune ─────────────────────────────────────────────────────────────
prune_backups() {
log "Pruning old backups (retaining $RETENTION_COUNT)..."
local backup_count
backup_count=$(find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | wc -l)
if [[ "$backup_count" -le "$RETENTION_COUNT" ]]; then
log " No pruning needed ($backup_count backups present)"
return 0
fi
local remove_count=$((backup_count - RETENTION_COUNT))
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d | sort | head -n "$remove_count" | while IFS= read -r dir; do
log " Removing $(basename "$dir")"
rm -rf "$dir"
done
log " Pruned $remove_count old backups"
}
# ── Restore ───────────────────────────────────────────────────────────
do_restore() {
if [[ -z "$RESTORE_DIR" ]]; then
err "--restore requires a directory argument"
exit 1
fi
if [[ ! -d "$RESTORE_DIR" ]]; then
err "Restore directory does not exist: $RESTORE_DIR"
exit 1
fi
# Find the tar archive in the backup directory
local archive
archive=$(find "$RESTORE_DIR" -maxdepth 1 -name '*.tar.gz' -type f | head -1)
if [[ -z "$archive" ]]; then
err "No tar archive found in: $RESTORE_DIR"
exit 1
fi
log "Restore source: $archive"
log "Restore target: $JENKINS_HOME"
if [[ "$DRY_RUN" == "true" ]]; then
log "${BOLD}Dry run — listing archive contents:${RESET}"
tar tzf "$archive" | head -50
local total
total=$(tar tzf "$archive" | wc -l)
log " ($total files total)"
log "Dry run complete — no changes made"
return 0
fi
# Verify checksum if available
if [[ -f "$RESTORE_DIR/checksums.sha256" ]]; then
log "Verifying backup integrity..."
if (cd "$RESTORE_DIR" && sha256sum -c checksums.sha256 --quiet 2>/dev/null); then
log " ${GREEN}Checksums verified${RESET}"
else
warn "Checksum verification failed — proceed with caution"
fi
fi
if [[ ! -d "$JENKINS_HOME" ]]; then
mkdir -p "$JENKINS_HOME"
fi
log "Restoring archive..."
tar xzf "$archive" -C "$(dirname "$JENKINS_HOME")" || {
err "Failed to extract archive"
exit 1
}
warn "Jenkins must be restarted for changes to take effect"
warn " sudo systemctl restart jenkins"
log "Restore complete"
}
# ── List ──────────────────────────────────────────────────────────────
do_list() {
if [[ ! -d "$BACKUP_DIR" ]]; then
log "No backups found (directory does not exist: $BACKUP_DIR)"
return 0
fi
local count=0
local format=" %-28s %-8s %s\n"
printf "\n"
# shellcheck disable=SC2059
printf "$format" "BACKUP" "TYPE" "SIZE"
# shellcheck disable=SC2059
printf "$format" "----------------------------" "--------" "--------"
for dir in "$BACKUP_DIR"/*/; do
[[ -d "$dir" ]] || continue
count=$((count + 1))
local name
name=$(basename "$dir")
local btype="unknown"
if [[ -f "$dir/.backup-type" ]]; then
btype=$(cat "$dir/.backup-type")
fi
local size
size=$(du -sh "$dir" 2>/dev/null | cut -f1)
size="${size:-?}"
# shellcheck disable=SC2059
printf "$format" "$name" "$btype" "$size"
done
printf "\n"
if [[ "$count" -eq 0 ]]; then
log "No backups found in $BACKUP_DIR"
else
log "$count backup(s) in $BACKUP_DIR"
fi
}
# ── Verify ────────────────────────────────────────────────────────────
do_verify() {
if [[ -z "$RESTORE_DIR" ]]; then
err "--verify requires a directory argument"
exit 1
fi
if [[ ! -d "$RESTORE_DIR" ]]; then
err "Backup directory does not exist: $RESTORE_DIR"
exit 1
fi
log "Verifying backup: $RESTORE_DIR"
# Check backup type
local btype="unknown"
if [[ -f "$RESTORE_DIR/.backup-type" ]]; then
btype=$(cat "$RESTORE_DIR/.backup-type")
fi
log " Backup type: $btype"
# Check for checksums
if [[ -f "$RESTORE_DIR/checksums.sha256" ]]; then
log " Verifying checksums..."
if (cd "$RESTORE_DIR" && sha256sum -c checksums.sha256 2>/dev/null); then
log " ${GREEN}All checksums passed${RESET}"
else
err "Checksum verification FAILED"
exit 1
fi
else
warn "No checksum file found — cannot verify integrity"
fi
# Archive contents summary
local archive
archive=$(find "$RESTORE_DIR" -maxdepth 1 -name '*.tar.gz' -type f | head -1)
if [[ -n "$archive" ]]; then
local size
size=$(stat -c%s "$archive" 2>/dev/null || stat -f%z "$archive" 2>/dev/null || echo 0)
log " Archive: $(basename "$archive") ($(human_size "$size"))"
local file_count
file_count=$(tar tzf "$archive" | wc -l)
log " Files in archive: $file_count"
fi
# API backup — list exported files
if [[ "$btype" == "api" ]]; then
local job_count
job_count=$(find "$RESTORE_DIR/jobs" -name '*.xml' 2>/dev/null | wc -l)
log " Exported jobs: $job_count"
if [[ -f "$RESTORE_DIR/plugins/plugin-list.json" ]]; then
local plugin_count
plugin_count=$({ grep -o '"shortName"' "$RESTORE_DIR/plugins/plugin-list.json" || true; } | wc -l)
log " Plugins recorded: $plugin_count"
fi
fi
log "Verification complete"
}
# ── Main ──────────────────────────────────────────────────────────────
main() {
setup_colors
parse_args "$@"
case "$RUN_MODE" in
backup) do_backup ;;
restore) do_restore ;;
list) do_list ;;
verify) do_verify ;;
*) err "Unknown mode: $RUN_MODE"; exit 1 ;;
esac
}
main "$@"