Skip to content
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

Handle overlaps in except and allow CIDRs #344

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

Pavani-Panakanti
Copy link
Contributor

@Pavani-Panakanti Pavani-Panakanti commented Dec 6, 2024

Issue #, if available:
If there is an overlap of IPs in an allow rule and in an except block of other rule, we do not handle the merging

Description of changes:

Example:

egress:
  - to:
      - ipBlock:
          cidr: 192.168.0.0/16
    ports:
      - port: 3306
  - to:
      - ipBlock:
          cidr: 0.0.0.0/0
          except:
            - 192.168.0.0/16

Before this change we evaluate this as

  • 0.0.0.0/0 - startPort:0 endPort: 0
  • except block - 192.168.0.0/16 - startPort: 0 endPort:0
  • 192.168.0.0/16 - allow on port 3306, as this is part of 0.0.0.0/0 allow on all ports. Append previous ports to this new L4 info. Final L4 ports info looks as below
    • startPort: 3306, endPort: 0 - allow
    • startPort:0, endPort: 0 - allow
    • startPort:0, endPort:0 - deny

The correct behavior for 192.168.0.0/16 should be allow on 3306 as explicit allow takes precedence and deny on rest all ports due to except
New evaluation logic - all except blocks are handled at end

  • 0.0.0.0/0 - startPort:0 endPort: 0
  • 192.168.0.0/16 - allow on port 3306. This is part of 0.0.0.0/0, so check if it is part of any except under this cidr. If yes do not add ports of 0.0.0.0/0 to 192.168.0.0/16. If no, add ports. In this case it is part of except so do not add it
  • Handle all except at end. For every except key, check if entry is already present in cidrsMap. If yes, there is some explicit allow and everything else will be default deny so no change required. If no, we need to add deny all entry to make sure we deny all traffic and do not allow it if it is part of some other superset CIDR

Other Changes in the PR:

  • Code refactoring to make it simpler and easy to follow
    • catch all need not be handled separately. We evaluate rules in the order of prefix length, so catch all will be handled first followed by others
  • If no protocol specified, it should be allow any protocol. Changed default to any_ip_protocol from tcp
  • For any_ip_protocol, check start and end ports for traffic evaluation. Before fix if it is any_protocol we allow on all ports
    • example:
      • Port:53 → This should be evaluated as allow any_protocol on port 53

Testing done with multiple cases of except overlaps

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@Pavani-Panakanti Pavani-Panakanti requested a review from a team as a code owner December 6, 2024 01:25
@jayanthvn
Copy link
Contributor

Can you also add a test case for this in our integration suite?

@@ -856,10 +857,7 @@ func mergeDuplicateL4Info(ports []v1alpha1.Port) []v1alpha1.Port {
func (l *bpfClient) computeMapEntriesFromEndpointRules(firewallRules []EbpfFirewallRules) (map[string][]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an enhancement, could you document the behavior of this function as function doc string itself. We pack a lot inside the function here.

@@ -201,7 +201,8 @@ int handle_egress(struct __sk_buff *skb)
return BPF_DROP;
}

if ((trie_val->protocol == ANY_IP_PROTOCOL) || (trie_val->protocol == ip->protocol &&
if ((trie_val->protocol == ANY_IP_PROTOCOL && ((trie_val->start_port == ANY_PORT) || (l4_dst_port == trie_val->start_port) ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enhancement Request: We can format this and explain the behavior of in the comment.
a) match any problem and ensure either destination port matches or falls within the range.

@@ -175,31 +175,18 @@ func TestBpfClient_IsEBPFProbeAttached(t *testing.T) {
}
}

func TestBpfClient_CheckAndDeriveCatchAllIPPorts(t *testing.T) {
func TestBpfClient_CheckAndDeriveL4InfoFromAnyMatchingCIDRs(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need more test cases here that will cover for the scenarios described in the description of the PR are handled by the test case.

192.168.0.0/16 - allow on port 3306. This is part of 0.0.0.0/0, so check if it is part of any except under this cidr. If yes do not add ports of 0.0.0.0/0 to 192.168.0.0/16. If no, add ports. 

Handle all except at end. For every except key, check if entry is already present in cidrsMap. If yes, there is some explicit allow and everything else will be default deny so no change required. If no, we need to add deny all entry to make sure we deny all traffic and do not allow it if it is part of some other superset CIDR

The code change and the logic looks good to me. As noted in the review of the design doc, I wanted to see if there is any simpler way, but handling all except at the end looks alright.

Coverage with test case can help us push this PR to finish line here. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants