#!/usr/bin/bash # This script is specifically designed to harden a singular Rocky Linux 9 LAMP Server # Use at your own peril! # Functions: # create_trusted_zones: Function to identify what IP Addresses/Blocks get access to protected ports (ssh, mysql, Rocky Linux Cockpit and Webmin) # setup_iptables: Disables firewalld if installed setup and install iptables to start at boot # flush_iptables: Clears out and empties all iptables rules for fresh build # setup_iptables_rules: This sets up base iptables rules for a functional firewall # setup_firewall_service_at_boot: This function sets up service to start firewall at boot # # Jim Kerr jim@advalgo.com # Functions for this bash: function create_trusted_zones(){ if [[ -f $TRUSTED_ZONES ]] then rm -f "$TRUSTED_ZONES" touch "$TRUSTED_ZONES" else touch "$TRUSTED_ZONES" # Copy this script where it can bu used at boot and set permissions accordingly: cp -f secure_lamp.sh /usr/local/advalgo-rocky9/firewall.sh chmod +x /usr/local/advalgo-rocky9/firewall.sh fi FINISHED="start" while [[ $FINISHED != 'done' ]] do echo 'Enter Trusted IP addresses or CIDR Blocks then enter "done" when finished:' read FINISHED if [[ $FINISHED != 'done' ]] then echo $FINISHED >> $TRUSTED_ZONES fi done echo "You have added the following as trusted IPs:" cat "$TRUSTED_ZONES" echo "Are these correct? (y or enter): " read ANSWER if [[ $ANSWER != 'y' ]] then create_trusted_zones fi } function setup_iptables(){ # Install iptables tools and utilities then turn off firewalld # and start iptables: dnf install iptables-services iptables-utils -y systemctl stop firewalld systemctl disable firewalld systemctl mask firewalld systemctl enable --now iptables # Start iptables at bootup } function flush_iptables(){ # Flush firewall and set policy: iptables -F echo "Flushing all iptables tables." iptables -t filter -F iptables -t raw -F iptables -t nat -F iptables -t mangle -F iptables -X # chain_name_goes_here_if_any echo "Flushing and destroying all ip sets." ipset -F ipset -X # only if chain name exist echo "SET DEFAULT POLICY TO DROP ANYTHING OUTSIDE ABOVE RULES..." iptables -P INPUT DROP iptables -P OUTPUT DROP iptables -P FORWARD DROP } function setup_iptables_rules(){ echo "Permiting loopback interface..." iptables -I INPUT -i lo -j ACCEPT iptables -I OUTPUT -o lo -j ACCEPT echo "Allow icmp packets..." iptables -I INPUT -p icmp -j ACCEPT iptables -I OUTPUT -p icmp -j ACCEPT echo "Allow stateful connections from established outgoing..." iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -I OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT # Allowed ports: echo "Allowing FTP: 21 and 2222 for proftpd virtualmin HTTP: 80 HTTPS: 443..." ALLOWED_PORTS="21 80 443 2222" for PORT in $ALLOWED_PORTS do iptables -A INPUT -p tcp --dport $PORT -j ACCEPT done echo "Creating port range 7000-7500 for passive ftp..." iptables -I INPUT -p tcp --destination-port 7000:7500 -j ACCEPT # FTP Port passive port range echo "Allowing DNS upd Traffic..." iptables -A INPUT -p udp --dport 53 -j ACCEPT # This needs modified for whatever network to allow SSH and anything else if [[ -d /usr/local/advalgo-rocky9 ]] then echo "System container /usr/local/advalgo-rocky9 to load firewall at boot set..." else echo "Creating container for advalgo-rocky9 scripts at /usr/local/advalgo-rocky9 to use at startup..." mkdir /usr/local/advalgo-rocky9 fi if [[ -f $TRUSTED_ZONES ]] then PERMIT_SSH_IPS=$(< /usr/local/advalgo-rocky9/trusted.zones) else create_trusted_zones PERMIT_SSH_IPS=$(< /usr/local/advalgo-rocky9/trusted.zones) fi for IP in $PERMIT_SSH_IPS do # ftp for secure users only 20 21 as well as email 25 110 587 iptables -A INPUT -p tcp -m tcp --dport 22 -s $IP -j ACCEPT # SSH Allowed IP Addresses iptables -A INPUT -p tcp -m tcp --dport 10000 -s $IP -j ACCEPT # This is for webmin access iptables -A INPUT -p tcp -m tcp --dport 3306 -s $IP -j ACCEPT # MairaDB Admin access iptables -A INPUT -p tcp -m tcp --dport 9090 -s $IP -j ACCEPT # Rocky Linux Cockpit access done # This may need modified depending on the website. For example if you have 100 people behind the same # IP Address trying to see a website only 10 of them are going to succeed. echo "Setting Connection limits per IP connection for DDoS and DoS mitigation..." iptables -A INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 10 -j DROP # Add firewall dropped packet logging: iptables -N INPUT-LOGGING iptables -A INPUT -j INPUT-LOGGING iptables -A INPUT-LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-INPUT-Dropped: " --log-level 4 iptables -A INPUT-LOGGING -j DROP iptables -N OUTPUT-LOGGING iptables -A OUTPUT -j OUTPUT-LOGGING iptables -A OUTPUT-LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-OUTPUT-Dropped: " --log-level 4 iptables -A OUTPUT-LOGGING -j DROP } function setup_firewall_service_at_boot () { if [[ -d /usr/local/advalgo-rocky9 ]] then echo "Directory advalgo-rocky9 for startup files and firewall present..." else echo "Creating /usr/local/advalgo-rocky9 for Advalgo Firewall Scripts..." mkdir /usr/local/advalgo-rocky9 fi cp secure_lamp.sh /usr/local/advalgo-rocky9/firewall.sh touch /etc/systemd/system/firewall.service echo "[Unit] Description=IPTables HTTP/HTTPS/FTP Firewall Service After=network.target [Service] Type=simple ExecStart=/usr/local/advalgo-rocky9/firewall.sh [Install] WantedBy=multi-user.target" > /etc/systemd/system/firewall.service systemctl daemon-reload systemctl enable firewall systemctl start firewall # This is needed to allow WordPress to create directories and write to files so do this once you have httpd setup: chcon -R -t httpd_sys_rw_content_t /var/www/html # The following three are needed to allow vsftpd to work and to setsebool -P ftpd_use_passive_mode=on setsebool -P ftpd_full_access=on # This is necessary to allow http talk to another http like when # WordPress shows you the themes and plugins available: setsebool -P httpd_can_network_connect=on } # Actual bash program starts here with: # Variables for this bash: TRUSTED_ZONES="/usr/local/advalgo-rocky9/trusted.zones" FIREWALL_SH="/usr/local/advalgo-rocky9/firewall.sh" FIREWALLD_STATUS="$(systemctl is-active firewalld)" IPTABLES_STATUS="$(systemctl is-active iptables)" FAIL2BAN_STATUS="$(systemctl is-active fail2ban)" echo "firewalld status: $FIREWALLD_STATUS" if [[ $FIREWALLD_STATUS = 'active' ]] then echo "Switching from firewalld to iptables and enabling iptables to start at boot..." setup_iptables fi echo "IPTables status: $IPTABLES_STATUS" if [[ $IPTABLES_STATUS = 'inactive' ]] then setup_iptables systemctl start iptables flush_iptables setup_iptables_rules else flush_iptables setup_iptables_rules fi echo "Service setup: $SERVICE_SETUP" if [[ -f /usr/local/advalgo-rocky9/firewall.sh ]] then echo "firewall.service already setup." else echo "Setting up, verifying and testing firewall service to start at boot to see how this went do:" echo "~]# systemctl status firewall" echo "" setup_firewall_service_at_boot fi if [[ FAIL2BAN_STATUS == 'inactive' ]] then echo "Installing fail2ban.." dnf install fail2ban -y echo "Starting fail2ban and setting to start at boot..." systemctl enable --now fail2ban else echo "fail2ban setup and running!" fi echo "Finished! Type at ~]# iptables -vnL to see your current rules." echo "You now have a semi hardened web server! Still need to make sure fail2ban is setup and rkhunter!"