-
Notifications
You must be signed in to change notification settings - Fork 1.6k
exception-policy: add 'reject-both' option #14060
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,8 @@ const char *ExceptionPolicyEnumToString(enum ExceptionPolicy policy, bool is_jso | |
return "reject"; | ||
case EXCEPTION_POLICY_BYPASS_FLOW: | ||
return "bypass"; | ||
case EXCEPTION_POLICY_REJECT_BOTH: | ||
return "reject_both"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should that be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By convention, in the YAML config we use |
||
case EXCEPTION_POLICY_DROP_FLOW: | ||
return is_json ? "drop_flow" : "drop-flow"; | ||
case EXCEPTION_POLICY_DROP_PACKET: | ||
|
@@ -145,8 +147,14 @@ void ExceptionPolicyApply(Packet *p, enum ExceptionPolicy policy, enum PacketDro | |
case EXCEPTION_POLICY_NOT_SET: | ||
break; | ||
case EXCEPTION_POLICY_REJECT: | ||
SCLogDebug("EXCEPTION_POLICY_REJECT"); | ||
PacketDrop(p, ACTION_REJECT, drop_reason); | ||
case EXCEPTION_POLICY_REJECT_BOTH: | ||
if (policy == EXCEPTION_POLICY_REJECT) { | ||
SCLogDebug("EXCEPTION_POLICY_REJECT"); | ||
PacketDrop(p, ACTION_REJECT, drop_reason); | ||
} else { | ||
SCLogDebug("EXCEPTION_POLICY_REJECT_BOTH"); | ||
PacketDrop(p, ACTION_REJECT_BOTH, drop_reason); | ||
} | ||
if (!EngineModeIsIPS()) { | ||
break; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially controversial suggestion below but it is a requirement for our environment so we would still need to patch this in if not accepted. The fallthrough to drop-flow below results in an edge case that can cause this not to work when packet loss is present in the network. When the midstream reject-both is triggered specifically by a retransmitted packet from the server side, the reset that gets sent to the client will not be accepted by the client host because its sequence number is no longer current. This is the scenario:
At very large scale this unlikely sequence of events happens enough to cause a major problem for us. Our solution is effectively to disable the drop-flow behavior by making line 164 below look like this:
This avoids setting the Note that there is a potential to create something like a DDoS reflection/amplification vector with the solution above, if Suricata responds to every unknown packet by generating two resets. The details of our infrastructure makes that not possible for us, but in general it is when Suricata is directly reachable from untrusted sources. So you would probably want to do something like disable that "reset everything" behavior from the untrusted side of the network (presumably There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
How does this happen if the flow is presumably in the established state? If the timeout that is configured very low? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For us it's a failover operation where we see this happen - so a host level hardware failure causes existing flows to be redirected to other redundant Suricata hosts, and when the existing connections land on those new hosts the Suricata process running there doesn't know about them. It's actually the failover operation that makes it likely for one of the packets to be lost too - so while the sequence of events described above is possible in general for other reasons too (process crashing at the right time, etc) it goes from "extremely unlikely" to "likely enough to cause an active problem" in our environment. We do have the ability to migrate connections with their state between hosts in controlled circumstances, but for uncontrolled ones (unplanned host failure) resetting those connections is the next best choice which allows the endpoints to fail fast and establish new connections. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would you expect "persistent TCP resets" (https://redmine.openinfosecfoundation.org/issues/960) to help with this? The idea being that we'll keep sending RST packets as long as we keep receiving more packets. There should perhaps be some kind of backoff mechanism to it, but it could help address the race condition? |
||
|
@@ -204,6 +212,7 @@ static enum ExceptionPolicy PickPacketAction(const char *option, enum ExceptionP | |
case EXCEPTION_POLICY_PASS_PACKET: | ||
break; | ||
case EXCEPTION_POLICY_REJECT: | ||
case EXCEPTION_POLICY_REJECT_BOTH: | ||
break; | ||
case EXCEPTION_POLICY_NOT_SET: | ||
break; | ||
|
@@ -229,6 +238,8 @@ static enum ExceptionPolicy ExceptionPolicyConfigValueParse( | |
policy = EXCEPTION_POLICY_PASS_PACKET; | ||
} else if (strcmp(value_str, "reject") == 0) { | ||
policy = EXCEPTION_POLICY_REJECT; | ||
} else if (strcmp(value_str, "reject-both") == 0) { | ||
policy = EXCEPTION_POLICY_REJECT_BOTH; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Github won't let me comment on the unchanged parts of this file, but a few lines below this on line 248, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to also update docs to mention this (including the suricata.yaml bits) |
||
} else if (strcmp(value_str, "ignore") == 0) { // TODO name? | ||
policy = EXCEPTION_POLICY_NOT_SET; | ||
} else if (strcmp(value_str, "auto") == 0) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks prone to error if we forget to update it. Should we make MAX a part of the enum above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, but that then has as a consequence that all the runtime code paths need to handle the size value in switch statements.