Last active
January 7, 2025 16:02
-
-
Save lucabased/5cf84f106e0a52ee661e6cc192bb27ba to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # Secure Server Setup & Hardening Script (Prompt Before Each Function) | |
| # | |
| # Description: | |
| # This script updates the system, installs useful packages, configures | |
| # security enhancements (UFW, Fail2Ban, SSH hardening, sysctl tweaks), | |
| # blacklists known bad IPs, restricts some ICMP types, adds a non-root user, | |
| # and performs basic system audits—now prompting the user to confirm (y/n) | |
| # before each function executes. | |
| set -euo pipefail | |
| #--- 1) HELPER FUNCTIONS ---------------------------------------------# | |
| info() { echo -e "\e[32m[INFO]\e[0m $*"; } | |
| warning() { echo -e "\e[33m[WARN]\e[0m $*"; } | |
| error() { echo -e "\e[31m[ERROR]\e[0m $*" >&2; } | |
| confirm_exec() { | |
| local func="$1" | |
| read -r -p "Execute function '$func'? (y/n): " response | |
| case "$response" in | |
| [yY][eE][sS]|[yY]) | |
| return 0 # "Yes" -> proceed | |
| ;; | |
| *) | |
| return 1 # "No" -> skip | |
| ;; | |
| esac | |
| } | |
| check_root() { | |
| if [[ "$EUID" -ne 0 ]]; then | |
| error "This script must be run as root. Please use sudo or log in as root." | |
| exit 1 | |
| fi | |
| } | |
| backup_file() { | |
| local file="$1" | |
| if [[ -f "$file" ]]; then | |
| cp -v "$file" "${file}.bak.$(date +%F_%T)" | |
| info "Backup created for $file" | |
| else | |
| warning "$file not found. Skipping backup." | |
| fi | |
| } | |
| #--- 2) UPDATE & INSTALL PACKAGES ------------------------------------# | |
| update_system() { | |
| info "Updating and upgrading system packages..." | |
| apt update | |
| apt -y upgrade | |
| } | |
| install_packages() { | |
| info "Installing essential and security-related packages..." | |
| apt install -y \ | |
| sudo neofetch htop ripgrep fail2ban neovim git curl wget \ | |
| unzip gnupg gnupg2 ufw tmux net-tools dnsutils software-properties-common \ | |
| build-essential zip jq tree python3-pip aptitude rsync \ | |
| rkhunter chkrootkit unattended-upgrades apt-listchanges lynis \ | |
| auditd audispd-plugins aide apparmor apparmor-profiles apparmor-utils \ | |
| cryptsetup fzf exa bat | |
| } | |
| #--- 3) SSH & KEY SETUP ----------------------------------------------# | |
| setup_ssh_keys() { | |
| info "Configuring SSH keys..." | |
| mkdir -p ~/.ssh | |
| chmod 700 ~/.ssh | |
| # Replace with your actual public key if needed | |
| echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAgdyGRBJ4PypN2+jCWLmXTuvXdIQoxPGMh0DLyOxZ4r" >> ~/.ssh/authorized_keys | |
| chmod 600 ~/.ssh/authorized_keys | |
| } | |
| harden_ssh() { | |
| info "Hardening SSH configuration..." | |
| local ssh_config="/etc/ssh/sshd_config" | |
| backup_file "$ssh_config" | |
| # Disable password auth, root login, X11 forwarding, and DNS lookups | |
| sed -i 's/^#\?Port .*/Port 2222/' "$ssh_config" | |
| sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' "$ssh_config" | |
| sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' "$ssh_config" | |
| sed -i 's/^#\?ChallengeResponseAuthentication.*/ChallengeResponseAuthentication no/' "$ssh_config" | |
| sed -i 's/^#\?X11Forwarding.*/X11Forwarding no/' "$ssh_config" | |
| sed -i 's/^#\?UseDNS.*/UseDNS no/' "$ssh_config" | |
| # Restrict SSH access to the current user (optional; you can specify others) | |
| echo "AllowUsers $(whoami)" >> "$ssh_config" | |
| systemctl reload sshd | |
| } | |
| #--- 4) CONFIGURE SECURITY -------------------------------------------# | |
| configure_fail2ban() { | |
| info "Configuring Fail2Ban..." | |
| backup_file "/etc/fail2ban/jail.local" | |
| cat <<EOF >/etc/fail2ban/jail.local | |
| [DEFAULT] | |
| bantime = 1h | |
| findtime = 10m | |
| maxretry = 3 | |
| ignoreip = 127.0.0.1/8 ::1 | |
| destemail = root@localhost | |
| sender = fail2ban@\$(hostname -f) | |
| action = %(action_mwl)s | |
| [sshd] | |
| enabled = true | |
| EOF | |
| systemctl enable fail2ban | |
| systemctl restart fail2ban | |
| } | |
| configure_sysctl() { | |
| info "Hardening sysctl settings..." | |
| local sysctl_file="/etc/sysctl.d/99-custom.conf" | |
| backup_file "$sysctl_file" | |
| cat <<EOF >"$sysctl_file" | |
| ############################################################################### | |
| # IPv4 security settings | |
| ############################################################################### | |
| net.ipv4.ip_forward = 0 | |
| net.ipv4.conf.all.accept_source_route = 0 | |
| net.ipv4.conf.all.accept_redirects = 0 | |
| net.ipv4.conf.all.secure_redirects = 0 | |
| net.ipv4.conf.all.log_martians = 1 | |
| net.ipv4.conf.default.rp_filter = 1 | |
| net.ipv4.tcp_syncookies = 1 | |
| net.ipv4.tcp_max_syn_backlog = 2048 | |
| net.ipv4.tcp_synack_retries = 2 | |
| ############################################################################### | |
| # ICMP restrictions | |
| ############################################################################### | |
| # Ignore broadcast pings | |
| net.ipv4.icmp_echo_ignore_broadcasts = 1 | |
| # Optionally ignore all inbound pings (0=respond, 1=ignore) | |
| # net.ipv4.icmp_echo_ignore_all = 1 | |
| # Rate limit ICMP to avoid ping floods (tweak as needed) | |
| net.ipv4.icmp_ratelimit = 100 | |
| net.ipv4.icmp_ratemask = 88089 | |
| EOF | |
| sysctl --system | |
| } | |
| configure_firewall() { | |
| info "Configuring UFW firewall..." | |
| ufw default deny incoming | |
| ufw default allow outgoing | |
| # Allow incoming SSH, HTTP, HTTPS (adjust as needed) | |
| ufw allow ssh | |
| ufw allow 80/tcp | |
| ufw allow 443/tcp | |
| # Example: Blacklisting known bad IPs | |
| KNOWN_BAD_IPS=( | |
| "203.0.113.123" | |
| "198.51.100.250" | |
| "192.0.2.5" | |
| ) | |
| for ip in "${KNOWN_BAD_IPS[@]}"; do | |
| ufw deny from "$ip" | |
| done | |
| ufw --force enable | |
| } | |
| configure_unattended_upgrades() { | |
| info "Configuring unattended upgrades..." | |
| dpkg-reconfigure --priority=low unattended-upgrades | |
| } | |
| configure_apparmor() { | |
| info "Enabling and starting AppArmor..." | |
| systemctl enable apparmor | |
| systemctl start apparmor | |
| } | |
| configure_auditd() { | |
| info "Enabling and starting auditd..." | |
| systemctl enable auditd | |
| systemctl start auditd | |
| } | |
| #--- 5) SYSTEM SCANS & LOGGING ---------------------------------------# | |
| run_malware_scans() { | |
| info "Updating and running rkhunter checks..." | |
| # Fix WEB_CMD configuration in rkhunter | |
| local rkhunter_conf="/etc/rkhunter.conf" | |
| if [[ -f "$rkhunter_conf" ]]; then | |
| backup_file "$rkhunter_conf" | |
| # Use /usr/bin/links -dump or comment out WEB_CMD if not needed | |
| if command -v links >/dev/null 2>&1; then | |
| sed -i 's|^WEB_CMD=.*|WEB_CMD="/usr/bin/links -dump"|' "$rkhunter_conf" | |
| else | |
| sed -i 's|^WEB_CMD=.*|# WEB_CMD disabled, no valid renderer|' "$rkhunter_conf" | |
| fi | |
| fi | |
| # Update and initialize rkhunter | |
| rkhunter --update | |
| rkhunter --propupd # Save current system state as baseline | |
| rkhunter --check | |
| } | |
| run_lynis() { | |
| info "Running Lynis security audit..." | |
| lynis audit system || true | |
| } | |
| configure_aide() { | |
| info "Initializing AIDE (file integrity monitoring)..." | |
| aideinit | |
| mv /var/lib/aide/aide.db.new /var/lib/aide/aide.db | |
| } | |
| #--- 6) USER & PERMISSIONS -------------------------------------------# | |
| create_sudo_user() { | |
| read -r -p "Enter new username: " new_user | |
| if id "$new_user" &>/dev/null; then | |
| warning "User '$new_user' already exists. Skipping creation." | |
| else | |
| adduser "$new_user" | |
| usermod -aG sudo "$new_user" | |
| info "New user '$new_user' added and granted sudo privileges." | |
| fi | |
| } | |
| secure_file_permissions() { | |
| info "Hardening file system permissions..." | |
| chmod 700 /root | |
| chmod -R go-rwx /etc/ssh | |
| } | |
| #--- 7) WRAP-UP ------------------------------------------------------# | |
| display_system_info() { | |
| info "Displaying system information..." | |
| neofetch || true | |
| } | |
| cleanup() { | |
| info "Cleaning up unnecessary packages..." | |
| apt -y autoremove | |
| apt clean | |
| } | |
| #--- MAIN ------------------------------------------------------------# | |
| main() { | |
| check_root # This one is mandatory to ensure the script runs as root | |
| if confirm_exec "update_system"; then | |
| update_system | |
| fi | |
| if confirm_exec "install_packages"; then | |
| install_packages | |
| fi | |
| if confirm_exec "setup_ssh_keys"; then | |
| setup_ssh_keys | |
| fi | |
| if confirm_exec "harden_ssh"; then | |
| harden_ssh | |
| fi | |
| if confirm_exec "configure_fail2ban"; then | |
| configure_fail2ban | |
| fi | |
| if confirm_exec "configure_sysctl"; then | |
| configure_sysctl | |
| fi | |
| if confirm_exec "configure_firewall"; then | |
| configure_firewall | |
| fi | |
| if confirm_exec "configure_unattended_upgrades"; then | |
| configure_unattended_upgrades | |
| fi | |
| if confirm_exec "configure_apparmor"; then | |
| configure_apparmor | |
| fi | |
| if confirm_exec "configure_auditd"; then | |
| configure_auditd | |
| fi | |
| if confirm_exec "run_malware_scans"; then | |
| run_malware_scans | |
| fi | |
| if confirm_exec "run_lynis"; then | |
| run_lynis | |
| fi | |
| if confirm_exec "configure_aide"; then | |
| configure_aide | |
| fi | |
| if confirm_exec "create_sudo_user"; then | |
| create_sudo_user | |
| fi | |
| if confirm_exec "secure_file_permissions"; then | |
| secure_file_permissions | |
| fi | |
| if confirm_exec "display_system_info"; then | |
| display_system_info | |
| fi | |
| if confirm_exec "cleanup"; then | |
| cleanup | |
| fi | |
| info "Setup complete. A system reboot is recommended." | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment