diff --git a/pkg/render/apiserver.go b/pkg/render/apiserver.go index 1a87b3345c..3bf3dc4e30 100644 --- a/pkg/render/apiserver.go +++ b/pkg/render/apiserver.go @@ -565,6 +565,16 @@ func calicoSystemAPIServerPolicy(cfg *APIServerConfiguration) *v3.NetworkPolicy }, }...) + // A managed cluster has no local Linseed; the query server reaches it through Guardian, + // matching the LINSEED_URL that LinseedEndpoint returns. + if cfg.ManagementClusterConnection != nil { + egressRules = append(egressRules, v3.Rule{ + Action: v3.Allow, + Protocol: &networkpolicy.TCPProtocol, + Destination: GuardianEntityRule, + }) + } + if cfg.KeyValidatorConfig != nil { if parsedURL, err := url.Parse(cfg.KeyValidatorConfig.Issuer()); err == nil { oidcEgressRule := networkpolicy.GetOIDCEgressRule(parsedURL) diff --git a/pkg/render/apiserver_test.go b/pkg/render/apiserver_test.go index 2d17eb0d2a..f9dbbf5730 100644 --- a/pkg/render/apiserver_test.go +++ b/pkg/render/apiserver_test.go @@ -63,6 +63,8 @@ import ( var _ = Describe("API server rendering tests (Calico Enterprise)", func() { apiServerPolicy := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/apiserver.json") apiServerPolicyForOCP := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/apiserver_ocp.json") + apiServerPolicyForManaged := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/apiserver_managed.json") + apiServerPolicyForManagedOCP := testutils.GetExpectedPolicyFromFile("./testutils/expected_policies/apiserver_managed_ocp.json") var ( instance *operatorv1.InstallationSpec apiserver *operatorv1.APIServerSpec @@ -830,6 +832,33 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { })) }) + It("should allow egress to Guardian on a managed cluster", func() { + cfg.ManagementClusterConnection = &operatorv1.ManagementClusterConnection{} + + component := render.APIServerPolicy(cfg) + resources, _ := component.Objects() + policyName := types.NamespacedName{Name: "calico-system.apiserver-access", Namespace: "calico-system"} + policy := testutils.GetCalicoSystemPolicyFromResources(policyName, resources) + Expect(policy).ToNot(BeNil()) + Expect(policy.Spec.Egress).To(ContainElement(calicov3.Rule{ + Action: calicov3.Allow, + Protocol: &networkpolicy.TCPProtocol, + Destination: render.GuardianEntityRule, + })) + // The rule is only reached if it precedes the trailing Pass. + n := len(policy.Spec.Egress) + Expect(policy.Spec.Egress[n-1].Action).To(Equal(calicov3.Pass)) + }) + + It("should omit the Guardian egress rule when the cluster is not managed", func() { + component := render.APIServerPolicy(cfg) + resources, _ := component.Objects() + policyName := types.NamespacedName{Name: "calico-system.apiserver-access", Namespace: "calico-system"} + policy := testutils.GetCalicoSystemPolicyFromResources(policyName, resources) + Expect(policy).ToNot(BeNil()) + Expect(policy.Spec.Egress).NotTo(ContainElement(HaveField("Destination", render.GuardianEntityRule))) + }) + It("should add egress policy with Enterprise variant and K8SServiceEndpoint as IP defined", func() { cfg.K8SServiceEndpoint.Host = "169.169.169.169" cfg.K8SServiceEndpoint.Port = "4321" @@ -1182,7 +1211,15 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { resources, _ := component.Objects() policy := testutils.GetCalicoSystemPolicyFromResources(policyName, resources) - expectedPolicy := testutils.SelectPolicyByProvider(scenario, apiServerPolicy, apiServerPolicyForOCP) + expectedPolicy := testutils.SelectPolicyByClusterTypeAndProvider( + scenario, + map[string]*calicov3.NetworkPolicy{ + "unmanaged": apiServerPolicy, + "unmanaged-openshift": apiServerPolicyForOCP, + "managed": apiServerPolicyForManaged, + "managed-openshift": apiServerPolicyForManagedOCP, + }, + ) Expect(policy).To(Equal(expectedPolicy)) }, Entry("for management/standalone, kube-dns", testutils.CalicoSystemScenario{ManagedCluster: false, OpenShift: false}), diff --git a/pkg/render/testutils/expected_policies/apiserver_managed.json b/pkg/render/testutils/expected_policies/apiserver_managed.json new file mode 100644 index 0000000000..e3ebdcc3f2 --- /dev/null +++ b/pkg/render/testutils/expected_policies/apiserver_managed.json @@ -0,0 +1,123 @@ +{ + "apiVersion": "projectcalico.org/v3", + "kind": "NetworkPolicy", + "metadata": { + "name": "calico-system.apiserver-access", + "namespace": "calico-system" + }, + "spec": { + "order": 1, + "tier": "calico-system", + "selector": "k8s-app == 'calico-apiserver'", + "types": [ + "Ingress", + "Egress" + ], + "ingress": [ + { + "action": "Allow", + "protocol": "TCP", + "source": { + "nets": [ + "0.0.0.0/0" + ] + }, + "destination": { + "ports": [ + 443, + 5443, + 8080, + 10443 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "source": { + "nets": [ + "::/0" + ] + }, + "destination": { + "ports": [ + 443, + 5443, + 8080, + 10443 + ] + } + } + ], + "egress": [ + { + "action": "Allow", + "protocol": "UDP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'kube-system'", + "selector": "k8s-app in { 'kube-dns', 'coredns' }", + "ports": [ + 53 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "services": { + "name": "kubernetes", + "namespace": "default" + } + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'tigera-prometheus'", + "selector": "k8s-app == 'tigera-prometheus'", + "ports": [ + 9095 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "selector": "k8s-app == 'tigera-dex'", + "namespaceSelector": "projectcalico.org/name == 'tigera-dex'", + "ports": [ + 5556 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'tigera-elasticsearch'", + "selector": "k8s-app == 'tigera-linseed'", + "ports": [ + 8444 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'calico-system'", + "selector": "k8s-app == 'guardian'", + "ports": [ + 8080 + ] + } + }, + { + "action": "Pass" + } + ] + } +} diff --git a/pkg/render/testutils/expected_policies/apiserver_managed_ocp.json b/pkg/render/testutils/expected_policies/apiserver_managed_ocp.json new file mode 100644 index 0000000000..97ed1d1267 --- /dev/null +++ b/pkg/render/testutils/expected_policies/apiserver_managed_ocp.json @@ -0,0 +1,134 @@ +{ + "apiVersion": "projectcalico.org/v3", + "kind": "NetworkPolicy", + "metadata": { + "name": "calico-system.apiserver-access", + "namespace": "calico-system" + }, + "spec": { + "order": 1, + "tier": "calico-system", + "selector": "k8s-app == 'calico-apiserver'", + "types": [ + "Ingress", + "Egress" + ], + "ingress": [ + { + "action": "Allow", + "protocol": "TCP", + "source": { + "nets": [ + "0.0.0.0/0" + ] + }, + "destination": { + "ports": [ + 443, + 5443, + 8080, + 10443 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "source": { + "nets": [ + "::/0" + ] + }, + "destination": { + "ports": [ + 443, + 5443, + 8080, + 10443 + ] + } + } + ], + "egress": [ + { + "action": "Allow", + "protocol": "UDP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'openshift-dns'", + "selector": "dns.operator.openshift.io/daemonset-dns == 'default'", + "ports": [ + 5353 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'openshift-dns'", + "selector": "dns.operator.openshift.io/daemonset-dns == 'default'", + "ports": [ + 5353 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "services": { + "name": "kubernetes", + "namespace": "default" + } + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'tigera-prometheus'", + "selector": "k8s-app == 'tigera-prometheus'", + "ports": [ + 9095 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "selector": "k8s-app == 'tigera-dex'", + "namespaceSelector": "projectcalico.org/name == 'tigera-dex'", + "ports": [ + 5556 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'tigera-elasticsearch'", + "selector": "k8s-app == 'tigera-linseed'", + "ports": [ + 8444 + ] + } + }, + { + "action": "Allow", + "protocol": "TCP", + "destination": { + "namespaceSelector": "projectcalico.org/name == 'calico-system'", + "selector": "k8s-app == 'guardian'", + "ports": [ + 8080 + ] + } + }, + { + "action": "Pass" + } + ] + } +}