Skip to content

Commit

Permalink
Merge pull request #3487 from tmjd/policy-for-k8s-endpoint
Browse files Browse the repository at this point in the history
Dynamically add network policy for apiserver egress with configured K8SServicEndpoint
  • Loading branch information
tmjd authored Sep 27, 2024
2 parents 62b56a8 + 0e24920 commit d56be0f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 0 deletions.
35 changes: 35 additions & 0 deletions pkg/controller/k8sapi/k8s-endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ package k8sapi

import (
"fmt"
"net"
"os"
"strings"

calicov3 "github.com/tigera/api/pkg/apis/projectcalico/v3"
"github.com/tigera/api/pkg/lib/numorstring"
operator "github.com/tigera/operator/api/v1"
v1 "k8s.io/api/core/v1"
)
Expand Down Expand Up @@ -65,6 +68,38 @@ func (k8s ServiceEndpoint) EnvVars(hostNetworked bool, provider operator.Provide
}
}

// DestinationEntityRule returns an EntityRule to match the Host and Port
// if the ServiceEndpoint was set. It returns nil if either was empty.
func (k8s ServiceEndpoint) DestinationEntityRule() (*calicov3.EntityRule, error) {
if k8s.Host == "" || k8s.Port == "" {
return nil, nil
}

p, err := numorstring.PortFromString(k8s.Port)
if err != nil {
return nil, err
}

rule := calicov3.EntityRule{
Ports: []numorstring.Port{p},
}

ip := net.ParseIP(k8s.Host)
if ip == nil {
rule.Domains = []string{k8s.Host}
} else {
var netSuffix string
if ip.To4() != nil {
netSuffix = "/32"
} else {
netSuffix = "/128"
}
rule.Nets = []string{ip.String() + netSuffix}
}

return &rule, nil
}

func (k8s ServiceEndpoint) CNIAPIRoot() string {
if k8s.Host == "" || k8s.Port == "" {
return ""
Expand Down
8 changes: 8 additions & 0 deletions pkg/render/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,14 @@ func allowTigeraAPIServerPolicy(cfg *APIServerConfiguration) *v3.NetworkPolicy {
},
}...)

if r, err := cfg.K8SServiceEndpoint.DestinationEntityRule(); r != nil && err == nil {
egressRules = append(egressRules, v3.Rule{
Action: v3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: *r,
})
}

// The ports Calico Enterprise API Server and Calico Enterprise Query Server are configured to listen on.
ingressPorts := networkpolicy.Ports(443, APIServerPort, QueryServerPort, 10443)
if cfg.IsSidecarInjectionEnabled() {
Expand Down
46 changes: 46 additions & 0 deletions pkg/render/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (

"github.com/openshift/library-go/pkg/crypto"

calicov3 "github.com/tigera/api/pkg/apis/projectcalico/v3"
operatorv1 "github.com/tigera/operator/api/v1"
"github.com/tigera/operator/pkg/apis"
"github.com/tigera/operator/pkg/common"
Expand All @@ -38,6 +39,7 @@ import (
"github.com/tigera/operator/pkg/dns"
"github.com/tigera/operator/pkg/render"
rmeta "github.com/tigera/operator/pkg/render/common/meta"
"github.com/tigera/operator/pkg/render/common/networkpolicy"
"github.com/tigera/operator/pkg/render/common/podaffinity"
rtest "github.com/tigera/operator/pkg/render/common/test"
"github.com/tigera/operator/pkg/render/testutils"
Expand Down Expand Up @@ -662,6 +664,50 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() {
rtest.ExpectK8sServiceEpEnvVars(deployment.Spec.Template.Spec, "k8shost", "1234")
})

It("should add egress policy with Enterprise variant and K8SServiceEndpoint defined", func() {
cfg.K8SServiceEndpoint.Host = "k8shost"
cfg.K8SServiceEndpoint.Port = "1234"
cfg.ForceHostNetwork = true

component := render.APIServerPolicy(cfg)
resources, _ := component.Objects()
policyName := types.NamespacedName{Name: "allow-tigera.cnx-apiserver-access", Namespace: "tigera-system"}
policy := testutils.GetAllowTigeraPolicyFromResources(policyName, resources)
Expect(policy).ToNot(BeNil())
Expect(policy.Spec).ToNot(BeNil())
Expect(policy.Spec.Egress).ToNot(BeNil())
Expect(policy.Spec.Egress).To(ContainElement(calicov3.Rule{
Action: calicov3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: calicov3.EntityRule{
Ports: networkpolicy.Ports(1234),
Domains: []string{"k8shost"},
},
}))
})

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"
cfg.ForceHostNetwork = false

component := render.APIServerPolicy(cfg)
resources, _ := component.Objects()
policyName := types.NamespacedName{Name: "allow-tigera.cnx-apiserver-access", Namespace: "tigera-system"}
policy := testutils.GetAllowTigeraPolicyFromResources(policyName, resources)
Expect(policy).ToNot(BeNil())
Expect(policy.Spec).ToNot(BeNil())
Expect(policy.Spec.Egress).ToNot(BeNil())
Expect(policy.Spec.Egress).To(ContainElement(calicov3.Rule{
Action: calicov3.Allow,
Protocol: &networkpolicy.TCPProtocol,
Destination: calicov3.EntityRule{
Ports: networkpolicy.Ports(4321),
Nets: []string{"169.169.169.169/32"},
},
}))
})

It("should not set KUBERENETES_SERVICE_... variables if not host networked on Docker EE with proxy.local", func() {
cfg.K8SServiceEndpoint.Host = "proxy.local"
cfg.K8SServiceEndpoint.Port = "1234"
Expand Down

0 comments on commit d56be0f

Please sign in to comment.