Introduction to the Updated Firewall Setup
In this updated blog post, we introduce a new version of our Linux IP Tables firewall configuration script that we incorporate next to the powerful CrowdSec blocklist feature. This integration significantly enhances security by leveraging community-maintained threat intelligence feeds, providing robust protection against various network threats.
We allow for logging of all blocked packets which also benefits the CrowdSec IP Tables Parser Agent that will listen to the syslog entries for IP Tables blocks. Truly, our logging pathways and rate limited rules allow for the logging to CrowdSec’s parser to provide good updates/blocks for the LAPI’s connected security perimeter.
What is CrowdSec?
CrowdSec is an open-source project that aggregates and maintains blocklists from reputable sources such as firehol_cruzit_web_attacks, crowdsec, and more. These lists help identify malicious IP addresses attempting to access your system, offering real-time threat detection and mitigation.
You can install it all by itself, but as mentioned in other blog entries here a Multi-Server installation offers a way to grow your perimeter without having to get additional ‘Engines’, your Server is your Engine, and the Agents (Parsers/Bouncers) all connect to the Server’s LAPI. You might have a LAPI on your OPNSense router, or deployed as a Docker container that all of your CrowdSec Agents then phone home to.
The Agents all will have a local auth key they use to connect to the CrowdSec Server LAPI, then it connects back home if you configure it to the CrowdSec main CAPI to get any lists you might subscribe to.
Don’t have CrowdSec yet, simply install it on this host:
https://docs.crowdsec.net/u/getting_started/installation/linux
Install the CrowdSec IPTables based Bouncer:
https://docs.crowdsec.net/u/bouncers/firewall
Install the IPTables based Parser for your CrowdSec Agent/Server:
https://app.crowdsec.net/hub/author/crowdsecurity/collections/iptables
Key Features of the New Firewall Configuration
- Dynamic Network Configuration:
- The script dynamically calculates network information based on the interface’s IP address, ensuring adaptability across different network environments.
- Comprehensive Service Protection:
- Enhanced protection for essential services like SSH, HTTP/HTTPS, DNS, and NTP with rate limiting and connection tracking to prevent DDoS attacks.
- Minimal IPv6 Support:
- Includes specific rules for IPv6 ICMPv6 functionality, may expand in the future for full service communication over both IPv4 and IPv6 networks.
- Advanced Logging and Dropping Mechanisms:
- Utilizes a unified LOGGING_DROP chain with detailed logging, improving troubleshooting capabilities and providing consistent security monitoring.
- CrowdSec Integration:
- Designed to continue the protection of the CrowdSec blocklists at the top of the firewall rules, ensuring malicious traffic is intercepted early, and only necessary services are permitted, including package updates and the like.
- WireGuard VPN Allow Featured:
- To connect an external host to another network to get access to a LAPI, one would want to allow that host in via a VPN and here we ensure WireGuard’s functionality in our rules by way of the port and connection name.
Firewall Configuration Script
The new script introduces several improvements:
- Dynamic IP Handling: Calculates network information such as subnet, gateway, and netmask based on the interface’s IP.
- Rate Limiting and Connection Tracking: Implements strict rate limits for SSH and web services to mitigate brute force attacks and DDoS attempts.
- Expanded Service Rules: Includes rules for additional services like SMTP, NTP, and DHCP, ensuring all essential protocols are securely managed.
The Bash script
Finally, here is the IP Tables firewall bash script, obviously you do not have to use CrowdSec with this script and it is just an upgraded version of the v2.7 script we have published before. Please enjoy!
#!/bin/sh
IPT=/sbin/iptables
IP6T=/sbin/ip6tables
ETHINTF="eth0" # Your primary network adapter
SSHPORT="22" # The port you use for SSH
SMTP_PORT="587" # Secure SMTP port (STARTTLS)
WIREGUARD_PORT="51820" # The remote WireGuard port for your VPN Server
WIREGUARD_INTF="wg0" # WG NIC
WIREGUARD_SUB_NET="192.168.0.0/24" # WG IPv4 sub/net aka CIDR
# Get interface information
interface_info=$(ip addr show $ETHINTF | grep "inet ")
if [ -z "$interface_info" ]; then
echo "Interface not found."
exit 1
fi
# Extract IP and CIDR notation (e.g., "192.168.1.1/24")
ip_cidr=$(echo "$interface_info" | awk '{print $2}')
if [ -z "$ip_cidr" ]; then
echo "Could not extract IP address."
exit 1
fi
# Split IP and CIDR
IP_ADDRESS=$(echo "$ip_cidr" | cut -d'/' -f1)
NETWORK_ADDRESS="$(echo "$IP_ADDRESS" | awk -F'.' '{ print $1"."$2"."$3".255" }')"
NETWORK_SUBNET="$(echo "$IP_ADDRESS" | awk -F'.' '{ print $1"."$2"."$3".0/24" }')"
NETWORK_GATEWAY="$(echo "$IP_ADDRESS" | awk -F'.' '{ print $1"."$2"."$3".1" }')"
# Output the results
echo "IP Address: $IP_ADDRESS"
echo "Network Address: $NETWORK_ADDRESS"
echo "Network Subnet: $NETWORK_SUBNET"
echo "Network Gateway: $NETWORK_GATEWAY"
# Drop all packets by default
$IPT --policy INPUT DROP
$IPT --policy OUTPUT DROP
$IPT --policy FORWARD DROP
# Allow loopback traffic
$IPT -A INPUT -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT
# Allow local WireGuard client communication
$IPT -A INPUT -i $WIREGUARD_INTF -j ACCEPT
$IPT -A OUTPUT -o $WIREGUARD_INTF -j ACCEPT
$IPT -A INPUT -i $ETHINTF -s $WIREGUARD_SUB_NET -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -d $WIREGUARD_SUB_NET -j ACCEPT
# Add logging for dropped packets to aid in troubleshooting and security analysis
$IPT -N LOGGING_DROP
$IPT -A LOGGING_DROP -j LOG --log-prefix "DROP: " --log-level 4
$IPT -A LOGGING_DROP -j DROP
# SSH Service Rules with DDoS Protection on TCP
$IPT -N SSH_PROTECT
$IPT -A INPUT -i $ETHINTF -p tcp --dport $SSHPORT -j SSH_PROTECT
# Rate limit SSH connections to prevent DDoS Attacks and Log Excess Attempts
$IPT -A SSH_PROTECT -i $ETHINTF -m conntrack --ctstate ESTABLISHED -j ACCEPT
$IPT -A SSH_PROTECT -i $ETHINTF -m conntrack --ctstate NEW -m limit --limit 5/min --limit-burst 10 -j ACCEPT
$IPT -A SSH_PROTECT -i $ETHINTF -j LOGGING_DROP
# Allow Outbound response to SSH Requests on TCP
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport $SSHPORT --dport 1024:65535 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
# HTTP/HTTPS Service Rules with DDoS Protection for TCP and UDP
$IPT -N WEB_PROTECT
$IPT -A INPUT -i $ETHINTF -p tcp --dport 80 -j WEB_PROTECT
$IPT -A INPUT -i $ETHINTF -p tcp --dport 443 -j WEB_PROTECT
$IPT -A INPUT -i $ETHINTF -p udp --dport 80 -j WEB_PROTECT
$IPT -A INPUT -i $ETHINTF -p udp --dport 443 -j WEB_PROTECT
# Allow HTTP(S) content delivery for TCP and UDP with connection tracking and limits
$IPT -A WEB_PROTECT -i $ETHINTF -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$IPT -A WEB_PROTECT -i $ETHINTF -m conntrack --ctstate NEW -m limit --limit 200/min --limit-burst 3000 -j ACCEPT
$IPT -A WEB_PROTECT -i $ETHINTF -j LOGGING_DROP
# Allow Outbound response to HTTP(S) Requests on TCP and UDP
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 80 --dport 1024:65535 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 80 --dport 1024:65535 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 443 --dport 1024:65535 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 443 --dport 1024:65535 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
# Allow outbound DNS queries on UDP and TCP for both recursive and iterative queries
$IPT -A OUTPUT -o $ETHINTF -p udp --dport 53 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p tcp --dport 53 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
# Allow response to outbound DNS queries on UDP and TCP
$IPT -A INPUT -i $ETHINTF -p udp --sport 53 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p tcp --sport 53 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
# WireGuard VPN Traffic Rules on UDP with connection tracking
$IPT -A OUTPUT -o $ETHINTF -p udp --dport $WIREGUARD_PORT -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport $WIREGUARD_PORT -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
# IPv6 Support Rules for basic ICMPv6 functionality
$IP6T -A INPUT -i $ETHINTF -p icmpv6 --icmpv6-type echo-request -m limit --limit 1/s -j ACCEPT
$IP6T -A OUTPUT -o $ETHINTF -p icmpv6 --icmpv6-type echo-reply -m limit --limit 1/s -j ACCEPT
$IP6T -A INPUT -i $ETHINTF -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
$IP6T -A OUTPUT -o $ETHINTF -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
# Allow outbound SMTP traffic on TCP with connection tracking
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 1024:65535 --dport $SMTP_PORT \
-m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p tcp --sport $SMTP_PORT --dport 1024:65535 \
-m conntrack --ctstate ESTABLISHED -j ACCEPT
# Enable outgoing DHCP client traffic (required for dynamic IP configuration)
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 68 --dport 67 -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport 67 --dport 68 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
# Allow NTP traffic for time synchronization over UDP and TCP
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 1024:65535 --dport 123 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport 123 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
# Allow outbound HTTP(S) requests and responses for software updates client API integrations over both TCP and UDP
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 1024:65535 --dport 80 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p tcp --sport 80 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 1024:65535 --dport 80 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport 80 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 1024:65535 --dport 8080 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p tcp --sport 8080 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 1024:65535 --dport 8080 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport 8080 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p tcp --sport 1024:65535 --dport 443 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p tcp --sport 443 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
$IPT -A OUTPUT -o $ETHINTF -p udp --sport 1024:65535 --dport 443 -m conntrack \
--ctstate NEW,ESTABLISHED -j ACCEPT
$IPT -A INPUT -i $ETHINTF -p udp --sport 443 --dport 1024:65535 -m conntrack \
--ctstate ESTABLISHED -j ACCEPT
# Allow established and related connections on INPUT
$IPT -A INPUT -i $ETHINTF -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# We will want to allow
# in - NETWORK_SUBNET to '255.255.255.255'
# in - NETWORK_SUBNET to NETWORK_ADDRESS
# in - NETWORK_GATEWAY to '224.0.0.1'
# Allow broadcast traffic (currently allowing in)
$IPT -A INPUT -i $ETHINTF -s $NETWORK_SUBNET -d $NETWORK_ADDRESS -j ACCEPT
# $IPT -A OUTPUT -o $ETHINTF -s $NETWORK_SUBNET -d $NETWORK_ADDRESS -j ACCEPT
# Allow local network multicast traffic in (both just in here)
$IPT -A INPUT -i $ETHINTF -s $NETWORK_GATEWAY -d 224.0.0.1 -j ACCEPT
$IPT -A INPUT -i $ETHINTF -s $NETWORK_SUBNET -d 255.255.255.255 -j ACCEPT
# Allow multicast traffic out (not enabled currently)
# $IPT -A OUTPUT -i $ETHINTF -s 224.0.0.1 -j ACCEPT
# $IPT -A OUTPUT -i $ETHINTF -s 255.255.255.255 -j ACCEPT
# Default send not matched for INPUT and OUTPUT to LOGGING_DROP
$IPT -A INPUT -i $ETHINTF -j LOGGING_DROP
$IPT -A OUTPUT -o $ETHINTF -j LOGGING_DROP
$IPT -A FORWARD -j LOGGING_DROP
# Handle fragmented packets with care
$IPT -A INPUT -i $ETHINTF -f -j DROP
$IPT -A OUTPUT -o $ETHINTF -f -j DROP
# Save iptables rules (optional, adjust path as necessary)
/sbin/iptables-save > /etc/sysconfig/iptables
Create and Use
You will want to paste the text of the script above to a new file and then chmod +x filename.sh
to allow it to execute. After that, you should be able to execute the script to set the rules to protect your host (./filename.sh
). That said, IP Tables ‘settings’ or state, does not persist across reboots normally. You can attempt to manage that with several tools, but due to CrowdSec creating some of the first rules (and us wanting to let it be first), read the next bit.
Use Always along side CrowdSec to Set State on Boot
CrowdSec has a roll-out function that sets its rule chain, and elements that enable IP Tables to block for the CrowdSec lists your LAPI maintains and is subscribed to.
In this, having these rules apply at boot is best after CrowdSec has done its work, and doing a reboot task can be the best method to get around any level of race condition here. Here is a root crontab example that should accomplish this goal (as root, crontab -e
).
@reboot sleep 10 && /root/iptablesconfv28.sh
Conclusion
This update introduces a more secure and adaptable firewall setup, leveraging modern tools like CrowdSec for threat intelligence. The enhanced rules provide better protection against various cyber threats while maintaining performance efficiency. Feel free to share your experiences or ask questions in the comments!