Prevent state table DoS: How to properly use "maximum state entries per host"?



  • Hello!

    I use pfSense in a virtual environment (VMware ESXi). It provides routing and firewall functionality for virtual hosts, connected to one external network.

    I'd like to prevent single outside (ab)users from being able to incapacitate pfSense by filling up the state table with a connection flood.

    I figured the "Maximum state entries per host" setting should be suitable for that? Unfortunately, the documentation is not completely clear about some points there.

    First question: What exactly is meant by "per host" here. Is it source host (user from the Internet)? Or destination host (as in one of my server VMs)? Does it refer to ANY states, like "established", "close_wait", "fin_wait" etc.? Or only "established"?

    When I set Max State Entries to say 100 for a PASS rule, does that mean this rule only applies as long as that specific source host does not have more than 100 entries of any kind in the state table? Otherwise the rule is ignored?

    Thanks in advance for information!



  • "Maximum state entries per host" means the source IP as far as I know. S0 if one source IP has 100 connections open - no matter to which destination - pfsense will not accept any other connections. So one source could only have x states in the firewall. So probably the 2established2 connections will be counted. You cannot limit the client to make TCP-SYN attempts but these attempts will be blocked until the state count decreases.

    The direction depends on the rule and where you placed it. I am using this for rules which are accessable from the wan for my OpenVPN servers and I have such state entries for outgoing connections from LAN to WAN to limit connections to 5.000 per host. I had some guys on the LAN which did port scans and this increased my state table to the limit. The Maximum state entries per host fixed that.

    The rule is always there and will always work. It just adds the feature of the Maximum state entries per host limit.



  • Hello!

    Thanks for your reply, Nachtfalke! A few addon questions if I may. :)

    If only the ESTABLISHED connections are counted, what would be the separate setting "Maximum number of established connections per host" be for?

    An external host can fill up the state table also with SYN_RECV or CLOSE_WAIT states, that's why I figured all states should be counted?

    So if I have this "maximum state entries" in a PASS rule, and the host will go above that limit, the host will be blocked? Even without a BLOCK/REJECT rule? I was rather thinking that the rule counts as "does not match" if the host exceeds the maximum states? Since kinda all the other settings in a rule are of the "rule only applies if all these match" type.

    I used the "max state entries" in a rule for the WAN interface, with source being "ANY" and destination being my server VMs on the LAN side (on specific ports).

    What exactly do you mean with "I cannot limit the client to make TCP-SYN attempts, but they will be blocked until the state count decreases"? If they're blocked, they don't create a state table entry, thus I AM limiting their TCP-SYN attempts? My aim is to prevent the state table from getting flooded by single external evildoers, no matter if they produce lots of SYN_RECV or ESTABLISHED or CLOSE_WAIT or other states. :)



  • @Locutus:

    Hello!

    Thanks for your reply, Nachtfalke! A few addon questions if I may. :)

    If only the ESTABLISHED connections are counted, what would be the separate setting "Maximum number of established connections per host" be for?

    An external host can fill up the state table also with SYN_RECV or CLOSE_WAIT states, that's why I figured all states should be counted?

    So if I have this "maximum state entries" in a PASS rule, and the host will go above that limit, the host will be blocked? Even without a BLOCK/REJECT rule? I was rather thinking that the rule counts as "does not match" if the host exceeds the maximum states? Since kinda all the other settings in a rule are of the "rule only applies if all these match" type.

    I used the "max state entries" in a rule for the WAN interface, with source being "ANY" and destination being my server VMs on the LAN side (on specific ports).

    What exactly do you mean with "I cannot limit the client to make TCP-SYN attempts, but they will be blocked until the state count decreases"? If they're blocked, they don't create a state table entry, thus I AM limiting their TCP-SYN attempts? My aim is to prevent the state table from getting flooded by single external evildoers, no matter if they produce lots of SYN_RECV or ESTABLISHED or CLOSE_WAIT or other states. :)

    Your conclusion is right. "Max state entries per host" counts everything. If the limit is reached no further connestions/states will be added until other connections for this host timeout or will be closed. So this would probably prevent your host table to be flooded by this unique host. So only everything above the Max states per host limit will be blocked, the rest will be allowed. So it is more a "limiter" and not a block rule in general which cuts every traffic if one host reached the limit once. So if we are talking about a limit of 1000 then all connections <=1000 will work and pass and all connections >1000 will be blocked.

    There is another option which seems to count only ESTABLISHED connections - your are right.



  • Okay, thanks again!

    One question remains, did I understand you right here? This "Max state entries" setting has, although it's attached to one rule, global effect per host?

    Like, as opposed to other settings in a rule, which only dictate when this one rule applies and when it is skipped, the "Max state entries" will cause pfSense to not add further state entries for that host globally, also e.g. on other destination ports, even if another rule before the Max-entries one would allow that other traffic?

    If so, I think there should be a clear indication that this setting has global effect, no matter what rule it is attached to.



  • Hmm,

    I this is something I cannot say definitly and I did not test this. I would say it depends only for this specific rule. Because If there is another rule before this rule which does not have limit it allows connections. And the rule which has this state limit will check the states and then decide if a new connection should be allowed or not.

    So I would say: It is not global over all other rules.



  • Okidoki, that's what my theory would have been too!

    So I don't need an additional DENY rule after the PASS one with the "maximum states" set? I would imagine that the "maximum states" just causes the rule to "not apply" if the number of states is exceeded, and the firewall proceed with the next rule.

    Okay if I have a default DENY (which I do), and no further rule after the not applying "maximum states" one would allow the traffic, the default would become effective.

    But say I have multiple rules that allow certain traffic, I put "maximum states" on the first one, and the number of states is exceeded, then the second allow rule would become effective, wouldn't it? In which case I'd need a DENY rule in between the two ALLOW ones, matching the parameters of the first rule except for the "maximum states" setting?



  • You do not need any additional DENY rule.

    Give it a try. Put a single rule allowing all traffic and ports with one specific source destination IP.
    Then start a port scan with nmap to a server on the internet and check the state table of pfsense.
    Filte the states depending on the source IP address from which you do the port scan and check the number of connections.

    then place a limit on the same rule for lets say 100 connections and do again a nmap port scan and you will see that this will absolutly slow down tzhe port scan process.



  • Thanks again Nachtfalke! I tried it now, and it indeed works as advertised. :)


Log in to reply