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:
Executable
+796
@@ -0,0 +1,796 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#########################################################################################
|
||||
#### sysctl-tuner.sh — Apply, audit, diff, and rollback sysctl tuning profiles ####
|
||||
#### Built-in presets for web servers, database servers, and high-throughput ####
|
||||
#### workloads with automatic backup and restore ####
|
||||
#### ####
|
||||
#### Author: Phil Connor ####
|
||||
#### Contact: contact@mylinux.work ####
|
||||
#### License: MIT ####
|
||||
#### Version 1.01 ####
|
||||
#### ####
|
||||
#### Usage: ####
|
||||
#### ./sysctl-tuner.sh --profile web-server ####
|
||||
#### ####
|
||||
#### See --help for all options. ####
|
||||
#########################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ──────────────────────────────────────────────────────────
|
||||
MODE=""
|
||||
PROFILE="${SYSCTL_TUNER_PROFILE:-}"
|
||||
CUSTOM_FILE="${SYSCTL_TUNER_CUSTOM:-}"
|
||||
BACKUP_DIR="${SYSCTL_TUNER_BACKUP_DIR:-/var/lib/sysctl-tuner/backups}"
|
||||
PERSIST="${SYSCTL_TUNER_PERSIST:-false}"
|
||||
PERSIST_FILE="${SYSCTL_TUNER_PERSIST_FILE:-/etc/sysctl.d/99-sysctl-tuner.conf}"
|
||||
VERBOSE="${VERBOSE:-false}"
|
||||
COLOR="${COLOR:-auto}"
|
||||
RESTORE_FILE=""
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────
|
||||
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" RESET=""
|
||||
setup_colors() {
|
||||
if [[ "$COLOR" == "never" ]]; then
|
||||
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" 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'
|
||||
CYAN='\033[0;36m'
|
||||
BOLD='\033[1m'
|
||||
DIM='\033[2m'
|
||||
RESET='\033[0m'
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" CYAN="" BOLD="" DIM="" 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 "${DIM}[DEBUG]${RESET} $*"; fi; }
|
||||
|
||||
die() {
|
||||
err "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── Helpers ───────────────────────────────────────────────────────────
|
||||
section_header() {
|
||||
echo ""
|
||||
echo -e " ${BOLD}${CYAN}── $1 ──${RESET}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
require_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
die "This operation requires root privileges. Run with sudo."
|
||||
fi
|
||||
}
|
||||
|
||||
elapsed() {
|
||||
local end_time
|
||||
end_time=$(date +%s)
|
||||
echo "$(( end_time - START_TIME ))s"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# BUILT-IN PROFILES
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
# Each profile is a newline-separated list of "key = value" pairs.
|
||||
# These are written to an associative array by load_profile().
|
||||
|
||||
profile_web_server() {
|
||||
cat <<'EOF'
|
||||
# Web Server Profile — optimized for high connection counts and low latency
|
||||
# Increase listen backlog for busy web servers
|
||||
net.core.somaxconn = 4096
|
||||
# TCP SYN backlog queue
|
||||
net.ipv4.tcp_max_syn_backlog = 8192
|
||||
# Network device backlog
|
||||
net.core.netdev_max_backlog = 5000
|
||||
# Reduce keepalive time (default 7200 is too long for web)
|
||||
net.ipv4.tcp_keepalive_time = 600
|
||||
net.ipv4.tcp_keepalive_intvl = 30
|
||||
net.ipv4.tcp_keepalive_probes = 5
|
||||
# Faster connection teardown
|
||||
net.ipv4.tcp_fin_timeout = 15
|
||||
# Reuse TIME_WAIT sockets
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
# Wider ephemeral port range
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
# SYN cookies for SYN flood protection
|
||||
net.ipv4.tcp_syncookies = 1
|
||||
# Increase file descriptor limit awareness
|
||||
fs.file-max = 2097152
|
||||
# Reduce swappiness for web workloads
|
||||
vm.swappiness = 10
|
||||
EOF
|
||||
}
|
||||
|
||||
profile_db_server() {
|
||||
cat <<'EOF'
|
||||
# Database Server Profile — optimized for memory-heavy, I/O-intensive workloads
|
||||
# Minimize swapping — databases manage their own caches
|
||||
vm.swappiness = 10
|
||||
# Dirty page ratios — flush sooner for consistent write latency
|
||||
vm.dirty_ratio = 15
|
||||
vm.dirty_background_ratio = 5
|
||||
# Dirty page expiry (centiseconds) — flush pages older than 3s
|
||||
vm.dirty_expire_centisecs = 300
|
||||
# Writeback interval (centiseconds)
|
||||
vm.dirty_writeback_centisecs = 100
|
||||
# Don't overcommit memory
|
||||
vm.overcommit_memory = 0
|
||||
vm.overcommit_ratio = 80
|
||||
# Shared memory — allow large SHM segments for databases
|
||||
kernel.shmmax = 68719476736
|
||||
kernel.shmall = 4294967296
|
||||
# Semaphore limits — needed by Oracle, PostgreSQL, etc.
|
||||
kernel.sem = 250 32000 100 128
|
||||
# Increase file descriptor limit
|
||||
fs.file-max = 2097152
|
||||
# Reduce TCP keepalive for connection pooling
|
||||
net.ipv4.tcp_keepalive_time = 600
|
||||
net.ipv4.tcp_keepalive_intvl = 30
|
||||
net.ipv4.tcp_keepalive_probes = 5
|
||||
# Increase listen backlog
|
||||
net.core.somaxconn = 4096
|
||||
EOF
|
||||
}
|
||||
|
||||
profile_high_throughput() {
|
||||
cat <<'EOF'
|
||||
# High-Throughput Profile — optimized for maximum network bandwidth
|
||||
# Increase socket buffer sizes
|
||||
net.core.rmem_default = 262144
|
||||
net.core.wmem_default = 262144
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.wmem_max = 16777216
|
||||
# TCP buffer auto-tuning ranges (min, default, max)
|
||||
net.ipv4.tcp_rmem = 4096 262144 16777216
|
||||
net.ipv4.tcp_wmem = 4096 262144 16777216
|
||||
# Enable TCP window scaling
|
||||
net.ipv4.tcp_window_scaling = 1
|
||||
# Enable BBR congestion control (requires kernel 4.9+)
|
||||
net.ipv4.tcp_congestion_control = bbr
|
||||
net.core.default_qdisc = fq
|
||||
# Large listen backlog
|
||||
net.core.somaxconn = 8192
|
||||
net.ipv4.tcp_max_syn_backlog = 8192
|
||||
net.core.netdev_max_backlog = 16384
|
||||
# Reuse sockets and wider port range
|
||||
net.ipv4.tcp_tw_reuse = 1
|
||||
net.ipv4.ip_local_port_range = 1024 65535
|
||||
# Faster keepalive
|
||||
net.ipv4.tcp_keepalive_time = 600
|
||||
net.ipv4.tcp_keepalive_intvl = 30
|
||||
net.ipv4.tcp_keepalive_probes = 5
|
||||
# Reduce FIN timeout
|
||||
net.ipv4.tcp_fin_timeout = 10
|
||||
# Enable SACK and timestamps
|
||||
net.ipv4.tcp_sack = 1
|
||||
net.ipv4.tcp_timestamps = 1
|
||||
# Increase file descriptor limit
|
||||
fs.file-max = 2097152
|
||||
# Reduce swappiness
|
||||
vm.swappiness = 10
|
||||
EOF
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# PROFILE LOADING
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
declare -a PROFILE_KEYS=()
|
||||
declare -A PROFILE_VALUES=()
|
||||
|
||||
load_profile() {
|
||||
local source="$1"
|
||||
local raw=""
|
||||
|
||||
case "$source" in
|
||||
web-server) raw=$(profile_web_server) ;;
|
||||
db-server) raw=$(profile_db_server) ;;
|
||||
high-throughput) raw=$(profile_high_throughput) ;;
|
||||
*) die "Unknown built-in profile: $source (available: web-server, db-server, high-throughput)" ;;
|
||||
esac
|
||||
|
||||
parse_profile_data "$raw"
|
||||
}
|
||||
|
||||
load_custom_profile() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
die "Custom profile file not found: $file"
|
||||
fi
|
||||
|
||||
local raw
|
||||
raw=$(cat "$file")
|
||||
parse_profile_data "$raw"
|
||||
}
|
||||
|
||||
parse_profile_data() {
|
||||
local raw="$1"
|
||||
PROFILE_KEYS=()
|
||||
PROFILE_VALUES=()
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Skip blank lines and comments
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
|
||||
# Parse "key = value" or "key=value"
|
||||
local key value
|
||||
key=$(echo "$line" | sed 's/[[:space:]]*=.*$//' | xargs)
|
||||
value=$(echo "$line" | sed 's/^[^=]*=[[:space:]]*//' | xargs)
|
||||
|
||||
if [[ -n "$key" && -n "$value" ]]; then
|
||||
PROFILE_KEYS+=("$key")
|
||||
PROFILE_VALUES["$key"]="$value"
|
||||
verbose "Loaded: $key = $value"
|
||||
fi
|
||||
done <<< "$raw"
|
||||
|
||||
if [[ ${#PROFILE_KEYS[@]} -eq 0 ]]; then
|
||||
die "No valid parameters found in profile"
|
||||
fi
|
||||
|
||||
verbose "Loaded ${#PROFILE_KEYS[@]} parameter(s)"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# SYSCTL HELPERS
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
get_current_value() {
|
||||
local key="$1"
|
||||
sysctl -n "$key" 2>/dev/null | xargs || echo ""
|
||||
}
|
||||
|
||||
set_sysctl_value() {
|
||||
local key="$1"
|
||||
local value="$2"
|
||||
sysctl -w "${key}=${value}" &>/dev/null
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# BACKUP / RESTORE
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
create_backup() {
|
||||
local label="${1:-manual}"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
local timestamp
|
||||
timestamp=$(date +%Y%m%d-%H%M%S)
|
||||
local backup_file="${BACKUP_DIR}/sysctl-backup-${label}-${timestamp}.conf"
|
||||
|
||||
verbose "Creating backup at $backup_file"
|
||||
|
||||
{
|
||||
echo "# Sysctl Tuner Backup"
|
||||
echo "# Created: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
echo "# Label: $label"
|
||||
echo "# Hostname: $(hostname)"
|
||||
echo ""
|
||||
} > "$backup_file"
|
||||
|
||||
local count=0
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
if [[ -n "$current" ]]; then
|
||||
echo "${key} = ${current}" >> "$backup_file"
|
||||
((count++)) || true
|
||||
else
|
||||
echo "# ${key} = <not set>" >> "$backup_file"
|
||||
fi
|
||||
done
|
||||
|
||||
log "Backup saved: $backup_file ($count parameters)"
|
||||
echo "$backup_file"
|
||||
}
|
||||
|
||||
get_latest_backup() {
|
||||
if [[ ! -d "$BACKUP_DIR" ]]; then
|
||||
die "No backup directory found: $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
local latest
|
||||
latest=$(find "$BACKUP_DIR" -maxdepth 1 -name 'sysctl-backup-*.conf' -printf '%T@\t%p\n' 2>/dev/null | sort -rn | head -1 | cut -f2)
|
||||
|
||||
if [[ -z "$latest" ]]; then
|
||||
die "No backup files found in $BACKUP_DIR"
|
||||
fi
|
||||
|
||||
echo "$latest"
|
||||
}
|
||||
|
||||
do_backup() {
|
||||
load_active_profile
|
||||
|
||||
require_root
|
||||
|
||||
log "Backing up current sysctl values..."
|
||||
local profile_label="${PROFILE:-custom}"
|
||||
create_backup "$profile_label" > /dev/null
|
||||
}
|
||||
|
||||
do_restore() {
|
||||
require_root
|
||||
|
||||
local backup_file
|
||||
if [[ -n "$RESTORE_FILE" ]]; then
|
||||
backup_file="$RESTORE_FILE"
|
||||
if [[ ! -f "$backup_file" ]]; then
|
||||
die "Backup file not found: $backup_file"
|
||||
fi
|
||||
else
|
||||
backup_file=$(get_latest_backup)
|
||||
fi
|
||||
|
||||
log "Restoring from: $backup_file"
|
||||
|
||||
# Load the backup as a profile
|
||||
load_custom_profile "$backup_file"
|
||||
|
||||
local applied=0
|
||||
local failed=0
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local value="${PROFILE_VALUES[$key]}"
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
|
||||
if [[ "$current" == "$value" ]]; then
|
||||
verbose "Already set: $key = $value"
|
||||
((applied++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
if set_sysctl_value "$key" "$value"; then
|
||||
echo -e " ${GREEN}✓${RESET} ${key} = ${value} (was: ${current})"
|
||||
((applied++)) || true
|
||||
else
|
||||
echo -e " ${RED}✗${RESET} ${key} — failed to set ${value}"
|
||||
((failed++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
log "Restore complete: $applied applied, $failed failed ($(elapsed))"
|
||||
|
||||
if [[ "$failed" -gt 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# PROFILE RESOLUTION
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
load_active_profile() {
|
||||
if [[ -n "$CUSTOM_FILE" ]]; then
|
||||
load_custom_profile "$CUSTOM_FILE"
|
||||
elif [[ -n "$PROFILE" ]]; then
|
||||
load_profile "$PROFILE"
|
||||
else
|
||||
die "No profile specified. Use --profile NAME or --custom FILE."
|
||||
fi
|
||||
}
|
||||
|
||||
get_profile_display_name() {
|
||||
if [[ -n "$PROFILE" ]]; then
|
||||
echo "$PROFILE"
|
||||
elif [[ -n "$CUSTOM_FILE" ]]; then
|
||||
echo "custom ($(basename "$CUSTOM_FILE"))"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MODE: DIFF
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
do_diff() {
|
||||
load_active_profile
|
||||
|
||||
local profile_name
|
||||
profile_name=$(get_profile_display_name)
|
||||
|
||||
section_header "Diff: $profile_name"
|
||||
|
||||
printf " ${BOLD}%-38s %-14s %-14s${RESET}\n" "PARAMETER" "CURRENT" "PROFILE"
|
||||
echo " ─────────────────────────────────────────────────────────────"
|
||||
|
||||
local would_change=0
|
||||
local already_match=0
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local target="${PROFILE_VALUES[$key]}"
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
|
||||
if [[ -z "$current" ]]; then
|
||||
printf " ${YELLOW}%-38s %-14s → %-14s${RESET}\n" "$key" "<not set>" "$target"
|
||||
((would_change++)) || true
|
||||
elif [[ "$current" == "$target" ]]; then
|
||||
verbose "Match: $key = $current"
|
||||
((already_match++)) || true
|
||||
else
|
||||
printf " %-38s ${RED}%-14s${RESET} → ${GREEN}%-14s${RESET}\n" "$key" "$current" "$target"
|
||||
((would_change++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
log "$would_change parameter(s) would change, $already_match already match"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MODE: AUDIT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
do_audit() {
|
||||
load_active_profile
|
||||
|
||||
local profile_name
|
||||
profile_name=$(get_profile_display_name)
|
||||
|
||||
section_header "Audit: $profile_name"
|
||||
|
||||
local pass=0
|
||||
local fail=0
|
||||
local missing=0
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local target="${PROFILE_VALUES[$key]}"
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
|
||||
if [[ -z "$current" ]]; then
|
||||
echo -e " ${YELLOW}?${RESET} ${key} — parameter not found"
|
||||
((missing++)) || true
|
||||
elif [[ "$current" == "$target" ]]; then
|
||||
echo -e " ${GREEN}✓${RESET} ${key} = ${current}"
|
||||
((pass++)) || true
|
||||
else
|
||||
echo -e " ${RED}✗${RESET} ${key} — expected ${target}, got ${current}"
|
||||
((fail++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
local total=$(( pass + fail + missing ))
|
||||
echo ""
|
||||
log "Audit complete: $pass passed, $fail failed, $missing missing out of $total checks ($(elapsed))"
|
||||
|
||||
if [[ "$fail" -gt 0 || "$missing" -gt 0 ]]; then
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MODE: APPLY
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
do_apply() {
|
||||
load_active_profile
|
||||
require_root
|
||||
|
||||
local profile_name
|
||||
profile_name=$(get_profile_display_name)
|
||||
|
||||
log "Applying profile: $profile_name"
|
||||
|
||||
# Always backup before applying
|
||||
log "Backing up current values before applying..."
|
||||
local backup_file
|
||||
backup_file=$(create_backup "${PROFILE:-custom}")
|
||||
|
||||
section_header "Apply: $profile_name"
|
||||
|
||||
local applied=0
|
||||
local skipped=0
|
||||
local failed=0
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local target="${PROFILE_VALUES[$key]}"
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
|
||||
if [[ "$current" == "$target" ]]; then
|
||||
verbose "Already set: $key = $target"
|
||||
((skipped++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
if set_sysctl_value "$key" "$target"; then
|
||||
local display_current="${current:-<not set>}"
|
||||
echo -e " ${GREEN}✓${RESET} ${key} = ${target} (was: ${display_current})"
|
||||
((applied++)) || true
|
||||
else
|
||||
echo -e " ${RED}✗${RESET} ${key} — failed to set ${target}"
|
||||
((failed++)) || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Persist if requested
|
||||
if [[ "$PERSIST" == "true" ]]; then
|
||||
write_persist_file "$profile_name"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
log "Apply complete: $applied changed, $skipped unchanged, $failed failed ($(elapsed))"
|
||||
|
||||
if [[ "$failed" -gt 0 ]]; then
|
||||
warn "Some parameters failed to apply. Backup saved at: $backup_file"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
write_persist_file() {
|
||||
local profile_name="$1"
|
||||
|
||||
log "Writing persistent configuration to $PERSIST_FILE"
|
||||
|
||||
{
|
||||
echo "# Sysctl Tuner — persistent configuration"
|
||||
echo "# Profile: $profile_name"
|
||||
echo "# Generated: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
echo "# Hostname: $(hostname)"
|
||||
echo "#"
|
||||
echo "# Applied by sysctl-tuner.sh — do not edit manually"
|
||||
echo "# To revert: sudo sysctl-tuner.sh --restore && sudo rm $PERSIST_FILE"
|
||||
echo ""
|
||||
} > "$PERSIST_FILE"
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
echo "${key} = ${PROFILE_VALUES[$key]}" >> "$PERSIST_FILE"
|
||||
done
|
||||
|
||||
log "Persistent config written: $PERSIST_FILE"
|
||||
log "Settings will survive reboot. Remove the file to revert."
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MODE: EXPORT
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
do_export() {
|
||||
echo "# Sysctl Export — current running values"
|
||||
echo "# Hostname: $(hostname)"
|
||||
echo "# Exported: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
echo "#"
|
||||
echo "# Use with: sysctl-tuner.sh --apply --custom THIS_FILE"
|
||||
echo ""
|
||||
|
||||
# If a profile is specified, export only those keys
|
||||
if [[ -n "$PROFILE" || -n "$CUSTOM_FILE" ]]; then
|
||||
load_active_profile
|
||||
|
||||
for key in "${PROFILE_KEYS[@]}"; do
|
||||
local current
|
||||
current=$(get_current_value "$key")
|
||||
if [[ -n "$current" ]]; then
|
||||
echo "${key} = ${current}"
|
||||
else
|
||||
echo "# ${key} = <not set>"
|
||||
fi
|
||||
done
|
||||
else
|
||||
# Export all non-default sysctl values
|
||||
sysctl -a 2>/dev/null | sort
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# BANNER
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
print_banner() {
|
||||
local profile_name
|
||||
profile_name=$(get_profile_display_name)
|
||||
|
||||
echo -e "${BOLD}Sysctl Tuner${RESET}"
|
||||
|
||||
if [[ -n "$PROFILE" || -n "$CUSTOM_FILE" ]]; then
|
||||
echo "Profile: $profile_name"
|
||||
fi
|
||||
|
||||
echo "Mode: $MODE"
|
||||
echo "Time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# HELP
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
show_help() {
|
||||
cat <<EOF
|
||||
Usage: $SCRIPT_NAME [MODE] [OPTIONS]
|
||||
|
||||
Modes:
|
||||
--apply Apply a tuning profile (requires --profile or --custom)
|
||||
--audit Compare current values against a profile
|
||||
--diff Show what would change without applying (dry-run)
|
||||
--backup Save current values for all keys in a profile
|
||||
--restore [FILE] Roll back to the most recent (or specified) backup
|
||||
--export Dump current sysctl values as a profile file
|
||||
|
||||
Profile options:
|
||||
--profile NAME Built-in profile: web-server, db-server, high-throughput
|
||||
--custom FILE Load a custom profile file (key=value format)
|
||||
|
||||
Options:
|
||||
--persist Write drop-in to /etc/sysctl.d/ for reboot persistence
|
||||
--backup-dir DIR Backup directory (default: /var/lib/sysctl-tuner/backups)
|
||||
--verbose Debug output
|
||||
--no-color Disable colored output
|
||||
--help Show this help message
|
||||
|
||||
Environment variables:
|
||||
SYSCTL_TUNER_PROFILE Built-in profile name
|
||||
SYSCTL_TUNER_CUSTOM Path to custom profile file
|
||||
SYSCTL_TUNER_BACKUP_DIR Backup directory
|
||||
SYSCTL_TUNER_PERSIST Set to "true" to persist
|
||||
SYSCTL_TUNER_PERSIST_FILE Drop-in file path
|
||||
VERBOSE Set to "true" for debug output
|
||||
COLOR auto, always, never
|
||||
|
||||
Examples:
|
||||
# Preview changes for web-server profile
|
||||
sudo $SCRIPT_NAME --diff --profile web-server
|
||||
|
||||
# Apply database profile with persistence
|
||||
sudo $SCRIPT_NAME --apply --profile db-server --persist
|
||||
|
||||
# Audit current system against high-throughput profile
|
||||
$SCRIPT_NAME --audit --profile high-throughput
|
||||
|
||||
# Export current values as a profile
|
||||
$SCRIPT_NAME --export > my-profile.conf
|
||||
|
||||
# Apply a custom profile
|
||||
sudo $SCRIPT_NAME --apply --custom my-profile.conf
|
||||
|
||||
# Roll back the last change
|
||||
sudo $SCRIPT_NAME --restore
|
||||
EOF
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# ARGUMENT PARSING
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--apply)
|
||||
MODE="apply"
|
||||
shift
|
||||
;;
|
||||
--audit)
|
||||
MODE="audit"
|
||||
shift
|
||||
;;
|
||||
--diff)
|
||||
MODE="diff"
|
||||
shift
|
||||
;;
|
||||
--backup)
|
||||
MODE="backup"
|
||||
shift
|
||||
;;
|
||||
--restore)
|
||||
MODE="restore"
|
||||
shift
|
||||
# Optional: next arg could be a backup file path
|
||||
if [[ $# -gt 0 && ! "$1" =~ ^-- ]]; then
|
||||
RESTORE_FILE="$1"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--export)
|
||||
MODE="export"
|
||||
shift
|
||||
;;
|
||||
--profile)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
die "--profile requires a name (web-server, db-server, high-throughput)"
|
||||
fi
|
||||
PROFILE="$1"
|
||||
shift
|
||||
;;
|
||||
--custom)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
die "--custom requires a file path"
|
||||
fi
|
||||
CUSTOM_FILE="$1"
|
||||
shift
|
||||
;;
|
||||
--persist)
|
||||
PERSIST="true"
|
||||
shift
|
||||
;;
|
||||
--backup-dir)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
die "--backup-dir requires a directory path"
|
||||
fi
|
||||
BACKUP_DIR="$1"
|
||||
shift
|
||||
;;
|
||||
--verbose)
|
||||
VERBOSE="true"
|
||||
shift
|
||||
;;
|
||||
--no-color)
|
||||
COLOR="never"
|
||||
shift
|
||||
;;
|
||||
--help|-h)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
die "Unknown option: $1 (see --help)"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$MODE" ]]; then
|
||||
# Default to diff if a profile is given but no mode
|
||||
if [[ -n "$PROFILE" || -n "$CUSTOM_FILE" ]]; then
|
||||
MODE="diff"
|
||||
else
|
||||
show_help
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
# MAIN
|
||||
# ══════════════════════════════════════════════════════════════════════
|
||||
|
||||
main() {
|
||||
parse_args "$@"
|
||||
setup_colors
|
||||
|
||||
# Export mode goes straight to stdout — no banner
|
||||
if [[ "$MODE" == "export" ]]; then
|
||||
do_export
|
||||
exit 0
|
||||
fi
|
||||
|
||||
print_banner
|
||||
|
||||
case "$MODE" in
|
||||
apply) do_apply ;;
|
||||
audit) do_audit ;;
|
||||
diff) do_diff ;;
|
||||
backup) do_backup ;;
|
||||
restore) do_restore ;;
|
||||
*) die "Unknown mode: $MODE" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user