Skip to content

Commit

Permalink
test: add ginkgo default-allow tests
Browse files Browse the repository at this point in the history
Add some tests that create various mixtures of default-allow and
default-deny policies. It is important that default-deny policies always
take precedence over default-allow. It is also important that Deny rules
take precedence over default-allow.

These need to be integration tests, since they rely on specific
interactions between the userspace and bpf policy engines.

Signed-off-by: Casey Callendrello <cdc@isovalent.com>
  • Loading branch information
squeed committed Mar 14, 2024
1 parent ace9740 commit 95c916d
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .github/actions/ginkgo/main-focus.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ include:

###
# K8sAgentPolicyTest Clusterwide policies Test clusterwide connectivity with policies
# K8sAgentPolicyTest Clusterwide policies Tests connectivity with default-allow policies
# K8sAgentPolicyTest External services To Services first endpoint creation
# K8sAgentPolicyTest External services To Services first endpoint creation match service by labels
# K8sAgentPolicyTest External services To Services first policy
Expand Down Expand Up @@ -82,6 +83,8 @@ include:
# K8sAgentPolicyTest Basic Test Traffic redirections to proxy Tests DNS proxy visibility without policy
# K8sAgentPolicyTest Basic Test Traffic redirections to proxy Tests HTTP proxy visibility without policy
# K8sAgentPolicyTest Basic Test Traffic redirections to proxy Tests proxy visibility interactions with policy lifecycle operations
# K8sAgentPolicyTest Basic Test Traffic redirections to proxy Tests proxy visibility with L7 default-allow rules
# K8sAgentPolicyTest Basic Test Traffic redirections to proxy Tests proxy visibility with L7 rules
# K8sPolicyTestExtended Validate toEntities KubeAPIServer Allows connection to KubeAPIServer
# K8sPolicyTestExtended Validate toEntities KubeAPIServer Denies connection to KubeAPIServer
# K8sPolicyTestExtended Validate toEntities KubeAPIServer Still allows connection to KubeAPIServer with a duplicate policy
Expand Down
10 changes: 5 additions & 5 deletions test/helpers/wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func CurlFail(endpoint string, optionalValues ...interface{}) string {
endpoint = fmt.Sprintf(endpoint, optionalValues...)
}
return fmt.Sprintf(
`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
`curl -k --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
CurlConnectTimeout, CurlMaxTimeout, endpoint, statsInfo)
}

Expand All @@ -91,7 +91,7 @@ func CurlFailNoStats(endpoint string, optionalValues ...interface{}) string {
endpoint = fmt.Sprintf(endpoint, optionalValues...)
}
return fmt.Sprintf(
`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %[1]d --max-time %[2]d %[3]s`,
`curl -k --path-as-is -s -D /dev/stderr --fail --connect-timeout %[1]d --max-time %[2]d %[3]s`,
CurlConnectTimeout, CurlMaxTimeout, endpoint)
}

Expand All @@ -106,7 +106,7 @@ func CurlWithHTTPCode(endpoint string, optionalValues ...interface{}) string {
}

return fmt.Sprintf(
`curl --path-as-is -s -D /dev/stderr --output /dev/stderr -w '%%{http_code}' --connect-timeout %d %s`,
`curl -k --path-as-is -s -D /dev/stderr --output /dev/stderr -w '%%{http_code}' --connect-timeout %d %s`,
CurlConnectTimeout, endpoint)
}

Expand All @@ -126,7 +126,7 @@ func CurlWithRetries(endpoint string, retries int, fail bool, optionalValues ...
endpoint = fmt.Sprintf(endpoint, optionalValues...)
}
return fmt.Sprintf(
`curl --path-as-is -s -D /dev/stderr --output /dev/stderr --retry %d %s`,
`curl -k --path-as-is -s -D /dev/stderr --output /dev/stderr --retry %d %s`,
retries, endpoint)
}

Expand All @@ -140,7 +140,7 @@ func CurlTimeout(endpoint string, timeout time.Duration, optionalValues ...inter
endpoint = fmt.Sprintf(endpoint, optionalValues...)
}
return fmt.Sprintf(
`curl --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
`curl -k --path-as-is -s -D /dev/stderr --fail --connect-timeout %d --max-time %d %s -w "%s"`,
timeout, timeout, endpoint, statsInfo)
}

Expand Down
16 changes: 16 additions & 0 deletions test/k8s/manifests/ccnp-allow-apiserver-default-allow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: "cilium.io/v2"
kind: CiliumClusterwideNetworkPolicy
metadata:
name: ccnp-allow-apiserver-default-allow
spec:
endpointSelector: {}
enableDefaultDeny:
egress: false
egress:
- toEntities:
- kube-apiserver
- toEntities:
- all
toPorts:
- ports:
- port: "53"
14 changes: 14 additions & 0 deletions test/k8s/manifests/cnp-deny-to-app1-default-allow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: deny-to-app1-default-allow
spec:
enableDefaultDeny:
egress: false
endpointSelector:
matchLabels:
id: app2
egressDeny:
- toEndpoints:
- matchLabels:
id: app1
23 changes: 23 additions & 0 deletions test/k8s/manifests/l7-policy-allow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l7-policy-default-allow"
spec:
description: "L7 policy for getting started using Kubernetes guide"
endpointSelector:
matchLabels:
id: app1
enableDefaultDeny:
ingress: false
ingress:
- fromEndpoints:
- matchLabels:
id: app2
toPorts:
- ports:
- port: "80"
protocol: TCP
rules:
http:
- method: "GET"
path: "/public"
125 changes: 125 additions & 0 deletions test/k8s/net_policies.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ var _ = SkipDescribeIf(func() bool {
ciliumFilename string
demoPath string
l3Policy string
l7Policy string
l7PolicyDefAllow string
connectivityCheckYml string

app1Service = "app1-service"
Expand All @@ -52,6 +54,8 @@ var _ = SkipDescribeIf(func() bool {

demoPath = helpers.ManifestGet(kubectl.BasePath(), "demo-named-port.yaml")
l3Policy = helpers.ManifestGet(kubectl.BasePath(), "l3-l4-policy.yaml")
l7Policy = helpers.ManifestGet(kubectl.BasePath(), "l7-policy.yaml")
l7PolicyDefAllow = helpers.ManifestGet(kubectl.BasePath(), "l7-policy-allow.yaml")
connectivityCheckYml = kubectl.GetFilePath("../examples/kubernetes/connectivity-check/connectivity-check-proxy.yaml")

daemonCfg = map[string]string{
Expand Down Expand Up @@ -335,6 +339,41 @@ var _ = SkipDescribeIf(func() bool {

By("Importing policy using named ports which selects app1; proxy-visibility annotation should remain")
})

It("Tests proxy visibility with L7 rules", func() {
By("Creating a l7 policy for the pod")
_, err := kubectl.CiliumPolicyAction(
namespaceForTest, l7Policy, helpers.KubectlApply, helpers.HelperTimeout)
Expect(err).Should(BeNil(),
"policy %s cannot be applied in %q namespace", l3Policy, namespaceForTest)

By("Checking that traffic is proxied")
checkProxyRedirection(app1PodIP, true, policy.ParserTypeHTTP, false)

By("Checking that ping is blocked")
res := kubectl.ExecPodCmd(
namespaceForTest, appPods[helpers.App2],
helpers.Ping(app1PodIP))
res.ExpectFail("Ingrress ping connectivity should be denied for pod %q", helpers.App2)
})

It("Tests proxy visibility with L7 default-allow rules", func() {
By("Creating a l7 policy with default-allow for the pod")
_, err := kubectl.CiliumPolicyAction(
namespaceForTest, l7PolicyDefAllow, helpers.KubectlApply, helpers.HelperTimeout)
Expect(err).Should(BeNil(),
"policy %s cannot be applied in %q namespace", l3Policy, namespaceForTest)

By("Checking that traffic is proxied")
checkProxyRedirection(app1PodIP, true, policy.ParserTypeHTTP, false)

By("Checking that ping is allowed")
res := kubectl.ExecPodCmd(
namespaceForTest, appPods[helpers.App2],
helpers.Ping(app1PodIP))
res.ExpectSuccess("Ingrress ping connectivity should be allowed for pod %q", helpers.App2)

})
})
})

Expand Down Expand Up @@ -1074,6 +1113,9 @@ var _ = SkipDescribeIf(func() bool {
egressDenyAllPolicy string
allowIngressPolicy string
allowAllPolicy string

// non-default-deny policies
egressAllowApiDefaultAllow string
)

BeforeAll(func() {
Expand All @@ -1084,6 +1126,7 @@ var _ = SkipDescribeIf(func() bool {
ingressDenyAllPolicy = helpers.ManifestGet(kubectl.BasePath(), "ccnp-default-deny-ingress.yaml")
allowIngressPolicy = helpers.ManifestGet(kubectl.BasePath(), "ccnp-update-allow-ingress.yaml")
allowAllPolicy = helpers.ManifestGet(kubectl.BasePath(), "ccnp-update-allow-all.yaml")
egressAllowApiDefaultAllow = helpers.ManifestGet(kubectl.BasePath(), "ccnp-allow-apiserver-default-allow.yaml")

demoManifestNS1 = fmt.Sprintf("%s -n %s", demoPath, firstNS)
demoManifestNS2 = fmt.Sprintf("%s -n %s", demoPath, secondNS)
Expand Down Expand Up @@ -1294,6 +1337,88 @@ var _ = SkipDescribeIf(func() bool {
Expect(err).Should(BeNil(),
"%q Clusterwide Policy cannot be deleted", ingressDenyAllPolicy)
})

It("Tests connectivity with default-allow policies", func() {

// Cases:
// 1: default-allow policy allows all traffic
// 2: creating a default-deny policy flips this
// 3: Without default deny, explicit Deny rules take precedence

// case 1: default-allow policy
By("Creating a default-allow policy that allows apiserver access")
_, err := kubectl.CiliumClusterwidePolicyAction(
egressAllowApiDefaultAllow, helpers.KubectlApply, helpers.HelperTimeout)
Expect(err).Should(BeNil(),
"%q Clusterwide Policy cannot be applied", egressDenyAllPolicy)

app2 := appPodsFirstNS[helpers.App2]
app1 := appPodsFirstNS[helpers.App1]

By("Testing that app2 can connect to the apiserver")
res := kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail("https://kubernetes.default.svc.cluster.local/healthz"))
res.ExpectSuccess("Connectivity should be allowed from %s to apiserver in %s", app2, firstNS)

By("Testing connectivity from %q to %q in %q namespace without explicit allow", app2, app1, firstNS)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail(fmt.Sprintf("http://%s/public", firstNSclusterIP)))
res.ExpectSuccess("Connectivity should be allowed from %s to %s in ns %s", app2, app1, firstNS)

// case 2: default-allow policy allows apiserver, default-deny policy allows nothing
// so only apiserver should be allowed
By("Creating a default-deny policy in namespace %s", firstNS)
denyEgress := helpers.ManifestGet(kubectl.BasePath(), "cnp-default-deny-egress.yaml")
_, err = kubectl.CiliumPolicyAction(
firstNS, denyEgress, helpers.KubectlApply, helpers.HelperTimeout)
Expect(err).Should(BeNil(), "%q Policy cannot be applied", firstNS)

By("Testing that %s can still connect to the apiserver", helpers.App2)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail("https://kubernetes.default.svc.cluster.local/healthz"))
res.ExpectSuccess("Connectivity should be allowed from %s to apiserver in %s", app2, firstNS)

By("Testing connectivity from %q to %q in %q namespace is blocked", helpers.App2, helpers.App1, firstNS)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail(fmt.Sprintf("http://%s/public", firstNSclusterIP)))
res.ExpectFail("Connectivity should be denied from %s to %s in ns %s", app2, app1, firstNS)

// Back to case 1 -- delete default-deny
By("Deleting the default-deny egress policy")
_, err = kubectl.CiliumPolicyAction(
firstNS, denyEgress, helpers.KubectlDelete, helpers.HelperTimeout)
Expect(err).Should(BeNil(), "%q Policy cannot be deleted", firstNS)

By("Testing connectivity again from %q to %q in %q namespace without explicit allow", app2, app1, firstNS)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail(fmt.Sprintf("http://%s/public", firstNSclusterIP)))
res.ExpectSuccess("Connectivity should be allowed from %s to %s in ns %s", app2, app1, firstNS)

// case 3: only default-allow policy, one has EgressDeny rule.
// ensure that all connectivity is allowed except the explicit deny
By("Creating a default-allow policy that denies access to app1")
denyApp1 := helpers.ManifestGet(kubectl.BasePath(), "cnp-deny-to-app1-default-allow.yaml")
_, err = kubectl.CiliumPolicyAction(
firstNS, denyApp1, helpers.KubectlApply, helpers.HelperTimeout)
Expect(err).Should(BeNil(), "%q Policy cannot be applied", firstNS)

By("Testing connectivity again from %q to %q in %q namespace with explicit deny", app2, app1, firstNS)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail(fmt.Sprintf("http://%s/public", firstNSclusterIP)))
res.ExpectFail("Connectivity should be denied from %s to %s in ns %s", app2, app1, firstNS)

By("Testing cross namespace connectivity from %q to %q namespace", firstNS, secondNS)
res = kubectl.ExecPodCmd(
firstNS, appPodsFirstNS[helpers.App2],
helpers.CurlFail(fmt.Sprintf("http://%s/public", secondNSclusterIP)))
res.ExpectSuccess("Connectivity should be allowed from %s to %s in ns %s", app2, secondNSclusterIP, firstNS)
})
})

//TODO: Check service with IPV6
Expand Down

0 comments on commit 95c916d

Please sign in to comment.