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

Support AdminNetworkPolicy named ports #9254

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,13 @@ image:
# using a local kind cluster.
###############################################################################
E2E_FOCUS ?= "sig-network.*Conformance"
ADMINPOLICY_SUPPORTED_FEATURES ?= "AdminNetworkPolicy,BaselineAdminNetworkPolicy"
ADMINPOLICY_SUPPORTED_FEATURES ?= "AdminNetworkPolicy,AdminNetworkPolicyNamedPorts,BaselineAdminNetworkPolicy,BaselineAdminNetworkPolicyNamedPorts"
ADMINPOLICY_UNSUPPORTED_FEATURES ?= ""
e2e-test:
$(MAKE) -C e2e build
$(MAKE) -C node kind-k8st-setup
KUBECONFIG=$(KIND_KUBECONFIG) ./e2e/bin/k8s/e2e.test -ginkgo.focus=$(E2E_FOCUS)
KUBECONFIG=$(KIND_KUBECONFIG) ./e2e/bin/adminpolicy/e2e.test \
KUBECONFIG=$(KIND_KUBECONFIG) ./e2e/bin/adminpolicy/e2e.test -test.v -debug \
-exempt-features=$(ADMINPOLICY_UNSUPPORTED_FEATURES) \
-supported-features=$(ADMINPOLICY_SUPPORTED_FEATURES)

Expand Down
2 changes: 1 addition & 1 deletion felix/rules/endpoints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ var _ = Describe("Endpoints", func() {
})))
})

It("should render a fully-loaded workload endpoint with EndOfTierPass enabled", func() {
It("should render a fully-loaded workload endpoint with tier defaultAction is Pass", func() {
Expect(renderer.WorkloadEndpointToIptablesChains(
"cali1234",
epMarkMapper,
Expand Down
146 changes: 124 additions & 22 deletions libcalico-go/lib/backend/k8s/conversion/adminnetworkpolicy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,13 +371,17 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
})

It("should drop rules with invalid ports in a k8s AdminNetworkPolicy", func() {
portFoo := "foo"
goodPorts := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
{
PortRange: &adminpolicy.PortRange{Start: 2000, End: 3000},
},
{
NamedPort: &portFoo,
},
}
badPorts := []adminpolicy.AdminNetworkPolicyPort{
{
Expand All @@ -386,6 +390,9 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
{
PortRange: &adminpolicy.PortRange{Start: 1000, End: 10},
},
{
NamedPort: &portFoo,
},
}

anp := adminpolicy.AdminNetworkPolicy{
Expand Down Expand Up @@ -517,8 +524,20 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {

protoTCP := numorstring.ProtocolFromString("TCP")

Expect(len(gnp.Spec.Ingress)).To(Equal(1))
Expect(len(gnp.Spec.Ingress)).To(Equal(2))
Expect(gnp.Spec.Ingress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random ingress rule 2"),
Action: "Pass",
Source: apiv3.EntityRule{
NamespaceSelector: "k10 == 'v10' && k20 == 'v20'",
},
Destination: apiv3.EntityRule{
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random ingress rule 2"),
Action: "Pass",
Expand All @@ -535,8 +554,19 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
},
))

Expect(len(gnp.Spec.Egress)).To(Equal(1))
Expect(len(gnp.Spec.Egress)).To(Equal(2))
Expect(gnp.Spec.Egress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random egress rule"),
Action: "Deny",
Source: apiv3.EntityRule{},
Destination: apiv3.EntityRule{
NamespaceSelector: "k3 == 'v3' && k4 == 'v4'",
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random egress rule"),
Action: "Deny",
Expand Down Expand Up @@ -844,13 +874,17 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
})

It("should parse an AdminNetworkPolicy with multiple peers and ports", func() {
portFoo := "foo"
ports := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
{
PortRange: &adminpolicy.PortRange{Start: 20, End: 30, Protocol: kapiv1.ProtocolUDP},
},
{
NamedPort: &portFoo,
},
}
anp := adminpolicy.AdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -929,35 +963,35 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {

Expect(gnp.Spec.NamespaceSelector).To(Equal("label == 'value' && label2 == 'value2'"))

Expect(len(gnp.Spec.Ingress)).To(Equal(4))
Expect(len(gnp.Spec.Ingress)).To(Equal(6))
Expect(gnp.Spec.Ingress[0].Source.NamespaceSelector).To(Equal("k == 'v'"))
Expect(gnp.Spec.Ingress[0].Destination.Ports).To(Equal([]numorstring.Port{numorstring.SinglePort(80)}))
Expect(gnp.Spec.Ingress[0].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.NamedPort("foo"),
}))

Expect(gnp.Spec.Ingress[1].Source.NamespaceSelector).To(Equal("all()"))
Expect(gnp.Spec.Ingress[1].Source.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && k2 == 'v2'"))
Expect(gnp.Spec.Ingress[1].Destination.Ports).To(Equal([]numorstring.Port{numorstring.SinglePort(80)}))
Expect(gnp.Spec.Ingress[1].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.NamedPort("foo"),
}))

Expect(gnp.Spec.Ingress[2].Source.NamespaceSelector).To(Equal("k == 'v'"))
Expect(gnp.Spec.Ingress[2].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))
Expect(gnp.Spec.Ingress[2].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.SinglePort(80),
}))

Expect(gnp.Spec.Ingress[3].Source.NamespaceSelector).To(Equal("all()"))
Expect(gnp.Spec.Ingress[3].Source.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && k2 == 'v2'"))
Expect(gnp.Spec.Ingress[3].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))

Expect(gnp.Spec.Egress).To(HaveLen(4))
Expect(gnp.Spec.Egress[0].Destination.NamespaceSelector).To(Equal("k3 == 'v3'"))
Expect(gnp.Spec.Egress[0].Destination.Ports).To(Equal([]numorstring.Port{numorstring.SinglePort(80)}))
Expect(gnp.Spec.Ingress[3].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.SinglePort(80),
}))

Expect(gnp.Spec.Egress[1].Destination.NamespaceSelector).To(Equal("k4 == 'v4'"))
Expect(gnp.Spec.Egress[1].Destination.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && k5 == 'v5'"))
Expect(gnp.Spec.Egress[1].Destination.Ports).To(Equal([]numorstring.Port{numorstring.SinglePort(80)}))
Expect(gnp.Spec.Ingress[4].Source.NamespaceSelector).To(Equal("k == 'v'"))
Expect(gnp.Spec.Ingress[4].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))

Expect(gnp.Spec.Egress[2].Destination.NamespaceSelector).To(Equal("k3 == 'v3'"))
Expect(gnp.Spec.Egress[2].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))

Expect(gnp.Spec.Egress[3].Destination.NamespaceSelector).To(Equal("k4 == 'v4'"))
Expect(gnp.Spec.Egress[3].Destination.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && k5 == 'v5'"))
Expect(gnp.Spec.Egress[3].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))
Expect(gnp.Spec.Ingress[5].Source.NamespaceSelector).To(Equal("all()"))
Expect(gnp.Spec.Ingress[5].Source.Selector).To(Equal("projectcalico.org/orchestrator == 'k8s' && k2 == 'v2'"))
Expect(gnp.Spec.Ingress[5].Destination.Ports).To(Equal([]numorstring.Port{{MinPort: 20, MaxPort: 30}}))

// Check that Types field exists and has only 'ingress'
Expect(len(gnp.Spec.Types)).To(Equal(2))
Expand Down Expand Up @@ -1413,15 +1447,23 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
})

It("should replace an unsupported AdminNeworkPolicy rule with Deny action with a deny-all one", func() {
portHTTP := "http"
ports := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
{
NamedPort: &portHTTP,
},
}
portFoo := "foo"
badPorts := []adminpolicy.AdminNetworkPolicyPort{
{
PortRange: &adminpolicy.PortRange{Start: 40, End: 20, Protocol: kapiv1.ProtocolUDP},
},
{
NamedPort: &portFoo,
},
}
anp := adminpolicy.AdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -1569,13 +1611,23 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
Expect(gnp.Spec.Egress[0].Destination.NamespaceSelector).To(BeZero())
Expect(gnp.Spec.Egress[0]).To(Equal(apiv3.Rule{Action: apiv3.Deny}))

Expect(gnp.Spec.Egress).To(HaveLen(2))
Expect(gnp.Spec.Egress).To(HaveLen(3))
Expect(gnp.Spec.Egress[0].Destination.NamespaceSelector).To(BeZero())
Expect(gnp.Spec.Egress[0]).To(Equal(apiv3.Rule{Action: apiv3.Deny}))

Expect(gnp.Spec.Egress[1].Destination.NamespaceSelector).To(Equal("k4 == 'v4'"))
Expect(gnp.Spec.Egress[1].Destination.Selector).To(BeZero())
Expect(gnp.Spec.Egress[1].Destination.Ports).To(Equal([]numorstring.Port{numorstring.SinglePort(80)}))
Expect(gnp.Spec.Egress[1].Protocol).To(BeNil())
Expect(gnp.Spec.Egress[1].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.NamedPort("http"),
}))

Expect(gnp.Spec.Egress[2].Destination.NamespaceSelector).To(Equal("k4 == 'v4'"))
Expect(gnp.Spec.Egress[2].Destination.Selector).To(BeZero())
Expect(*gnp.Spec.Egress[2].Protocol).To(Equal(numorstring.ProtocolFromString(numorstring.ProtocolTCP)))
Expect(gnp.Spec.Egress[2].Destination.Ports).To(Equal([]numorstring.Port{
numorstring.SinglePort(80),
}))

// Check that Types field exists and has only 'ingress'
Expect(len(gnp.Spec.Types)).To(Equal(2))
Expand Down Expand Up @@ -1748,10 +1800,14 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
})

It("should parse a k8s AdminNetworkPolicy with a Networks peer and ports", func() {
portFoo := "foo"
ports := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
{
NamedPort: &portFoo,
},
}
anp := adminpolicy.AdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -1811,6 +1867,16 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {

protocolTCP := numorstring.ProtocolFromString(numorstring.ProtocolTCP)
Expect(gnp.Spec.Ingress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random ingress rule"),
Action: "Pass",
Source: apiv3.EntityRule{NamespaceSelector: "k == 'v'"},
Destination: apiv3.EntityRule{
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random ingress rule"),
Action: "Pass",
Expand All @@ -1824,6 +1890,28 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
},
))
Expect(gnp.Spec.Egress).To(ConsistOf(
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random egress rule"),
Action: "Deny",
Source: apiv3.EntityRule{},
Destination: apiv3.EntityRule{
NamespaceSelector: "k3 == 'v3'",
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random egress rule"),
Action: "Deny",
Source: apiv3.EntityRule{},
Destination: apiv3.EntityRule{
Nets: []string{"10.10.10.0/24", "1.1.1.1/32"},
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random egress rule"),
Action: "Deny",
Expand Down Expand Up @@ -1852,10 +1940,14 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
})

It("should parse a k8s AdminNetworkPolicy with an invalid networks peer", func() {
portFoo := "foo"
ports := []adminpolicy.AdminNetworkPolicyPort{
{
PortNumber: &adminpolicy.Port{Port: 80},
},
{
NamedPort: &portFoo,
},
}
anp := adminpolicy.AdminNetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -1952,6 +2044,16 @@ var _ = Describe("Test AdminNetworkPolicy conversion", func() {
},
},
},
apiv3.Rule{
Metadata: k8sAdminNetworkPolicyToCalicoMetadata("A random ingress rule"),
Action: "Pass",
Source: apiv3.EntityRule{NamespaceSelector: "k == 'v'"},
Destination: apiv3.EntityRule{
Ports: []numorstring.Port{
numorstring.NamedPort("foo"),
},
},
},
))
Expect(gnp.Spec.Egress).To(ConsistOf(
apiv3.Rule{
Expand Down
25 changes: 24 additions & 1 deletion libcalico-go/lib/backend/k8s/conversion/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,12 @@ func unpackANPPorts(k8sPorts *[]adminpolicy.AdminNetworkPolicyPort) (map[string]
break
}

// Named ports do not have protocol
/*if protocol == nil {
protocolPorts[""] = []numorstring.Port{*calicoPort}
continue
}*/

pStr := protocol.String()
// treat nil as 'all ports'
if calicoPort == nil {
Expand Down Expand Up @@ -754,7 +760,16 @@ func k8sAdminPolicyPortToCalicoFields(port *adminpolicy.AdminNetworkPolicyPort)
protocol = k8sProtocolToCalico(&proto)
return
}
// TODO: Add support for NamedPorts
if port.NamedPort != nil {
// For named ports, protocol is nil.
dstPort, err = k8sAdminPolicyNamedPortToCalico(*port.NamedPort)
if err != nil {
return
}
proto := numorstring.ProtocolFromString(numorstring.ProtocolTCP)
protocol = &proto
return
}
return
}

Expand Down Expand Up @@ -784,6 +799,14 @@ func k8sAdminPolicyPortRangeToCalico(port *adminpolicy.PortRange) (*numorstring.
return &p, nil
}

func k8sAdminPolicyNamedPortToCalico(port string) (*numorstring.Port, error) {
if port == "" {
return nil, fmt.Errorf("empty named port")
}
p, err := numorstring.PortFromString(port)
return &p, err
}

// K8sNetworkPolicyToCalico converts a k8s NetworkPolicy to a model.KVPair.
func (c converter) K8sNetworkPolicyToCalico(np *networkingv1.NetworkPolicy) (*model.KVPair, error) {
// Pull out important fields.
Expand Down
Loading