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
+486
View File
@@ -0,0 +1,486 @@
#!/usr/bin/env bash
#########################################################################################
#### nginx-security-hardening.sh — Add rate limiting, bot honeypot, security ####
#### headers, query string filtering, and hotlink protection to nginx ####
#### ####
#### Supports standalone nginx (default) and HestiaCP/VestaCP ####
#### ####
#### Author: Phil Connor ####
#### Contact: contact@mylinux.work ####
#### License: MIT ####
#### Version 2.04 ####
#### ####
#### Usage: ####
#### sudo ./nginx-security-hardening.sh --domain example.com ####
#### sudo ./nginx-security-hardening.sh --domain example.com --dry-run ####
#### sudo ./nginx-security-hardening.sh --domain example.com --user admin ####
#### sudo ./nginx-security-hardening.sh --domain example.com --skip honeypot ####
#### ####
#### See --help for all options. ####
#########################################################################################
# v2.03 changes:
# - Fixed: grep in pipeline crashes under set -euo pipefail when no matches found. Added || true guard
#########################################################################################
set -euo pipefail
# ── Defaults ──────────────────────────────────────────────────────────
DOMAIN=""
USER=""
DRY_RUN=false
SKIP=()
RATE="5r/m"
BURST=10
BANTIME=86400
TRAP_PATH="/trap"
DOWNLOAD_PATH="/downloads/"
BACKUP_EXT=".bak.$(date +%Y%m%d%H%M%S)"
# ── Colors ────────────────────────────────────────────────────────────
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
# ── Usage ─────────────────────────────────────────────────────────────
SCRIPT_NAME="$(basename "$0")"
usage() {
cat <<EOF
${SCRIPT_NAME} — Nginx security hardening (standalone or HestiaCP/VestaCP)
USAGE:
sudo ${SCRIPT_NAME} --domain DOMAIN [OPTIONS]
OPTIONS:
--domain DOMAIN Domain name (required)
--user USER HestiaCP/VestaCP username (omit for standalone nginx)
--skip FEATURES Comma-separated: rate-limit, honeypot, headers, query-filter, hotlink-protection
--rate RATE Rate limit (default: 5r/m)
--burst NUM Rate limit burst (default: 10)
--bantime SECS Honeypot ban duration (default: 86400)
--trap-path PATH Honeypot path (default: /trap)
--download-path PATH Rate-limited path (default: /downloads/)
--dry-run Preview changes without writing files
--help Show this help
EXAMPLES:
${SCRIPT_NAME} --domain example.com
${SCRIPT_NAME} --domain example.com --skip honeypot
${SCRIPT_NAME} --domain example.com --user admin
${SCRIPT_NAME} --domain example.com --dry-run
EOF
exit 1
}
# ── Logging ───────────────────────────────────────────────────────────
log_info() { echo -e "${GREEN}[INFO]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERROR]${NC} $*"; }
log_dry() { echo -e "${CYAN}[DRY-RUN]${NC} $*"; }
# ── Helpers ───────────────────────────────────────────────────────────
should_skip() {
local feature="$1"
for skip in "${SKIP[@]+"${SKIP[@]}"}"; do
[[ "$skip" == "$feature" ]] && return 0
done
return 1
}
backup_file() {
local file="$1"
[[ -f "$file" ]] || return 0
local backup_dir="$(dirname "$file")/.backups"
local backup_dest="${backup_dir}/$(basename "$file")${BACKUP_EXT}"
if $DRY_RUN; then
log_dry "Would back up $file${backup_dest}"
else
mkdir -p "$backup_dir"
cp "$file" "$backup_dest"
log_info "Backed up $file"
fi
}
write_file() {
local file="$1"
local content="$2"
local desc="$3"
if $DRY_RUN; then
log_dry "Would create $file"
echo -e "${CYAN}--- $desc ---${NC}"
echo "$content"
echo ""
else
mkdir -p "$(dirname "$file")"
backup_file "$file"
echo "$content" > "$file"
log_info "Created $file"
fi
}
is_hestia_mode() { [[ -n "$USER" ]]; }
detect_backend() {
local conf_dir="/home/${USER}/conf/web/${DOMAIN}"
local http_backend=""
local https_backend=""
if [[ -f "${conf_dir}/nginx.conf" ]]; then
http_backend=$({ grep -m1 'proxy_pass' "${conf_dir}/nginx.conf" || true; } | awk '{print $2}' | tr -d ';')
fi
if [[ -f "${conf_dir}/nginx.ssl.conf" ]]; then
https_backend=$({ grep -m1 'proxy_pass' "${conf_dir}/nginx.ssl.conf" || true; } | awk '{print $2}' | tr -d ';')
fi
if [[ -z "$http_backend" || -z "$https_backend" ]]; then
log_error "Could not detect backend proxy_pass from ${conf_dir}/nginx.conf"
log_error "Check that the domain exists in HestiaCP/VestaCP"
exit 1
fi
echo "${http_backend}|${https_backend}"
}
# ── Parse Arguments ───────────────────────────────────────────────────
while [[ $# -gt 0 ]]; do
case "$1" in
--domain) DOMAIN="$2"; shift 2 ;;
--user) USER="$2"; shift 2 ;;
--dry-run) DRY_RUN=true; shift ;;
--skip) IFS=',' read -ra SKIP <<< "$2"; shift 2 ;;
--rate) RATE="$2"; shift 2 ;;
--burst) BURST="$2"; shift 2 ;;
--bantime) BANTIME="$2"; shift 2 ;;
--trap-path) TRAP_PATH="$2"; shift 2 ;;
--download-path) DOWNLOAD_PATH="$2"; shift 2 ;;
-h|--help) usage ;;
*) log_error "Unknown option: $1"; usage ;;
esac
done
# ── Validate ──────────────────────────────────────────────────────────
if [[ -z "$DOMAIN" ]]; then
log_error "Missing required --domain"
usage
fi
if [[ "$EUID" -ne 0 && "$DRY_RUN" == false ]]; then
log_error "Must run as root (or use --dry-run to preview)"
exit 1
fi
# ── Detect Mode ───────────────────────────────────────────────────────
if is_hestia_mode; then
CONF_DIR="/home/${USER}/conf/web/${DOMAIN}"
if [[ ! -d "$CONF_DIR" ]]; then
log_error "Directory not found: ${CONF_DIR}"
log_error "Check your --user and --domain values"
exit 1
fi
log_info "Mode: HestiaCP/VestaCP (user: ${USER}, domain: ${DOMAIN})"
BACKENDS=$(detect_backend)
HTTP_BACKEND=$(echo "$BACKENDS" | cut -d'|' -f1)
HTTPS_BACKEND=$(echo "$BACKENDS" | cut -d'|' -f2)
log_info "Detected HTTP backend: ${HTTP_BACKEND}"
log_info "Detected HTTPS backend: ${HTTPS_BACKEND}"
HTTPS_IS_SSL=false
if [[ "$HTTPS_BACKEND" == https://* ]]; then
HTTPS_IS_SSL=true
fi
else
CONF_DIR="/etc/nginx"
log_info "Mode: standalone nginx"
fi
echo ""
log_info "Domain: ${DOMAIN}"
log_info "Features to install:"
should_skip rate-limit || log_info " ✓ Rate limiting (${RATE}, burst ${BURST}) on ${DOWNLOAD_PATH}"
should_skip honeypot || log_info " ✓ Bot honeypot (${TRAP_PATH}, ban ${BANTIME}s)"
should_skip headers || log_info " ✓ Security headers"
should_skip query-filter || log_info " ✓ Query string filtering"
should_skip hotlink-protection || log_info " ✓ Hotlink protection (images)"
for skip in "${SKIP[@]+"${SKIP[@]}"}"; do
log_warn " ✗ Skipping: ${skip}"
done
echo ""
# =====================================================================
# 1. RATE LIMITING
# =====================================================================
if ! should_skip rate-limit; then
log_info "Setting up rate limiting..."
RATE_ZONE_CONF="# Rate limit zone for download paths
# Installed by nginx-security-hardening.sh
limit_req_zone \$binary_remote_addr zone=downloads_${DOMAIN//./_}:10m rate=${RATE};"
write_file "/etc/nginx/conf.d/rate-limit-${DOMAIN}.conf" "$RATE_ZONE_CONF" "Rate limit zone (http context)"
if is_hestia_mode; then
RATE_HTTP="# Rate limiting on ${DOWNLOAD_PATH}
# Installed by nginx-security-hardening.sh
location ${DOWNLOAD_PATH} {
limit_req zone=downloads_${DOMAIN//./_} burst=${BURST} nodelay;
limit_req_status 429;
proxy_pass ${HTTP_BACKEND};
}"
write_file "${CONF_DIR}/nginx.conf_rate_limit" "$RATE_HTTP" "Rate limit (HTTP)"
RATE_HTTPS="# Rate limiting on ${DOWNLOAD_PATH}
# Installed by nginx-security-hardening.sh
location ${DOWNLOAD_PATH} {
limit_req zone=downloads_${DOMAIN//./_} burst=${BURST} nodelay;
limit_req_status 429;"
if $HTTPS_IS_SSL; then
RATE_HTTPS+="
proxy_ssl_server_name on;
proxy_ssl_name \$host;"
fi
RATE_HTTPS+="
proxy_pass ${HTTPS_BACKEND};
}"
write_file "${CONF_DIR}/nginx.ssl.conf_rate_limit" "$RATE_HTTPS" "Rate limit (HTTPS)"
else
RATE_SNIPPET="# Rate limiting on ${DOWNLOAD_PATH}
# Installed by nginx-security-hardening.sh
# Include inside your server block: include snippets/rate-limit-${DOMAIN}.conf;
location ${DOWNLOAD_PATH} {
limit_req zone=downloads_${DOMAIN//./_} burst=${BURST} nodelay;
limit_req_status 429;
}"
write_file "/etc/nginx/snippets/rate-limit-${DOMAIN}.conf" "$RATE_SNIPPET" "Rate limit snippet"
log_info "Add to your server block: include snippets/rate-limit-${DOMAIN}.conf;"
fi
fi
# =====================================================================
# 2. BOT HONEYPOT
# =====================================================================
if ! should_skip honeypot; then
log_info "Setting up bot honeypot..."
HONEYPOT_LOG="/var/log/nginx/${DOMAIN}.honeypot.log"
if is_hestia_mode; then
HONEYPOT_LOG="/var/log/nginx/domains/${DOMAIN}.honeypot.log"
TRAP_HTTP="# Bot honeypot — any request to ${TRAP_PATH} gets logged and blocked
# Installed by nginx-security-hardening.sh
location ${TRAP_PATH} {
access_log ${HONEYPOT_LOG} combined;
return 403;
}"
write_file "${CONF_DIR}/nginx.conf_honeypot" "$TRAP_HTTP" "Honeypot (HTTP)"
TRAP_HTTPS="# Bot honeypot — any request to ${TRAP_PATH} gets logged and blocked
# Installed by nginx-security-hardening.sh
location ${TRAP_PATH} {
access_log ${HONEYPOT_LOG} combined;
return 403;
}"
write_file "${CONF_DIR}/nginx.ssl.conf_honeypot" "$TRAP_HTTPS" "Honeypot (HTTPS)"
else
TRAP_SNIPPET="# Bot honeypot — any request to ${TRAP_PATH} gets logged and blocked
# Installed by nginx-security-hardening.sh
# Include inside your server block: include snippets/honeypot-${DOMAIN}.conf;
location ${TRAP_PATH} {
access_log ${HONEYPOT_LOG} combined;
return 403;
}"
write_file "/etc/nginx/snippets/honeypot-${DOMAIN}.conf" "$TRAP_SNIPPET" "Honeypot snippet"
log_info "Add to your server block: include snippets/honeypot-${DOMAIN}.conf;"
fi
# Fail2ban filter
HONEYPOT_FILTER="# Bot honeypot filter
# Installed by nginx-security-hardening.sh
[Definition]
failregex = ^<HOST> .* \"(GET|POST|HEAD) ${TRAP_PATH}
ignoreregex ="
write_file "/etc/fail2ban/filter.d/nginx-honeypot.conf" "$HONEYPOT_FILTER" "Fail2ban honeypot filter"
# Fail2ban jail
HONEYPOT_JAIL="# Bot honeypot jail — ban on first hit
# Installed by nginx-security-hardening.sh
[nginx-honeypot]
enabled = true
port = http,https
filter = nginx-honeypot
logpath = ${HONEYPOT_LOG}
maxretry = 1
bantime = ${BANTIME}
findtime = ${BANTIME}"
write_file "/etc/fail2ban/jail.d/nginx-honeypot.conf" "$HONEYPOT_JAIL" "Fail2ban honeypot jail"
# Create the log file so fail2ban doesn't complain
if ! $DRY_RUN; then
touch "$HONEYPOT_LOG"
chmod 644 "$HONEYPOT_LOG"
fi
log_warn "Add a hidden link to your site template to attract bots:"
echo -e " ${CYAN}<a href=\"${TRAP_PATH}/\" style=\"display:none\" aria-hidden=\"true\" tabindex=\"-1\">admin</a>${NC}"
echo ""
fi
# =====================================================================
# 3. SECURITY HEADERS
# =====================================================================
if ! should_skip headers; then
log_info "Setting up security headers..."
HEADERS_CONF="# Security headers
# Installed by nginx-security-hardening.sh
# Prevent MIME type sniffing
add_header X-Content-Type-Options \"nosniff\" always;
# Prevent clickjacking (SAMEORIGIN allows AWStats/panel frames)
add_header X-Frame-Options \"SAMEORIGIN\" always;
# Control referrer information
add_header Referrer-Policy \"strict-origin-when-cross-origin\" always;
# Restrict browser features
add_header Permissions-Policy \"camera=(), microphone=(), geolocation=(), payment=()\" always;
# Prevent XSS in older browsers
add_header X-XSS-Protection \"1; mode=block\" always;"
if is_hestia_mode; then
write_file "${CONF_DIR}/nginx.conf_security_headers" "$HEADERS_CONF" "Security headers (HTTP)"
write_file "${CONF_DIR}/nginx.ssl.conf_security_headers" "$HEADERS_CONF" "Security headers (HTTPS)"
else
write_file "/etc/nginx/snippets/security-headers-${DOMAIN}.conf" "$HEADERS_CONF" "Security headers snippet"
log_info "Add to your server block: include snippets/security-headers-${DOMAIN}.conf;"
fi
fi
# =====================================================================
# 4. QUERY STRING FILTERING
# =====================================================================
if ! should_skip query-filter; then
log_info "Setting up query string filtering..."
QUERY_CONF="# Block malicious query strings (SQL injection, XSS, path traversal)
# Installed by nginx-security-hardening.sh
# SQL injection patterns
if (\$query_string ~* \"(union|select\s.*from|insert\s.*into|delete\s.*from|drop\s+table|update\s.*set|concat\s*\(|exec\s*\()\") {
return 403;
}
# XSS patterns
if (\$query_string ~* \"(<script|javascript:|on(?:error|load|click|mouseover)=)\") {
return 403;
}
# Path traversal
if (\$query_string ~* \"(\\.\\./|\\.\\.\\\\/)\") {
return 403;
}
# Common exploit tools
if (\$query_string ~* \"(base64_decode|eval\s*\\(|php://input)\") {
return 403;
}"
if is_hestia_mode; then
write_file "${CONF_DIR}/nginx.conf_query_filter" "$QUERY_CONF" "Query filter (HTTP)"
write_file "${CONF_DIR}/nginx.ssl.conf_query_filter" "$QUERY_CONF" "Query filter (HTTPS)"
else
write_file "/etc/nginx/snippets/query-filter-${DOMAIN}.conf" "$QUERY_CONF" "Query filter snippet"
log_info "Add to your server block: include snippets/query-filter-${DOMAIN}.conf;"
fi
fi
# =====================================================================
# 5. HOTLINK PROTECTION
# =====================================================================
if ! should_skip hotlink-protection; then
log_info "Setting up hotlink protection..."
HOTLINK_CONF="# Hotlink protection — block image requests from other domains
# Installed by nginx-security-hardening.sh
location ~* \.(png|jpg|jpeg|gif|webp|svg|ico)$ {
valid_referers none blocked ${DOMAIN} *.${DOMAIN};
if (\$invalid_referer) {
return 403;
}
}"
if is_hestia_mode; then
write_file "${CONF_DIR}/nginx.conf_hotlink_protection" "$HOTLINK_CONF" "Hotlink protection (HTTP)"
write_file "${CONF_DIR}/nginx.ssl.conf_hotlink_protection" "$HOTLINK_CONF" "Hotlink protection (HTTPS)"
else
write_file "/etc/nginx/snippets/hotlink-protection-${DOMAIN}.conf" "$HOTLINK_CONF" "Hotlink protection snippet"
log_info "Add to your server block: include snippets/hotlink-protection-${DOMAIN}.conf;"
fi
fi
# =====================================================================
# FINALIZE
# =====================================================================
echo ""
if $DRY_RUN; then
log_dry "No changes were made (dry-run mode)"
log_dry "Remove --dry-run to apply changes"
else
log_info "Testing nginx configuration..."
if nginx -t 2>&1; then
log_info "Nginx config test passed"
log_info "Reloading nginx..."
systemctl reload nginx
log_info "Nginx reloaded"
else
log_error "Nginx config test FAILED — check the errors above"
log_error "Your previous config is still active (no reload happened)"
log_error "Backup files are in .backups/ subdirectories"
exit 1
fi
if ! should_skip honeypot; then
if systemctl is-active --quiet fail2ban; then
log_info "Reloading fail2ban..."
fail2ban-client reload 2>/dev/null || true
log_info "Fail2ban reloaded"
else
log_warn "Fail2ban is not running — start it to enable the honeypot jail"
fi
fi
echo ""
log_info "Installation complete!"
echo ""
should_skip rate-limit || echo -e " ${GREEN}${NC} Rate limiting: ${RATE} on ${DOWNLOAD_PATH} (burst ${BURST})"
should_skip honeypot || echo -e " ${GREEN}${NC} Bot honeypot: ${TRAP_PATH} → ban ${BANTIME}s"
should_skip headers || echo -e " ${GREEN}${NC} Security headers: X-Frame-Options, CSP, Referrer-Policy"
should_skip query-filter || echo -e " ${GREEN}${NC} Query filter: SQL injection, XSS, path traversal blocked"
should_skip hotlink-protection || echo -e " ${GREEN}${NC} Hotlink protection: Image hotlinking blocked"
if ! is_hestia_mode; then
echo ""
log_info "Standalone nginx — include the snippets in your server block:"
should_skip rate-limit || echo -e " ${CYAN}include snippets/rate-limit-${DOMAIN}.conf;${NC}"
should_skip honeypot || echo -e " ${CYAN}include snippets/honeypot-${DOMAIN}.conf;${NC}"
should_skip headers || echo -e " ${CYAN}include snippets/security-headers-${DOMAIN}.conf;${NC}"
should_skip query-filter || echo -e " ${CYAN}include snippets/query-filter-${DOMAIN}.conf;${NC}"
should_skip hotlink-protection || echo -e " ${CYAN}include snippets/hotlink-protection-${DOMAIN}.conf;${NC}"
fi
if ! should_skip honeypot; then
echo ""
log_warn "Don't forget to add the hidden honeypot link to your site template!"
fi
fi