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

treat empty NetworkPolicyPort as "all ports on TCP" during parsing #14720

Merged
merged 1 commit into from
Apr 21, 2021

Conversation

mattfenwick
Copy link
Contributor

@mattfenwick mattfenwick commented Jan 25, 2021

Fixes: #14678

Treat empty NetworkPolicyPort as "all ports on TCP" during network policy parsing

Test plan

cd cilium

pushd pkg/k8s && go test
popd

pushd pkg/policy && go test
popd

pushd pkg/policy/api && go test
popd

@mattfenwick mattfenwick requested review from a team and nathanjsweet January 25, 2021 12:54
@maintainer-s-little-helper
Copy link

Commit d7bd86fe0b03a390feec04bbf1ab44e2a908b045 does not contain "Signed-off-by".

Please follow instructions provided in https://docs.cilium.io/en/stable/contributing/development/contributing_guide/#developer-s-certificate-of-origin

@maintainer-s-little-helper maintainer-s-little-helper bot added the dont-merge/needs-sign-off The author needs to add signoff to their commits before merge. label Jan 25, 2021
@maintainer-s-little-helper maintainer-s-little-helper bot added the dont-merge/needs-release-note-label The author needs to describe the release impact of these changes. label Jan 25, 2021
@maintainer-s-little-helper maintainer-s-little-helper bot added this to In progress in 1.10.0 Jan 25, 2021
@maintainer-s-little-helper maintainer-s-little-helper bot removed the dont-merge/needs-sign-off The author needs to add signoff to their commits before merge. label Jan 25, 2021
@mattfenwick
Copy link
Contributor Author

mattfenwick commented Jan 25, 2021

👋 wanted to put this up for preliminary feedback even though it's failing some tests!

I think I'm running into this at pkg/policy/api/rule_validation.go:388:

func (pp *PortProtocol) Sanitize() error {
	if pp.Port == "" {
		return fmt.Errorf("Port must be specified")
	}

	// Port names are formatted as IANA Service Names.  This means that
	// some legal numeric literals are no longer considered numbers, e.g,
	// 0x10 is now considered a name rather than number 16.
	if iana.IsSvcName(pp.Port) {
		pp.Port = strings.ToLower(pp.Port) // Normalize for case insensitive comparison
	} else {
		p, err := strconv.ParseUint(pp.Port, 0, 16)
		if err != nil {
			return fmt.Errorf("Unable to parse port: %s", err)
		}
		if p == 0 {
			return fmt.Errorf("Port cannot be 0")
		}
	}

	var err error
	pp.Protocol, err = ParseL4Proto(string(pp.Protocol))
	if err != nil {
		return err
	}

	return nil
}

What's the best way to handle that? Making this change:

--- a/pkg/policy/api/rule_validation.go
+++ b/pkg/policy/api/rule_validation.go
@@ -380,13 +380,10 @@ func (pp *PortProtocol) Sanitize() error {
        if iana.IsSvcName(pp.Port) {
                pp.Port = strings.ToLower(pp.Port) // Normalize for case insensitive comparison
        } else {
-               p, err := strconv.ParseUint(pp.Port, 0, 16)
+               _, err := strconv.ParseUint(pp.Port, 0, 16)
                if err != nil {
                        return fmt.Errorf("Unable to parse port: %s", err)
                }
-               if p == 0 {
-                       return fmt.Errorf("Port cannot be 0")
-               }
        }

gets the tests to pass, but not sure if that would have other bad downstream consequences?

@twpayne twpayne self-requested a review January 25, 2021 17:20
@twpayne twpayne added the release-note/bug This PR fixes an issue in a previous release of Cilium. label Jan 25, 2021
@maintainer-s-little-helper maintainer-s-little-helper bot removed the dont-merge/needs-release-note-label The author needs to describe the release impact of these changes. label Jan 25, 2021
Copy link
Member

@aanm aanm left a comment

Choose a reason for hiding this comment

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

@jrajahalme do you mind taking a look. The port number is optional in a k8s network policy so we it can be 0. To me the proposed fixed by @mattfenwick makes sense.

@aanm aanm requested review from jrajahalme and removed request for nathanjsweet January 30, 2021 15:13
@aanm aanm assigned jrajahalme and unassigned nathanjsweet and jrajahalme Jan 30, 2021
@aanm aanm added the sig/policy Impacts whether traffic is allowed or denied based on user-defined policies. label Jan 30, 2021
@joestringer joestringer moved this from Done to In progress in 1.10.0 Feb 12, 2021
@jrajahalme
Copy link
Member

gets the tests to pass, but not sure if that would have other bad downstream consequences?

Removing the zero test seems ok, my only concern is that L7 policies can only be applied if a port number has been specified. Would be good to add a test case with a port with default values and make sure it is rejected. Policy could be like this:

...
    toPorts:
    - ports:
      - {}
      rules:
        http:
        - method: "POST"
...

To be on the safe side this should be rejected by rule validation, maybe like this:

	// Sanitize L7 rules
	if !pr.Rules.IsEmpty() {
+		// Check if any of the ports was zero ("0"), maybe via a return value from 'pr.Ports[i].Sanitize()'
+		If haveZeroPort {
+			return fmt.Errorf("Port cannot be 0")
+		}
		if err := pr.Rules.sanitize(pr.Ports); err != nil {
			return err
		}
	}

@joestringer
Copy link
Member

Marking as "draft" while feedback is addressed. If you would like clarification on the feedback, feel free to respond and ping the person who provided the feedback 🙂 When you would like additional review, scroll to the bottom of the page and click the "Ready for review" button.

@joestringer joestringer marked this pull request as draft February 24, 2021 22:37
@mattfenwick
Copy link
Contributor Author

Marking as "draft" while feedback is addressed. If you would like clarification on the feedback, feel free to respond and ping the person who provided the feedback 🙂 When you would like additional review, scroll to the bottom of the page and click the "Ready for review" button.

Thanks for the reminder! 😄

@mattfenwick
Copy link
Contributor Author

gets the tests to pass, but not sure if that would have other bad downstream consequences?

Removing the zero test seems ok, my only concern is that L7 policies can only be applied if a port number has been specified. Would be good to add a test case with a port with default values and make sure it is rejected. Policy could be like this:

...
    toPorts:
    - ports:
      - {}
      rules:
        http:
        - method: "POST"
...

To be on the safe side this should be rejected by rule validation, maybe like this:

	// Sanitize L7 rules
	if !pr.Rules.IsEmpty() {
+		// Check if any of the ports was zero ("0"), maybe via a return value from 'pr.Ports[i].Sanitize()'
+		If haveZeroPort {
+			return fmt.Errorf("Port cannot be 0")
+		}
		if err := pr.Rules.sanitize(pr.Ports); err != nil {
			return err
		}
	}

thanks for the tip @jrajahalme !

I pushed an update to add an L7, but there's still something not quite right and one of the tests in the pkg/policy/api package is now failing.

I think the problem is I'm not separating the code paths for validation for L3/L4 from L7, can you take a look?

@mattfenwick mattfenwick marked this pull request as ready for review April 20, 2021 22:58
@mattfenwick mattfenwick requested a review from a team as a code owner April 20, 2021 22:58
@mattfenwick mattfenwick requested a review from rolinh April 20, 2021 22:58
@jrajahalme
Copy link
Member

The change I proposed above was a bit too brittle, as it would allow port values such as "00" pass the new check. It's better to do by tracking the parsed zero values in PortProtocol validation and return a boolean telling if the port was zero:

diff --git a/pkg/policy/api/rule_validation.go b/pkg/policy/api/rule_validation.go
index 695de01559..d4c8778efb 100644
--- a/pkg/policy/api/rule_validation.go
+++ b/pkg/policy/api/rule_validation.go
@@ -338,14 +338,20 @@ func (pr *L7Rules) sanitize(ports []PortProtocol) error {
 }
 
 func (pr *PortRule) sanitize(ingress bool) error {
+	haveZeroPort := false
+
 	if len(pr.Ports) > maxPorts {
 		return fmt.Errorf("too many ports, the max is %d", maxPorts)
 	}
 	for i := range pr.Ports {
-		if err := pr.Ports[i].Sanitize(); err != nil {
+		var isZero bool
+		var err error
+		if isZero, err = pr.Ports[i].sanitize(); err != nil {
 			return err
 		}
-
+		if isZero {
+			haveZeroPort = true
+		}
 		hasDNSRules := pr.Rules != nil && len(pr.Rules.DNS) > 0
 		// DNS L7 rules can be TCP, UDP or ANY, all others are TCP only.
 		switch {
@@ -362,11 +368,10 @@ func (pr *PortRule) sanitize(ingress bool) error {
 
 	// Sanitize L7 rules
 	if !pr.Rules.IsEmpty() {
-		for _, pp := range pr.Ports {
-			if err := pp.Sanitize(); err != nil {
-				return err
-			}
+		if haveZeroPort {
+			return fmt.Errorf("L7 rules can not be used when a port is 0")
 		}
+
 		if err := pr.Rules.sanitize(pr.Ports); err != nil {
 			return err
 		}
@@ -374,9 +379,9 @@ func (pr *PortRule) sanitize(ingress bool) error {
 	return nil
 }
 
-func (pp *PortProtocol) Sanitize() error {
+func (pp *PortProtocol) sanitize() (isZero bool, err error) {
 	if pp.Port == "" {
-		return fmt.Errorf("Port must be specified")
+		return isZero, fmt.Errorf("Port must be specified")
 	}
 
 	// Port names are formatted as IANA Service Names.  This means that
@@ -385,19 +390,15 @@ func (pp *PortProtocol) Sanitize() error {
 	if iana.IsSvcName(pp.Port) {
 		pp.Port = strings.ToLower(pp.Port) // Normalize for case insensitive comparison
 	} else {
-		_, err := strconv.ParseUint(pp.Port, 0, 16)
+		p, err := strconv.ParseUint(pp.Port, 0, 16)
 		if err != nil {
-			return fmt.Errorf("Unable to parse port: %s", err)
+			return isZero, fmt.Errorf("Unable to parse port: %s", err)
 		}
+		isZero = p == 0
 	}
 
-	var err error
 	pp.Protocol, err = ParseL4Proto(string(pp.Protocol))
-	if err != nil {
-		return err
-	}
-
-	return nil
+	return isZero, err
 }
 
 // sanitize the given CIDR. If successful, returns the prefixLength specified

@jrajahalme
Copy link
Member

@mattfenwick Sorry for the churn, I was kind of hoping I could have sent the latter comment above before you do any additional work on this! Anyway, there is a new diff w.r.t to your PR before the changes I suggested a bit earlier.

Fixes cilium#14678

Signed-off-by: Matt Fenwick <mfenwick100@gmail.com>
@mattfenwick
Copy link
Contributor Author

@mattfenwick Sorry for the churn, I was kind of hoping I could have sent the latter comment above before you do any additional work on this! Anyway, there is a new diff w.r.t to your PR before the changes I suggested a bit earlier.

np 😄 applied the diff and updated!

@twpayne
Copy link
Contributor

twpayne commented Apr 21, 2021

test-me-please

@twpayne
Copy link
Contributor

twpayne commented Apr 21, 2021

The runtime tests all failed, which looks like a flake: https://jenkins.cilium.io/job/Cilium-PR-Runtime-4.9/4480/

I'll re-trigger the tests.

@twpayne
Copy link
Contributor

twpayne commented Apr 21, 2021

retest-runtime

@twpayne
Copy link
Contributor

twpayne commented Apr 21, 2021

test-runtime

@aanm
Copy link
Member

aanm commented Apr 21, 2021

test-1.21-4.9 (previous failure https://jenkins.cilium.io/job/Cilium-PR-K8s-1.21-kernel-4.9/271/)

Copy link
Contributor

@twpayne twpayne left a comment

Choose a reason for hiding this comment

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

LGTM, thanks so much for the fix :)

@christarazi christarazi added the ready-to-merge This PR has passed all tests and received consensus from code owners to merge. label Apr 21, 2021
@christarazi
Copy link
Member

Merging as we have approval and CI is all green.

@christarazi christarazi merged commit d808afd into cilium:master Apr 21, 2021
1.10.0 automation moved this from In progress to Done Apr 21, 2021
@mattfenwick mattfenwick deleted the issue-14678 branch April 21, 2021 22:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready-to-merge This PR has passed all tests and received consensus from code owners to merge. release-note/bug This PR fixes an issue in a previous release of Cilium. sig/policy Impacts whether traffic is allowed or denied based on user-defined policies.
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Egress network policy allows traffic that should be denied
8 participants