4

This is a two part question, really. Keep in mind that I am a developer not a system admin, but being the only employee in the company, I wear ALL the hats.

I have deployed my server with two firewalls running on CARP for load balancing/redundancy plus about 40 computers for database and other backend application needs. As a start up I want to save some money by mitigating damages of a DDoS attack without paying my ISP for a business dedicated internet on top of DDoS protection. I KNOW YOU CAN'T TOTALLY protect against DDoS. I just want to mitigate damages until my App starts making money and then I can let the ISP deal with the headaches.

In that spirit, I was wondering if anybody ever implemented a solution of where a script (maybe through cron) would change the PF rules based on current usage. For example, if there are too many half open connections from millions of IP addresses I would like to tell PF to go into SYN-Cookies mode and then when the attack is over (or some time has passed) to go back to normal.

I cannot use Cloudfare because I am running a backend for an App and 99% of the content is not static. I could do cloudfare for the website of the app but that's about it.

To reiterate, money IS AN ISSUE. I am currently using FIOS Business and Verizon will not provide DDoS protection on that type of line.

Last thing, has anybody experienced drastic issues after enabling SYN-COOKIES/SYN-PROXYING. Give me real story. Please.

PS I do not want to start a debate about SYN-PROXYING vs SYN-COOKIES!

Miguel
  • 171
  • 4
  • Mitigate a DoS is a myth IMO, as the traffic hit down to your modem in all cases. Getting a highter ISP feed could help, or hosting on the cloud a backup solution. – yagmoth555 Nov 18 '18 at 15:32
  • I can easily do SYN-COOKIES and mitigate the majority of the easy ones..... – Miguel Nov 18 '18 at 15:35
  • Could a pfsense in the front be a solution? http://pfsensesetup.com/syn-flood-prevention-in-pfsense/ – marsh-wiggle Nov 18 '18 at 17:07

1 Answers1

3

Since no one has answered my question this is the pf configuration I ended up with. Specifically look at the line for setting cookies after the table is filled with 15% open tcp connections. SYN Cookies provide a great bang for your buck when it comes to preventing too many half open connections. This is not final however. Another thing that you can do in your own environment is limit the amount of icmp on the external interface to say, 1000/second. This is something I still have to play with. Just don't limit it inside your network as this can have devastating effects.

the bogons table and USA tables get updated frequently. Every 4 and 24 hours respectively. THIS IS a must as there are many IP addresses, especially ipv6, that are not in use today. So why bother even handling them. plus, there is no need for anyone outside the USA to access over SSH.

There is also a couple of brute-force attempts table that my applications update from the outside and the ssh one gets updated within pf. Just in case someone tries to attempt too many connections at once they are obviously trying something shady.

This wasn't my final configuration, as I did load balancing for my web servers and other things but didn't want to post too much unnecessary info.

last note, always try running carp for failover/redundancy/load balancing!!

# Macros
udp_services = "{ domain, ntp }"
tcp_services = "{ ssh, smtp, domain, www, pop3, auth, https, pop3s }"
carpdevs = "carp0"
ext_if = "em0"
int_if = "{ em1, em2, em3, em4, em5 }"
localnet = "{ em1:network, em2:network, em3:network, em4:network, em5:network}"

table <USA_IP_ADDRESSES> persist file "/etc/usa_all_ip.zone"
table <dhcpd_leased_ips> persist
table <dhcpd_abandoned_ips> persist
table <dhcpd_changed_ips> persist
table <bruteforce> persist
table <bogons> persist file "/etc/bogons" # see cron jobs for when this file gets updated (daily) and from where
table <lan> const { 192.168.1.0/24, 192.168.2.0/24, 192.168.3.0/24, 192.168.4.0/24, 192.168.5.0 }
table <webservers> persist { 192.168.4.10, 192.168.4.20 }
table <websiteoverload> persist

table <appoverload> persist
table <mysmateservers> persist

# Global settings
set debug warning
set limit { states 10000000, table-entries 1000000}
set optimization aggressive
set syncookies adaptive (start 15%, end 8%)
set timeout tcp.established 1800
set skip on lo
set loginterface em0

# Default everything coming in + antispoof
antispoof for $ext_if inet
antispoof for $int_if inet
antispoof for carp0 inet

block drop all
block drop in quick on $ext_if from { <bogons>, <bruteforce> }

# Allow traffic from all our CARP devices
pass on $carpdevs proto carp

# Allow connections between all of our intrefaces
pass from { self, $localnet }
#block drop in on $ext_if from carp0:network
# block all outgoing traffic through em0 coming from em2-em5 and don't bother testing other rules
block return out on egress proto { tcp, udp} from 192.168.0.0/16

# pass out clock synchronization
pass out on egress proto udp from 192.168.0.0/16 to port ntp

# pass out dns look up
pass out on egress proto { udp, tcp } from 192.168.0.0/16 to port domain

pass out on egress from 192.168.1.0/24
pass out on egress from 192.168.5.0/24
pass out log on egress from 192.168.2.0/24

match out on $ext_if inet from 192.168.0.0/16 nat-to (carp0) # NAT, match IPv4 only

# ICMP related rules -- See The PF Book Chapter 3
icmp_types = "{ echoreq, unreach }"
icmp6_types = "{ echoreq unreach timex paramprob }"
pass inet proto icmp icmp-type $icmp_types #max-pkt-rate 100/1
pass inet6 proto icmp6 icmp6-type $icmp6_types # max-pkt-rate 100/1

# Allow default port range for traceroute(8) 64*3 - Reference: The PF Book Chapter 3
pass out on egress inet proto udp to port 33433:33626 # For IPv4
pass out on egress inet6 proto udp to port 33433:33626 # For IPv6

# Allow incoming SSH connections from US IP addresses only
pass in log on $ext_if proto tcp from <USA_IP_ADDRESSES> to port ssh modulate state \
        (max-src-conn 50, max-src-conn-rate 10/1, overload <bruteforce> flush global)

# Allow incoming traffic on http,https and forward it to our web_server computer
pass in log on $ext_if proto tcp from <USA_IP_ADDRESSES> to port https rdr-to 192.168.5.5 keep state \
        (max-src-conn-rate 100/1, overload <websiteoverload>)

pass in log on $ext_if proto tcp from <USA_IP_ADDRESSES> to port 58777 rdr-to <mysmateservers> least-states modulate state \
        (max-src-conn-rate 1000/1, overload <appoverload>)

# By default, do not permit remote connections to X11
block return in on ! lo0 proto tcp to port 6000:6010

# Port build user does not need network
block return out proto {tcp udp} user _pbuild
Miguel
  • 171
  • 4