feat(firewalld,freebds+fail2ban):
- Linux: ufw -> firewalld - installations: separate install list for linux & freebsd - firewall: `pf` for freebsd & `firewalld` for linux - firewall: allow ssh, http, https in; block all other - firewall freebsd: start pf & pflog services - fail2ban: Jails for: ssh, nginx-botsearch, nginx-http-auth, nginx-limit-req, haproxy-http-auth, recidive - fail2ban linux: action performed by `firewalld` - fail2ban freebsd: action performed by `pf` - chore: Hide command outputs from `if` conditions - refactor: `if` with `else` checks positive conditions - refactor: No explicit `return`s on success - refactor: No `exit`s - chore: Consistent log formatting with values inside `[]` - fix: Reverting success isn't operation succes
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
SCRIPT_NAME=server-init-harden
|
SCRIPT_NAME=server-init-harden
|
||||||
SCRIPT_VERSION=2.2
|
SCRIPT_VERSION=2.7
|
||||||
TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S')
|
TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S')
|
||||||
LOG_FILE_NAME="${SCRIPT_NAME}_${TIMESTAMP}.log"
|
LOG_FILE_NAME="${SCRIPT_NAME}_${TIMESTAMP}.log"
|
||||||
START_TIME=$(date +%s)
|
|
||||||
|
|
||||||
USERNAME=""
|
USERNAME=""
|
||||||
RESET_ROOT=false
|
RESET_ROOT=false
|
||||||
@@ -25,7 +24,7 @@ DESCRIPTION:
|
|||||||
- Generates secure SSH keys
|
- Generates secure SSH keys
|
||||||
- Resets root password (optional)
|
- Resets root password (optional)
|
||||||
- Configures Fail2ban for intrusion prevention
|
- Configures Fail2ban for intrusion prevention
|
||||||
- Sets up UFW firewall rules
|
- Sets up Firewalld firewall rules
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
-u USERNAME Create a new sudo user with the specified username
|
-u USERNAME Create a new sudo user with the specified username
|
||||||
@@ -34,7 +33,7 @@ OPTIONS:
|
|||||||
-h Display this help message
|
-h Display this help message
|
||||||
|
|
||||||
EXAMPLES:
|
EXAMPLES:
|
||||||
# Basic hardening (SSH, Fail2ban, UFW)
|
# Basic hardening (SSH, Fail2ban, Firewalld)
|
||||||
$0
|
$0
|
||||||
|
|
||||||
# Create new sudo user during hardening
|
# Create new sudo user during hardening
|
||||||
@@ -77,8 +76,8 @@ parse_and_validate_args() {
|
|||||||
USERNAME="$2"
|
USERNAME="$2"
|
||||||
shift 2
|
shift 2
|
||||||
else
|
else
|
||||||
console_log "ERROR" "Invalid username format. Must start with a letter and contain only alphanumeric characters, hyphens, or underscores."
|
console_log "ERROR" "Invalid username"
|
||||||
file_log "ERROR" "Invalid username format. Must start with a letter and contain only alphanumeric characters, hyphens, or underscores."
|
file_log "ERROR" "Invalid username. Must start with a letter and contain alphanumeric characters, hyphens, underscores."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
@@ -143,11 +142,7 @@ print_operation_details() {
|
|||||||
echo "Following system hardening operations will be performed:"
|
echo "Following system hardening operations will be performed:"
|
||||||
|
|
||||||
if [ "$RESET_ROOT" = true ]; then
|
if [ "$RESET_ROOT" = true ]; then
|
||||||
echo " [-r]: Existing root user's password will be re-created"
|
echo " [-r]: root password will be reset"
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$SHOW_CREDENTIALS" = true ]; then
|
|
||||||
echo " [-s]: Passwords, keys are will be displayed on the screen"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$USERNAME" ]; then
|
if [ -n "$USERNAME" ]; then
|
||||||
@@ -160,8 +155,8 @@ print_operation_details() {
|
|||||||
echo " SSH: login to root account will be disabled"
|
echo " SSH: login to root account will be disabled"
|
||||||
echo " SSH: login can only happen using generated SSH keys"
|
echo " SSH: login can only happen using generated SSH keys"
|
||||||
echo " Software repository will be updated & required software will be installed"
|
echo " Software repository will be updated & required software will be installed"
|
||||||
echo " UFW: Firewall will be configured to only allow SSH, HTTP, HTTPS traffic into the server"
|
echo " Firewalld: Firewall will be configured to only allow SSH, HTTP, HTTPS traffic into the server"
|
||||||
echo " Fail2Ban: Configured to automatically block repeat offender IPs"
|
echo " Fail2ban: Configured to automatically block repeat offender IPs"
|
||||||
}
|
}
|
||||||
|
|
||||||
print_log_file_details() {
|
print_log_file_details() {
|
||||||
@@ -288,13 +283,13 @@ reset_root_password() {
|
|||||||
else
|
else
|
||||||
console_log "SUCCESS" "Root password reset"
|
console_log "SUCCESS" "Root password reset"
|
||||||
file_log "SUCCESS" "Root password reset"
|
file_log "SUCCESS" "Root password reset"
|
||||||
log_credentials "New root password: $ROOT_PASSWORD"
|
log_credentials "New root password: [ $ROOT_PASSWORD ]"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
revert_create_user() {
|
revert_create_user() {
|
||||||
console_log "INFO" "Attempting to remove user $USERNAME"
|
console_log "INFO" "Attempting to remove user [ $USERNAME ]"
|
||||||
file_log "INFO" "Attempting to remove user $USERNAME"
|
file_log "INFO" "Attempting to remove user [ $USERNAME ]"
|
||||||
|
|
||||||
# Check if the user exists before attempting to remove
|
# Check if the user exists before attempting to remove
|
||||||
if id "$USERNAME" >/dev/null 2>&1; then
|
if id "$USERNAME" >/dev/null 2>&1; then
|
||||||
@@ -304,21 +299,21 @@ revert_create_user() {
|
|||||||
file_log "INFO" "$output"
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
if [ $command_status -eq 0 ]; then
|
if [ $command_status -eq 0 ]; then
|
||||||
console_log "SUCCESS" "User $USERNAME and home directory removed"
|
console_log "INFO" "User [ $USERNAME ] and home directory removed"
|
||||||
file_log "SUCCESS" "User $USERNAME and home directory removed"
|
file_log "INFO" "User [ $USERNAME ] and home directory removed"
|
||||||
else
|
else
|
||||||
console_log "ERROR" "Failed to remove user $USERNAME"
|
console_log "ERROR" "Failed to remove user [ $USERNAME ]"
|
||||||
file_log "ERROR" "Failed to remove user $USERNAME"
|
file_log "ERROR" "Failed to remove user [ $USERNAME ]"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
console_log "WARNING" "No user $USERNAME found to remove"
|
console_log "WARNING" "No user [ $USERNAME ] found to remove"
|
||||||
file_log "WARNING" "No user $USERNAME found to remove"
|
file_log "WARNING" "No user $USERNAME found to remove"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
create_user() {
|
create_user() {
|
||||||
console_log "INFO" "Creating user $USERNAME..."
|
console_log "INFO" "Creating user [ $USERNAME ]..."
|
||||||
file_log "INFO" "Creating user $USERNAME"
|
file_log "INFO" "Creating user [ $USERNAME ]"
|
||||||
|
|
||||||
# Generate a 15-character random password
|
# Generate a 15-character random password
|
||||||
USER_PASSWORD=$(head -c 12 /dev/urandom | base64 | tr -dc "[:alnum:]" | head -c 15)
|
USER_PASSWORD=$(head -c 12 /dev/urandom | base64 | tr -dc "[:alnum:]" | head -c 15)
|
||||||
@@ -335,21 +330,21 @@ create_user() {
|
|||||||
|
|
||||||
file_log "INFO" "$output"
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
if [ $command_status -ne 0 ]; then
|
if [ $command_status -eq 0 ]; then
|
||||||
console_log "ERROR" "Failed to create user: $USERNAME"
|
file_log "SUCCESS" "Created user [ $USERNAME ]"
|
||||||
file_log "ERROR" "Failed to create user $USERNAME"
|
console_log "SUCCESS" "Created user [ $USERNAME ]"
|
||||||
|
log_credentials "$USERNAME's password [ $USER_PASSWORD ]"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to create user [ $USERNAME ]"
|
||||||
|
file_log "ERROR" "Failed to create user [ $USERNAME ]"
|
||||||
revert_create_user
|
revert_create_user
|
||||||
return 1
|
return 1
|
||||||
else
|
|
||||||
file_log "SUCCESS" "User created: $USERNAME"
|
|
||||||
console_log "SUCCESS" "User created: $USERNAME"
|
|
||||||
log_credentials "$USERNAME's password: $USER_PASSWORD"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
user_privileged_access() {
|
user_privileged_access() {
|
||||||
file_log "INFO" "Granting privileged access (sudo) to $USERNAME"
|
file_log "INFO" "Granting privileged access (sudo) to [ $USERNAME ]"
|
||||||
console_log "INFO" "Granting privileged access (sudo) to $USERNAME"
|
console_log "INFO" "Granting privileged access (sudo) to [ $USERNAME ]"
|
||||||
|
|
||||||
if getent group wheel >/dev/null 2>&1; then
|
if getent group wheel >/dev/null 2>&1; then
|
||||||
if command -v pw >/dev/null 2>&1; then # FreeBSD
|
if command -v pw >/dev/null 2>&1; then # FreeBSD
|
||||||
@@ -370,27 +365,27 @@ user_privileged_access() {
|
|||||||
|
|
||||||
file_log "INFO" "$output"
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
if [ "$command_status" -ne 0 ]; then
|
if [ "$command_status" -eq 0 ]; then
|
||||||
console_log "ERROR" "Failed to grant privileged access to $USERNAME"
|
file_log "SUCCESS" "[ $USERNAME ] granted privileged access"
|
||||||
file_log "ERROR" "Failed to grant privileged access to $USERNAME"
|
console_log "SUCCESS" "[ $USERNAME ] granted privileged access"
|
||||||
console_log "WARNING" "From $USERNAME, use [su -] to login to root & perform special operations"
|
|
||||||
file_log "WARNING" "From $USERNAME, use [su -] to login to root & perform special operations"
|
|
||||||
else
|
else
|
||||||
file_log "SUCCESS" "$USERNAME granted privileged access"
|
console_log "ERROR" "Failed to grant privileged access to [ $USERNAME ]"
|
||||||
console_log "SUCCESS" "$USERNAME granted privileged access"
|
file_log "ERROR" "Failed to grant privileged access to [ $USERNAME ]"
|
||||||
|
console_log "WARNING" "From [ $USERNAME ], use [su -] to login to root & perform special operations"
|
||||||
|
file_log "WARNING" "From [ $USERNAME ], use [su -] to login to root & perform special operations"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
generate_ssh_key() {
|
generate_ssh_key() {
|
||||||
console_log "INFO" "Generating SSH key for $SSH_KEY_USER..."
|
console_log "INFO" "Generating SSH key for [ $SSH_KEY_USER ]..."
|
||||||
file_log "INFO" "Generating SSH key for $SSH_KEY_USER"
|
file_log "INFO" "Generating SSH key for [ $SSH_KEY_USER ]"
|
||||||
|
|
||||||
# Create .ssh directory & set proper permissions
|
# Create .ssh directory & set proper permissions
|
||||||
home_dir=$(eval echo "~$USERNAME")
|
home_dir=$(eval echo "~$USERNAME")
|
||||||
ssh_dir="$home_dir/.ssh"
|
ssh_dir="$home_dir/.ssh"
|
||||||
if [ ! -d "$home_dir" ]; then
|
if [ ! -d "$home_dir" ]; then
|
||||||
console_log "ERROR" "Home directory not found for $SSH_KEY_USER"
|
console_log "ERROR" "Home directory not found for [ $SSH_KEY_USER ]"
|
||||||
file_log "ERROR" "Home directory not found for $SSH_KEY_USER"
|
file_log "ERROR" "Home directory not found for [ $SSH_KEY_USER ]"
|
||||||
return 1
|
return 1
|
||||||
else
|
else
|
||||||
mkdir -p "$ssh_dir" && chown "$SSH_KEY_USER:$SSH_KEY_USER" "$ssh_dir" && chmod 700 "$ssh_dir" || return 1
|
mkdir -p "$ssh_dir" && chown "$SSH_KEY_USER:$SSH_KEY_USER" "$ssh_dir" && chmod 700 "$ssh_dir" || return 1
|
||||||
@@ -447,26 +442,24 @@ generate_ssh_key() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
revert_ssh_config_changes() {
|
revert_ssh_config_changes() {
|
||||||
# Revert to backup and try restarting again
|
# Revert backup and try restarting again
|
||||||
console_log "INFO" "Reverting to backup configuration..."
|
console_log "INFO" "Reverting to backup configuration..."
|
||||||
file_log "INFO" "Reverting to backup configuration from: $SSH_CONFIG_BACKUP_FILE"
|
file_log "INFO" "Reverting to backup configuration from: $SSH_CONFIG_BACKUP_FILE"
|
||||||
|
|
||||||
if ! cp "$SSH_CONFIG_BACKUP_FILE" "$SSHD_CONFIG_FILE"; then
|
if ! cp "$SSH_CONFIG_BACKUP_FILE" "$SSHD_CONFIG_FILE" >/dev/null 2>&1; then
|
||||||
console_log "ERROR" "Failed to restore SSH config backup"
|
console_log "ERROR" "Failed to restore SSH config backup"
|
||||||
file_log "ERROR" "Failed to restore SSH config backup"
|
file_log "ERROR" "Failed to restore SSH config backup"
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Try restarting SSH with original config
|
# Try restarting SSH with original config
|
||||||
if manage_service sshd restart || manage_service ssh restart; then
|
if manage_service sshd restart >/dev/null 2>&1 || manage_service ssh restart >/dev/null 2>&1; then
|
||||||
console_log "SUCCESS" "SSH service restarted with original configuration"
|
console_log "INFO" "SSH service restarted with original configuration"
|
||||||
file_log "SUCCESS" "SSH service restarted with original configuration"
|
file_log "INFO" "SSH service restarted with original configuration"
|
||||||
exit 1
|
else
|
||||||
|
console_log "ERROR" "Failed to restart SSH service even with original configuration"
|
||||||
|
file_log "ERROR" "Failed to restart SSH service even with original configuration"
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
console_log "ERROR" "Failed to restart SSH service even with original configuration"
|
|
||||||
file_log "ERROR" "Failed to restart SSH service even with original configuration"
|
|
||||||
exit 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_ssh_setting() {
|
update_ssh_setting() {
|
||||||
@@ -481,17 +474,18 @@ update_ssh_setting() {
|
|||||||
command_status=$?
|
command_status=$?
|
||||||
rm -f "$SSHD_CONFIG_FILE.tmp" >/dev/null 2>&1
|
rm -f "$SSHD_CONFIG_FILE.tmp" >/dev/null 2>&1
|
||||||
|
|
||||||
if [ $command_status -ne 0 ]; then
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
if [ $command_status -eq 0 ]; then
|
||||||
|
# Add new setting at the end of file
|
||||||
|
echo "${setting} ${value}" >>"$SSHD_CONFIG_FILE"
|
||||||
|
file_log "INFO" "Updated SSH setting: ${setting} ${value}"
|
||||||
|
else
|
||||||
console_log "ERROR" "Updating SSH configuration [ $setting $value ] failed"
|
console_log "ERROR" "Updating SSH configuration [ $setting $value ] failed"
|
||||||
file_log "ERROR" "Updating SSH configuration [ $setting $value ] failed: $output"
|
file_log "ERROR" "Updating SSH configuration [ $setting $value ] failed: $output"
|
||||||
revert_ssh_config_changes
|
revert_ssh_config_changes
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
file_log "INFO" "$output"
|
|
||||||
|
|
||||||
# Add new setting at the end of file
|
|
||||||
echo "${setting} ${value}" >>"$SSHD_CONFIG_FILE"
|
|
||||||
file_log "INFO" "Updated SSH setting: ${setting} ${value}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
harden_ssh_config() {
|
harden_ssh_config() {
|
||||||
@@ -527,11 +521,11 @@ harden_ssh_config() {
|
|||||||
if manage_service sshd restart || manage_service ssh restart; then
|
if manage_service sshd restart || manage_service ssh restart; then
|
||||||
console_log "SUCCESS" "SSH service restarted"
|
console_log "SUCCESS" "SSH service restarted"
|
||||||
file_log "SUCCESS" "SSH service restarted"
|
file_log "SUCCESS" "SSH service restarted"
|
||||||
return 0
|
|
||||||
else
|
else
|
||||||
console_log "ERROR" "Failed to restart SSH service"
|
console_log "ERROR" "Failed to restart SSH service"
|
||||||
file_log "ERROR" "Failed to restart SSH service"
|
file_log "ERROR" "Failed to restart SSH service"
|
||||||
revert_ssh_config_changes
|
revert_ssh_config_changes
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,15 +533,16 @@ install_packages() {
|
|||||||
console_log "INFO" "Installing required applications..."
|
console_log "INFO" "Installing required applications..."
|
||||||
file_log "INFO" "Installing required applications..."
|
file_log "INFO" "Installing required applications..."
|
||||||
|
|
||||||
LINUX_ONLY_PACKAGES="firewalld"
|
LINUX_ONLY_PACKAGES="firewalld fail2ban"
|
||||||
COMMON_PACKAGES="curl sudo sshguard"
|
FREEBSD_ONLY_PACKAGES="py311-fail2ban"
|
||||||
|
COMMON_PACKAGES="curl sudo"
|
||||||
|
|
||||||
# Detect the package manager and OS
|
# Detect the package manager and OS
|
||||||
if [ -f /etc/debian_version ] || [ -f /etc/ubuntu_version ]; then # Debian/Ubuntu
|
if [ -f /etc/debian_version ] || [ -f /etc/ubuntu_version ]; then # Debian/Ubuntu
|
||||||
# Don't let timezone setting stop installation: make UTC server's timezone
|
# Don't let timezone setting stop installation: make UTC server's timezone
|
||||||
ln -fs /usr/share/zoneinfo/UTC /etc/localtime >/dev/null
|
ln -fs /usr/share/zoneinfo/UTC /etc/localtime >/dev/null
|
||||||
console_log "WARNING" "Server's timezone set to UTC to avoid installation interruption."
|
console_log "WARNING" "Timezone set to UTC to avoid installation interruption"
|
||||||
file_log "WARNING" "Server's timezone set to UTC to avoid installation interruption. Change this after the script completes."
|
file_log "WARNING" "Timezone set to UTC to avoid installation interruption. Change this after the script completes."
|
||||||
file_log "INFO" "Installing $COMMON_PACKAGES $LINUX_ONLY_PACKAGES using apt..."
|
file_log "INFO" "Installing $COMMON_PACKAGES $LINUX_ONLY_PACKAGES using apt..."
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
output=$(DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get install -y --no-install-recommends $COMMON_PACKAGES $LINUX_ONLY_PACKAGES 2>&1)
|
output=$(DEBIAN_FRONTEND=noninteractive apt-get update -y && apt-get install -y --no-install-recommends $COMMON_PACKAGES $LINUX_ONLY_PACKAGES 2>&1)
|
||||||
@@ -570,7 +565,7 @@ install_packages() {
|
|||||||
elif [ -f /etc/freebsd-update.conf ]; then # FreeBSD
|
elif [ -f /etc/freebsd-update.conf ]; then # FreeBSD
|
||||||
file_log "INFO" "Installing $COMMON_PACKAGES using pkg..."
|
file_log "INFO" "Installing $COMMON_PACKAGES using pkg..."
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
output=$(pkg update && pkg install -y $COMMON_PACKAGES 2>&1)
|
output=$(pkg update 2>&1 && pkg install -y $COMMON_PACKAGES $FREEBSD_ONLY_PACKAGES 2>&1)
|
||||||
command_status=$?
|
command_status=$?
|
||||||
else
|
else
|
||||||
file_log "ERROR" "Unsupported operating system"
|
file_log "ERROR" "Unsupported operating system"
|
||||||
@@ -579,194 +574,451 @@ install_packages() {
|
|||||||
|
|
||||||
file_log "INFO" "Applications installation output: $output"
|
file_log "INFO" "Applications installation output: $output"
|
||||||
|
|
||||||
if [ $command_status -ne 0 ]; then
|
if [ $command_status -eq 0 ]; then
|
||||||
console_log "SUCCESS" "Installed all required applications"
|
file_log "SUCCESS" "Installed required applications"
|
||||||
|
console_log "SUCCESS" "Installed required applications"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to install applications"
|
||||||
file_log "ERROR" "Failed to install applications"
|
file_log "ERROR" "Failed to install applications"
|
||||||
return 1
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_firewall_linux() {
|
||||||
|
# Debian/Ubuntu -> Disable ufw if active
|
||||||
|
if command -v ufw >/dev/null 2>&1 && ufw status 2>&1 | grep -q "Status: active"; then
|
||||||
|
file_log "INFO" "ufw installed & active. Disabling it..."
|
||||||
|
output=$(ufw disable 2>&1)
|
||||||
|
console_log "WARNING" "Pre-installed firewall application ufw disabled"
|
||||||
|
file_log "WARNING" "Pre-installed firewall application ufw disabled: $output"
|
||||||
|
output=$(systemctl disable --now ufw 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enable Firewalld
|
||||||
|
output=$(systemctl enable firewalld 2>&1 && systemctl start --now firewalld)
|
||||||
|
file_log "INFO" "Enable firewalld: $output"
|
||||||
|
|
||||||
|
output=$(firewall-cmd --permanent --add-service=ssh 2>&1)
|
||||||
|
file_log "INFO" "Allow SSH: $output"
|
||||||
|
|
||||||
|
output=$(firewall-cmd --permanent --add-service=http 2>&1)
|
||||||
|
file_log "INFO" "Allow HTTP: $output"
|
||||||
|
|
||||||
|
output=$(firewall-cmd --permanent --add-service=https 2>&1)
|
||||||
|
file_log "INFO" "Allow HTTPS: $output"
|
||||||
|
|
||||||
|
# Enable Firewall
|
||||||
|
output=$(firewall-cmd --reload 2>&1)
|
||||||
|
file_log "INFO" "Reload firewalld service: $output"
|
||||||
|
|
||||||
|
# Verify Firewall is active
|
||||||
|
output=$(firewall-cmd --list-services 2>&1)
|
||||||
|
file_log "INFO" "Active firewalls: $output"
|
||||||
|
|
||||||
|
if echo "$output" | grep -q '\bssh\b' &&
|
||||||
|
echo "$output" | grep -q '\bhttp\b' &&
|
||||||
|
echo "$output" | grep -q '\bhttps\b'; then
|
||||||
|
return 0
|
||||||
else
|
else
|
||||||
file_log "SUCCESS" "Installed applications"
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_ufw() {
|
configure_firewall_freebsd() {
|
||||||
console_log "INFO" "Starting UFW configuration..."
|
# Path to the new pf configuration file
|
||||||
file_log "INFO" "Starting UFW configuration"
|
PF_CONF_FILE="/etc/pf.conf"
|
||||||
|
|
||||||
# Check if UFW is installed
|
# Create backup with timestamps
|
||||||
if ! command -v ufw >/dev/null 2>&1; then
|
if [ -f "$PF_CONF_FILE" ]; then
|
||||||
file_log "ERROR" "UFW is not installed"
|
PF_CONF_BACKUP_FILE="${PF_CONF_FILE}.bak.${TIMESTAMP}"
|
||||||
return 1
|
output=$(mv "$PF_CONF_FILE" "$PF_CONF_BACKUP_FILE" 2>&1)
|
||||||
|
file_log "INFO" "Backed up existing configuration to $PF_CONF_BACKUP_FILE"
|
||||||
|
file_log "INFO" "$output"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
output=$(ufw allow ssh 2>&1)
|
touch $PF_CONF_FILE
|
||||||
if [ -n "$output" ]; then
|
cat >>$PF_CONF_FILE <<'EOF'
|
||||||
file_log "INFO" "ufw allow ssh output: $output"
|
# Network Hygiene: Normalize network packets
|
||||||
|
scrub in all
|
||||||
|
|
||||||
|
# Do not filter on the loopback interface for performance
|
||||||
|
set skip on lo0
|
||||||
|
|
||||||
|
# Block all incoming and outgoing traffic by default
|
||||||
|
# Only those that are allowed subsequently will happen
|
||||||
|
block all
|
||||||
|
|
||||||
|
# Allow all outgoing traffic and track connections
|
||||||
|
pass out all keep state
|
||||||
|
|
||||||
|
# Allow incoming ssh, http, https traffic
|
||||||
|
pass in proto tcp from any to any port { ssh, http, https } keep state
|
||||||
|
EOF
|
||||||
|
|
||||||
|
output=$(service pf start 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# Verify rules and load configuration
|
||||||
|
output=$(pfctl -nf $PF_CONF_FILE 2>&1 && pfctl -vvf $PF_CONF_FILE 2>&1)
|
||||||
|
command_status=$?
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# On config success, enable PF & pflog on boot
|
||||||
|
if [ $command_status -eq 0 ]; then
|
||||||
|
output=$(pfctl -e 2>/dev/null || true)
|
||||||
|
file_log "INFO" "PF Enabled: $output"
|
||||||
|
|
||||||
|
# Enable the PF firewall service on boot
|
||||||
|
output=$(sysrc pf_enable="YES" 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
output=$(sysrc pf_rules="$PF_CONF_FILE" 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# Enable logging for the firewall
|
||||||
|
output=$(sysrc pflog_enable="YES" 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# Set pf logfile to /var/log/pflog
|
||||||
|
output=$(sysrc pflog_logfile="/var/log/pflog" 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# Start pflog service
|
||||||
|
output=$(service pflog start 2>&1)
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
file_log "SUCCESS" "PF firewall configured"
|
||||||
|
|
||||||
|
return $command_status
|
||||||
|
else # Error in PF configuration
|
||||||
|
console_log "ERROR" "PF firewall configuration failed"
|
||||||
|
file_log "ERROR" "PF firewall configuration failed"
|
||||||
|
|
||||||
|
console_log "INFO" "Reverting PF configuration..."
|
||||||
|
file_log "INFO" "Reverting PF configuration..."
|
||||||
|
|
||||||
|
if cp "$PF_CONF_BACKUP_FILE" "$PF_CONF_FILE" >/dev/null 2>&1; then
|
||||||
|
console_log "INFO" "Restored [ $PF_CONF_FILE ]"
|
||||||
|
file_log "INFO" "Restored [ $PF_CONF_FILE ]"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restore $PF_CONF_FILE"
|
||||||
|
file_log "ERROR" "Failed to restore $PF_CONF_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Load original PF config
|
||||||
|
if pfctl -vvnf $PF_CONF_FILE >/dev/null 2>&1 && pfctl -f $PF_CONF_FILE >/dev/null 2>&1; then
|
||||||
|
console_log "INFO" "Restarted PF with original configuration"
|
||||||
|
file_log "INFO" "Restarted PF with original configuration"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restart PF even with original configuration"
|
||||||
|
file_log "ERROR" "Failed to restart PF even with original configuration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $command_status
|
||||||
fi
|
fi
|
||||||
|
|
||||||
output=$(ufw allow http 2>&1)
|
# TIP: Troubleshoot:
|
||||||
if [ -n "$output" ]; then
|
# List defined "rules": pfctl -s rules
|
||||||
file_log "INFO" "ufw allow http output: $output"
|
# Debug rules: pfctl -vvsr
|
||||||
fi
|
# Reset PF: pfctl -F all
|
||||||
|
|
||||||
output=$(ufw allow https 2>&1)
|
|
||||||
if [ -n "$output" ]; then
|
|
||||||
file_log "INFO" "ufw allow https output: $output"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Enable UFW
|
|
||||||
output=$(echo "y" | ufw enable 2>&1)
|
|
||||||
if [ -n "$output" ]; then
|
|
||||||
file_log "INFO" "ufw enable output: $output"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify UFW is active
|
|
||||||
output=$(ufw status 2>&1)
|
|
||||||
if [ -n "$output" ]; then
|
|
||||||
file_log "INFO" "ufw status output: $output"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! echo "$output" | grep -q "Status: active"; then
|
|
||||||
console_log "ERROR" "UFW is not active after enabling"
|
|
||||||
file_log "ERROR" "UFW is not active after enabling"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
console_log "SUCCESS" "UFW configuration completed successfully"
|
|
||||||
file_log "SUCCESS" "UFW configuration completed successfully"
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update_fail2ban_jail_local_file() {
|
configure_firewall() {
|
||||||
search_term="$1"
|
console_log "INFO" "Configuring firewall..."
|
||||||
new_value="$2"
|
file_log "INFO" "Configuring firewall..."
|
||||||
|
|
||||||
# We want to update the setting in the [DEFAULT] section
|
if command -v firewall-cmd >/dev/null 2>&1; then # Linux
|
||||||
# [DEFAULT] section ends right before "# JAILS"
|
configure_firewall_linux
|
||||||
range_start="^\[DEFAULT\]$"
|
command_status=$?
|
||||||
range_end="^# JAILS$"
|
elif [ -f /etc/freebsd-update.conf ]; then # FreeBSD
|
||||||
file=$JAIL_LOCAL_FILE
|
configure_firewall_freebsd
|
||||||
|
command_status=$?
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Could not find required application for firewall configuration"
|
||||||
|
file_log "ERROR" "Could not find required application for firewall configuration"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
# When the setting exists & it's NOT commented out -> comment it and add the new setting on next line
|
if [ $command_status -eq 0 ]; then
|
||||||
if sed -n "/${range_start}/,/${range_end}/p" "$file" | grep -q "^${search_term}[[:blank:]]*="; then
|
console_log "SUCCESS" "Firewall configured"
|
||||||
sed -ri "/${range_start}/,/${range_end}/ s/^(${search_term}[[:blank:]]*=.*)/#\1/" "$file"
|
file_log "SUCCESS" "Firewall configured"
|
||||||
sed -ri "/${range_start}/,/${range_end}/ s/^#${search_term}[[:blank:]]*=.*/&\n${search_term} = ${new_value}/" "$file"
|
else
|
||||||
else # If the setting is commented out or it doesn't exist -> add it after the commented line or at the end of the section
|
console_log "ERROR" "Failed to configure firewall"
|
||||||
sed -ri "/${range_start}/,/${range_end}/ s/^#${search_term}[[:blank:]]*=.*/&\n${search_term} = ${new_value}/" "$file"
|
file_log "ERROR" "Failed to configure firewall"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fail2ban_jail_settings() {
|
||||||
|
JAIL_LOCAL=$1
|
||||||
|
|
||||||
|
# Backup jail.local if it exists
|
||||||
|
if [ -f "$JAIL_LOCAL" ]; then
|
||||||
|
JAIL_LOCAL_BACKUP="${JAIL_LOCAL}.bak.${TIMESTAMP}"
|
||||||
|
cp "$JAIL_LOCAL" "$JAIL_LOCAL_BACKUP"
|
||||||
|
file_log "INFO" "Created backup of existing jail.local at $JAIL_LOCAL_BACKUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get server's public IP
|
||||||
|
file_log "Getting server's public IP..."
|
||||||
|
PUBLIC_IP=$(curl -s -4 ifconfig.me 2>&1 || curl -s -4 icanhazip.com 2>&1 || curl -s -4 ipinfo.io/ip 2>&1)
|
||||||
|
file_log "INFO" "Server public IP: $PUBLIC_IP"
|
||||||
|
|
||||||
|
file_log "INFO" "Adding jails to $JAIL_LOCAL..."
|
||||||
|
|
||||||
|
cat <<FAIL2BAN >"$JAIL_LOCAL"
|
||||||
|
[DEFAULT]
|
||||||
|
backend = auto
|
||||||
|
banaction = firewallcmd-rich-rules[actiontype=<multiport>]
|
||||||
|
banaction_allports = firewallcmd-rich-rules[actiontype=<allports>]
|
||||||
|
ignoreip = 127.0.0.1/8 ::1 $PUBLIC_IP
|
||||||
|
bantime = 1h
|
||||||
|
findtime = 10m
|
||||||
|
maxretry = 5
|
||||||
|
# Action: ban only (action_) or ban and email (action_mwl)
|
||||||
|
action = %(action_)s
|
||||||
|
|
||||||
|
#
|
||||||
|
# SSH Jail
|
||||||
|
#
|
||||||
|
[sshd]
|
||||||
|
enabled = true
|
||||||
|
port = ssh
|
||||||
|
filter = sshd
|
||||||
|
logpath = %(sshd_log)s
|
||||||
|
/var/log/auth.log
|
||||||
|
/var/log/secure
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 1h
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
#
|
||||||
|
# Nginx Bot Search - Blocks bots searching for vulnerabilities (404 errors)
|
||||||
|
#
|
||||||
|
[nginx-botsearch]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-botsearch
|
||||||
|
logpath = %(nginx_access_log)s
|
||||||
|
/var/log/nginx/access.log
|
||||||
|
$(dirname "$JAIL_LOCAL_BACKUP")/emptylog
|
||||||
|
maxretry = 5
|
||||||
|
bantime = 6h
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
#
|
||||||
|
# Nginx HTTP Authentication
|
||||||
|
#
|
||||||
|
[nginx-http-auth]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-http-auth
|
||||||
|
logpath = %(nginx_error_log)s
|
||||||
|
/var/log/nginx/error.log
|
||||||
|
$(dirname "$JAIL_LOCAL_BACKUP")/emptylog
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 6h
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
#
|
||||||
|
# Nginx Limit Request (DDoS protection)
|
||||||
|
#
|
||||||
|
[nginx-limit-req]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = nginx-limit-req
|
||||||
|
logpath = %(nginx_error_log)s
|
||||||
|
/var/log/nginx/error.log
|
||||||
|
$(dirname "$JAIL_LOCAL_BACKUP")/emptylog
|
||||||
|
maxretry = 10
|
||||||
|
bantime = 6h
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
#
|
||||||
|
# HAProxy HTTP Authentication Failures
|
||||||
|
#
|
||||||
|
[haproxy-http-auth]
|
||||||
|
enabled = true
|
||||||
|
port = http,https
|
||||||
|
filter = haproxy-http-auth
|
||||||
|
logpath = /var/log/haproxy.log
|
||||||
|
/var/log/haproxy/haproxy.log
|
||||||
|
/var/log/haproxy/*.log
|
||||||
|
$(dirname "$JAIL_LOCAL_BACKUP")/emptylog
|
||||||
|
maxretry = 3
|
||||||
|
bantime = 6h
|
||||||
|
findtime = 10m
|
||||||
|
|
||||||
|
#
|
||||||
|
# Recidive Jail - Ban repeat offenders
|
||||||
|
# This jail monitors fail2ban.log for IPs that have been banned multiple times
|
||||||
|
#
|
||||||
|
[recidive]
|
||||||
|
enabled = true
|
||||||
|
filter = recidive
|
||||||
|
logpath = /var/log/fail2ban.log
|
||||||
|
banaction = %(banaction_allports)s
|
||||||
|
bantime = 1w
|
||||||
|
findtime = 1d
|
||||||
|
maxretry = 3
|
||||||
|
FAIL2BAN
|
||||||
|
|
||||||
|
# FreeBSD specific ban-actions
|
||||||
|
if [ -f /etc/pf.conf ]; then
|
||||||
|
sed -i.bak -E 's/(^banaction = )firewallcmd.*/\1pf[actiontype=<allports>]/' "$JAIL_LOCAL"
|
||||||
|
sed -i.bak -E 's/(^banaction_allports = )firewallcmd.*/\1pf[actiontype=<allports>]/' "$JAIL_LOCAL"
|
||||||
|
rm "$JAIL_LOCAL".bak >/dev/null 2>&1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dummy logfile so the configuration doesn't fail
|
||||||
|
touch "$(dirname "$JAIL_LOCAL")"/emptylog && chmod 644 "$(dirname "$JAIL_LOCAL")"/emptylog
|
||||||
|
|
||||||
|
file_log "INFO" "Jails added to $JAIL_LOCAL"
|
||||||
|
}
|
||||||
|
|
||||||
|
revert_fail2ban_jail_file() {
|
||||||
|
if [ -f "$JAIL_LOCAL_BACKUP" ]; then
|
||||||
|
console_log "INFO" "Reverting jail.local using [ $JAIL_LOCAL_BACKUP ]..."
|
||||||
|
file_log "INFO" "Reverting jail.local using [ $JAIL_LOCAL_BACKUP ]..."
|
||||||
|
|
||||||
|
if cp "$JAIL_LOCAL_BACKUP" "$JAIL_LOCAL" >/dev/null 2>&1; then
|
||||||
|
console_log "INFO" "Restored jail.local to original version"
|
||||||
|
file_log "INFO" "Restored jail.local to original version"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restore jail.local"
|
||||||
|
file_log "INFO" "Failed to restore jail.local"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# If no backup exists -> we created new jail.local file; delete it
|
||||||
|
console_log "INFO" "Removing jail.local..."
|
||||||
|
file_log "INFO" "Removing jail.local..."
|
||||||
|
rm -f "$JAIL_LOCAL"
|
||||||
|
console_log "INFO" "Removed jail.local file"
|
||||||
|
file_log "INFO" "Removed jail.local file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try restarting fail2ban with original configuration
|
||||||
|
if manage_service fail2ban restart; then
|
||||||
|
console_log "INFO" "Restarted fail2ban with original configuration"
|
||||||
|
file_log "INFO" "Restarted fail2ban with original configuration"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restart fail2ban service even with original configuration"
|
||||||
|
file_log "ERROR" "Failed to restart fail2ban service even with original configuration"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_fail2ban_linux() {
|
||||||
|
fail2ban_jail_settings "/etc/fail2ban/jail.local"
|
||||||
|
|
||||||
|
# Restart fail2ban
|
||||||
|
if ! manage_service fail2ban restart; then # Error in configuration
|
||||||
|
console_log "ERROR" "Failed to restart fail2ban service"
|
||||||
|
file_log "ERROR" "Failed to restart fail2ban service"
|
||||||
|
|
||||||
|
revert_fail2ban_jail_file
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
configure_fail2ban_freebsd() {
|
||||||
|
RC_CONF_FILE="/etc/rc.conf"
|
||||||
|
PF_CONF_FILE="/etc/pf.conf"
|
||||||
|
fail2ban_jail_settings "/usr/local/etc/fail2ban/jail.local"
|
||||||
|
|
||||||
|
# Auto start fail2ban on boot and restart with new configuration
|
||||||
|
output=$(sysrc fail2ban_enable="YES" 2>&1 && manage_service fail2ban restart 2>&1)
|
||||||
|
command_output=$?
|
||||||
|
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
# Revert fail2ban & rc.conf changes if fail2ban starting failed
|
||||||
|
if [ $command_output -ne 0 ]; then
|
||||||
|
file_log "ERROR" "Could not start fail2ban service. Reverting changes..."
|
||||||
|
|
||||||
|
# Don't start fail2ban on boot
|
||||||
|
sed -i.bak '/^fail2ban_enable/d' $RC_CONF_FILE
|
||||||
|
rm "$RC_CONF_FILE".bak >/dev/null 2>&1
|
||||||
|
|
||||||
|
file_log "INFO" "Reverted $RC_CONF_FILE"
|
||||||
|
|
||||||
|
revert_fail2ban_jail_file
|
||||||
|
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f "$PF_CONF_FILE" ]; then
|
||||||
|
PF_CONF_BACKUP_FILE="${PF_CONF_FILE}.bak.${TIMESTAMP}"
|
||||||
|
output=$(mv "$PF_CONF_FILE" "$PF_CONF_BACKUP_FILE" 2>&1)
|
||||||
|
file_log "INFO" "Backed up existing configuration to $PF_CONF_BACKUP_FILE"
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add fail2ban table to PF configuration
|
||||||
|
if ! grep -q 'table <f2b>' "$PF_CONF_FILE" 2>/dev/null; then
|
||||||
|
cat >>"$PF_CONF_FILE" <<'EOF'
|
||||||
|
|
||||||
|
# Fail2ban table and anchor
|
||||||
|
table <f2b> persist
|
||||||
|
anchor "f2b/*"
|
||||||
|
block drop in quick from <f2b> to any
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify rules and load configuration
|
||||||
|
output=$(pfctl -nf $PF_CONF_FILE 2>&1 && pfctl -vvf $PF_CONF_FILE 2>&1)
|
||||||
|
command_status=$?
|
||||||
|
file_log "INFO" "$output"
|
||||||
|
|
||||||
|
if [ $command_output -ne 0 ]; then
|
||||||
|
console_log "ERROR" "Failed to restart pf post fail2ban. Reverting pf.config..."
|
||||||
|
file_log "ERROR" "Failed to restart pf post fail2ban. Reverting pf.config..."
|
||||||
|
|
||||||
|
if cp "$PF_CONF_BACKUP_FILE" "$PF_CONF_FILE" >/dev/null 2>&1; then
|
||||||
|
console_log "INFO" "Restored pf.conf to original version"
|
||||||
|
file_log "INFO" "Restored pf.conf to original version"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restore pf.conf"
|
||||||
|
file_log "INFO" "Failed to restore pf.conf"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try restarting pf with original configuration
|
||||||
|
if pfctl -f $PF_CONF_FILE >/dev/null 2>&1; then
|
||||||
|
console_log "INFO" "Restarted pf with original configuration"
|
||||||
|
file_log "INFO" "Restarted pf with original configuration"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Failed to restart pf even with original configuration"
|
||||||
|
file_log "ERROR" "Failed to restart pf even with original configuration"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
configure_fail2ban() {
|
configure_fail2ban() {
|
||||||
console_log "INFO" "Starting Fail2ban configuration..."
|
console_log "INFO" "Configuring Fail2ban..."
|
||||||
file_log "INFO" "Starting Fail2ban configuration"
|
file_log "INFO" "Configuring Fail2ban..."
|
||||||
|
|
||||||
if ! command -v fail2ban-client >/dev/null 2>&1; then
|
if command -v firewall-cmd >/dev/null 2>&1; then # Linux
|
||||||
file_log "ERROR" "Fail2ban is not installed"
|
configure_fail2ban_linux
|
||||||
|
command_status=$?
|
||||||
|
elif [ -f /etc/pf.conf ]; then # FreeBSD
|
||||||
|
configure_fail2ban_freebsd
|
||||||
|
command_status=$?
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$command_status" -eq 0 ]; then
|
||||||
|
console_log "SUCCESS" "Configured Fail2ban"
|
||||||
|
file_log "SUCCESS" "Configured Fail2ban"
|
||||||
|
else
|
||||||
|
console_log "ERROR" "Fail2ban configuration unsuccessful"
|
||||||
|
file_log "ERROR" "Fail2ban configuration unsuccessful"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
JAIL_LOCAL_FILE="/etc/fail2ban/jail.local"
|
|
||||||
DEFAULT_JAIL_CONF_FILE="/etc/fail2ban/jail.conf"
|
|
||||||
CUSTOM_JAILS_FILE="/etc/fail2ban/jail.d/custom-enabled.conf"
|
|
||||||
|
|
||||||
# Backup jail.local if it exists
|
|
||||||
if [ -f "$JAIL_LOCAL_FILE" ]; then
|
|
||||||
JAIL_LOCAL_BACKUP_FILE="${JAIL_LOCAL_FILE}.bak.${TIMESTAMP}"
|
|
||||||
cp "$JAIL_LOCAL_FILE" "$JAIL_LOCAL_BACKUP_FILE"
|
|
||||||
file_log "INFO" "Created backup of existing jail.local at $JAIL_LOCAL_BACKUP_FILE"
|
|
||||||
else # Copy jail.conf to jail.local if jail.local doesn't exist
|
|
||||||
if [ -f "$DEFAULT_JAIL_CONF_FILE" ]; then
|
|
||||||
cp "$DEFAULT_JAIL_CONF_FILE" "$JAIL_LOCAL_FILE"
|
|
||||||
file_log "INFO" "Created jail.local from jail.conf"
|
|
||||||
else
|
|
||||||
console_log "ERROR" "Neither jail.conf nor jail.local exists"
|
|
||||||
file_log "ERROR" "Neither jail.conf nor jail.local exists"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch public IP using ipinfo.io/ip
|
|
||||||
file_log "INFO" "Attempting to get server's public IP"
|
|
||||||
output=$(curl -s -4 ifconfig.me 2>&1 || curl -s -4 icanhazip.com 2>&1 || curl -s -4 ipinfo.io/ip 2>&1)
|
|
||||||
if [ -z "$output" ]; then
|
|
||||||
console_log "ERROR" "Could not determine server's public IP"
|
|
||||||
file_log "ERROR" "Could not determine server's public IP"
|
|
||||||
PUBLIC_IP=""
|
|
||||||
else
|
|
||||||
PUBLIC_IP="$output"
|
|
||||||
file_log "INFO" "Server public IP: $PUBLIC_IP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update default settings in jail.local
|
|
||||||
update_fail2ban_jail_local_file "bantime" "5h"
|
|
||||||
update_fail2ban_jail_local_file "backend" "systemd"
|
|
||||||
update_fail2ban_jail_local_file "ignoreip" "127.0.0.1\/8 ::1 $PUBLIC_IP"
|
|
||||||
|
|
||||||
# Enable jails and more settings for them in /etc/fail2ban/jail.d/custom-enabled.conf
|
|
||||||
file_log "INFO" "Enabling jails in $CUSTOM_JAILS_FILE"
|
|
||||||
cat <<FAIL2BAN >$CUSTOM_JAILS_FILE
|
|
||||||
[sshd]
|
|
||||||
enabled = true
|
|
||||||
filter = sshd
|
|
||||||
bantime = 1d
|
|
||||||
maxretry = 3
|
|
||||||
|
|
||||||
[nginx-http-auth]
|
|
||||||
enabled = true
|
|
||||||
logpath = /var/log/nginx/error.log
|
|
||||||
maxretry = 3
|
|
||||||
|
|
||||||
# Repeat offenders across all other jails
|
|
||||||
[recidive]
|
|
||||||
enabled = true
|
|
||||||
filter = recidive
|
|
||||||
findtime = 1d
|
|
||||||
bantime = 30d
|
|
||||||
maxretry = 50
|
|
||||||
FAIL2BAN
|
|
||||||
|
|
||||||
if ! manage_service fail2ban restart; then
|
|
||||||
|
|
||||||
console_log "ERROR" "Failed to restart fail2ban service"
|
|
||||||
file_log "ERROR" "Failed to restart fail2ban service"
|
|
||||||
|
|
||||||
# Revert jail.local to backup if it exists
|
|
||||||
if [ -f "$JAIL_LOCAL_BACKUP_FILE" ]; then
|
|
||||||
console_log "INFO" "Reverting jail.local to backup..."
|
|
||||||
file_log "INFO" "Reverting jail.local to backup from: $JAIL_LOCAL_BACKUP_FILE"
|
|
||||||
|
|
||||||
if ! cp "$JAIL_LOCAL_BACKUP_FILE" "$JAIL_LOCAL_FILE"; then
|
|
||||||
console_log "ERROR" "Failed to restore jail.local backup"
|
|
||||||
file_log "ERROR" "Failed to restore jail.local backup"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else # If no backup exists (i.e, jail.local was created from jail.conf) -> remove jail.local
|
|
||||||
console_log "INFO" "Removing newly created jail.local..."
|
|
||||||
file_log "INFO" "Removing newly created jail.local"
|
|
||||||
rm -f "$JAIL_LOCAL_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Remove the custom enabled configuration
|
|
||||||
if [ -f "$CUSTOM_JAILS_FILE" ]; then
|
|
||||||
console_log "INFO" "Removing custom jail configuration..."
|
|
||||||
file_log "INFO" "Removing custom jail configuration: $CUSTOM_JAILS_FILE"
|
|
||||||
rm -f "$CUSTOM_JAILS_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Try restarting fail2ban with original configuration
|
|
||||||
if ! manage_service fail2ban restart; then
|
|
||||||
console_log "ERROR" "Failed to restart fail2ban service even with original configuration"
|
|
||||||
file_log "ERROR" "Failed to restart fail2ban service even with original configuration"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
console_log "INFO" "Fail2ban restarted with original configuration"
|
|
||||||
file_log "INFO" "Fail2ban restarted with original configuration"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
console_log "SUCCESS" "Fail2ban configuration completed successfully"
|
|
||||||
file_log "SUCCESS" "Fail2ban configuration completed successfully"
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
@@ -776,11 +1028,14 @@ main() {
|
|||||||
|
|
||||||
print_operation_details
|
print_operation_details
|
||||||
print_log_file_details
|
print_log_file_details
|
||||||
|
|
||||||
|
echo
|
||||||
echo "Press [Enter] to continue. [Ctrl + c] to cancel..."
|
echo "Press [Enter] to continue. [Ctrl + c] to cancel..."
|
||||||
# shellcheck disable=SC2162,SC2034
|
# shellcheck disable=SC2162,SC2034
|
||||||
read dummy
|
read dummy
|
||||||
|
|
||||||
# Log script start
|
# Log script start
|
||||||
|
START_TIME=$(date +%s)
|
||||||
console_log "INFO" "Starting $SCRIPT_NAME v$SCRIPT_VERSION..."
|
console_log "INFO" "Starting $SCRIPT_NAME v$SCRIPT_VERSION..."
|
||||||
file_log "INFO" "Starting $SCRIPT_NAME v$SCRIPT_VERSION..."
|
file_log "INFO" "Starting $SCRIPT_NAME v$SCRIPT_VERSION..."
|
||||||
|
|
||||||
@@ -793,9 +1048,11 @@ main() {
|
|||||||
# Step 2: Create new user
|
# Step 2: Create new user
|
||||||
if [ -n "$USERNAME" ]; then
|
if [ -n "$USERNAME" ]; then
|
||||||
if ! create_user; then
|
if ! create_user; then
|
||||||
|
print_log_file_details
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
if ! user_privileged_access; then
|
if ! user_privileged_access; then
|
||||||
|
print_log_file_details
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -808,52 +1065,45 @@ main() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ! generate_ssh_key "$SSH_KEY_USER"; then
|
if ! generate_ssh_key "$SSH_KEY_USER"; then
|
||||||
console_log "ERROR" "Failed to generate SSH key for $SSH_KEY_USER"
|
console_log "ERROR" "Failed to generate SSH key for [ $SSH_KEY_USER ]"
|
||||||
|
print_log_file_details
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 4: Configure SSH
|
# Step 4: Configure SSH
|
||||||
if ! harden_ssh_config; then
|
if ! harden_ssh_config; then
|
||||||
console_log "ERROR" "Failed to update ssh configuration to harden it"
|
console_log "ERROR" "Failed to update ssh configuration to harden it"
|
||||||
|
print_log_file_details
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 5: Install required packages
|
# Step 5: Install required packages
|
||||||
if ! install_packages; then
|
if ! install_packages; then
|
||||||
|
print_log_file_details
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Step 6: Configure UFW
|
# Step 6: Configure Firewall
|
||||||
console_log "INFO" "Configuring UFW..."
|
if ! configure_firewall; then
|
||||||
file_log "INFO" "Configuring UFW..."
|
print_log_file_details
|
||||||
if ! configure_ufw; then
|
|
||||||
console_log "ERROR" "Failed to configure UFW"
|
|
||||||
print_logfile_details
|
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
console_log "SUCCESS" "Successfully configured UFW"
|
|
||||||
file_log "SUCCESS" "Successfully configured UFW"
|
|
||||||
|
|
||||||
# Step 7: Configure Fail2ban
|
# Step 7: Configure Fail2ban
|
||||||
console_log "INFO" "Configuring Fail2ban..."
|
|
||||||
file_log "INFO" "Configuring Fail2ban..."
|
|
||||||
if ! configure_fail2ban; then
|
if ! configure_fail2ban; then
|
||||||
console_log "ERROR" "Failed to configure Fail2ban"
|
print_log_file_details
|
||||||
print_logfile_details
|
|
||||||
return 1 # Abort on error
|
return 1 # Abort on error
|
||||||
fi
|
fi
|
||||||
console_log "SUCCESS" "Successfully configured Fail2ban"
|
|
||||||
file_log "SUCCESS" "Successfully configured Fail2ban"
|
|
||||||
|
|
||||||
console_log "SUCCESS" "Script completed successfully"
|
console_log "SUCCESS" "All Done"
|
||||||
file_log "SUCCESS" "Script completed successfully"
|
file_log "SUCCESS" "All Done"
|
||||||
|
|
||||||
# Calculate and show execution time
|
# Calculate and show execution time
|
||||||
FORMATTED_DURATION=$(formatted_execution_duration)
|
FORMATTED_DURATION=$(formatted_execution_duration)
|
||||||
console_log "INFO" "Total execution time: $FORMATTED_DURATION"
|
console_log "INFO" "Total execution time: [ $FORMATTED_DURATION ]"
|
||||||
file_log "INFO" "Total execution time: $FORMATTED_DURATION"
|
file_log "INFO" "Total execution time: [ $FORMATTED_DURATION ]"
|
||||||
|
|
||||||
print_logfile_details
|
print_log_file_details
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user