Skip to content

Commit

Permalink
[fix] add exception for the platform in network policy
Browse files Browse the repository at this point in the history
  • Loading branch information
facchettos committed Aug 2, 2024
1 parent 2a95c09 commit 2d5f895
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 0 deletions.
6 changes: 6 additions & 0 deletions chart/templates/networkpolicy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ spec:
podSelector:
matchLabels:
k8s-app: kube-dns
{{- if .Values.policies.networkPolicy.outgoingConnections.platform }}
- podSelector:
matchLabels:
app: loft
namespaceSelector: {}
{{- end }}
policyTypes:
- Egress
{{- end }}
4 changes: 4 additions & 0 deletions chart/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2038,6 +2038,10 @@
"ipBlock": {
"$ref": "#/$defs/IPBlock",
"description": "IPBlock describes a particular CIDR (Ex. \"192.168.1.0/24\",\"2001:db8::/64\") that is allowed\nto the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs\nthat should not be included within this rule."
},
"platform": {
"type": "boolean",
"description": "Platform enables egress access towards loft platform"
}
},
"additionalProperties": false,
Expand Down
2 changes: 2 additions & 0 deletions chart/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,8 @@ policies:
annotations: {}
fallbackDns: 8.8.8.8
outgoingConnections:
# Platform enables egress access towards loft platform
platform: true
# IPBlock describes a particular CIDR (Ex. "192.168.1.0/24","2001:db8::/64") that is allowed
# to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs
# that should not be included within this rule.
Expand Down
3 changes: 3 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -1451,6 +1451,9 @@ type OutgoingConnections struct {
// to the pods matched by a NetworkPolicySpec's podSelector. The except entry describes CIDRs
// that should not be included within this rule.
IPBlock IPBlock `json:"ipBlock,omitempty"`

// Platform enables egress access towards loft platform
Platform bool `json:"platform,omitempty"`
}

type IPBlock struct {
Expand Down
1 change: 1 addition & 0 deletions config/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ policies:
annotations: {}
fallbackDns: 8.8.8.8
outgoingConnections:
platform: true
ipBlock:
cidr: 0.0.0.0/0
except:
Expand Down
30 changes: 30 additions & 0 deletions test/e2e_isolation_mode/isolation/loftreachable.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package isolation

import (
"github.com/loft-sh/vcluster/test/framework"
"github.com/onsi/ginkgo/v2"
)

var _ = ginkgo.Describe("test that we can reach pod with a app:loft label", func() {
f := framework.DefaultFramework

if f.MultiNamespaceMode {
ginkgo.Skip("Isolated mode is not supported in Multi-Namespace mode")
}

cpod, err := f.CreateCurlPodHost(framework.DefaultFramework.VclusterNamespace, map[string]string{"release": framework.DefaultFramework.VclusterName})
framework.ExpectNoError(err)

ginkgo.It("should be able to reach a pod in another ns if it has a app:loft", func() {
_, svc, err := f.CreateHostNginxPodAndService("test-ns", map[string]string{"app": "loft"})
framework.ExpectNoError(err)

framework.DefaultFramework.TestHostServiceIsEventuallyReachable(cpod, svc)
})

ginkgo.It("should not be able to reach a pod in another ns if it has a app:something", func() {
_, svc, err := f.CreateHostNginxPodAndService("test-ns-2", map[string]string{"app": "something"})
framework.ExpectNoError(err)
framework.DefaultFramework.TestHostServiceIsEventuallyUnreachable(cpod, svc)
})
})
103 changes: 103 additions & 0 deletions test/framework/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,77 @@ func (f *Framework) CreateCurlPod(ns string) (*corev1.Pod, error) {
}, metav1.CreateOptions{})
}

func (f *Framework) CreateCurlPodHost(ns string, labels map[string]string) (*corev1.Pod, error) {
return f.HostClient.CoreV1().Pods(ns).Create(f.Context, &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "curl",
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "curl",
Image: "curlimages/curl",
ImagePullPolicy: corev1.PullIfNotPresent,
SecurityContext: f.GetDefaultSecurityContext(),
Command: []string{"sleep"},
Args: []string{"9999"},
},
},
},
}, metav1.CreateOptions{})
}

func (f *Framework) CreateHostNginxPodAndService(ns string, labels map[string]string) (*corev1.Pod, *corev1.Service, error) {
podName := "nginx"
serviceName := "nginx"

_, err := f.HostClient.CoreV1().Namespaces().Create(f.Context, &corev1.Namespace{
ObjectMeta: metav1.ObjectMeta{
Name: ns,
Labels: labels,
},
}, metav1.CreateOptions{})
if err != nil && !kerrors.IsAlreadyExists(err) {
return nil, nil, err
}

pod, err := f.HostClient.CoreV1().Pods(ns).Create(f.Context, &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: podName,
Labels: labels,
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: podName,
Image: "nginxinc/nginx-unprivileged",
ImagePullPolicy: corev1.PullIfNotPresent,
SecurityContext: f.GetDefaultSecurityContext(),
},
},
},
}, metav1.CreateOptions{})
if err != nil {
return nil, nil, err
}

service, err := f.HostClient.CoreV1().Services(ns).Create(f.Context, &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: serviceName,
Namespace: ns,
},
Spec: corev1.ServiceSpec{
Selector: labels,
Ports: []corev1.ServicePort{
{Port: 8080},
},
},
}, metav1.CreateOptions{})

return pod, service, err
}

func (f *Framework) CreateNginxPodAndService(ns string) (*corev1.Pod, *corev1.Service, error) {
podName := "nginx"
serviceName := "nginx"
Expand Down Expand Up @@ -283,6 +354,19 @@ func (f *Framework) CreateNginxPodAndService(ns string) (*corev1.Pod, *corev1.Se
return pod, service, err
}

func (f *Framework) TestHostServiceIsEventuallyReachable(curlPod *corev1.Pod, service *corev1.Service) {
var stdoutBuffer []byte
var lastError error
err := wait.PollUntilContextTimeout(f.Context, 10*time.Second, PollTimeout, true, func(ctx context.Context) (bool, error) {
stdoutBuffer, _, lastError = f.curlHostService(ctx, curlPod, service)
if lastError == nil && string(stdoutBuffer) == "200" {
return true, nil
}
return false, nil
})
ExpectNoError(err, "Nginx service is expected to be reachable. On the last attempt got %s http code and following error:", string(stdoutBuffer), lastError)
}

func (f *Framework) TestServiceIsEventuallyReachable(curlPod *corev1.Pod, service *corev1.Service) {
var stdoutBuffer []byte
var lastError error
Expand All @@ -309,12 +393,31 @@ func (f *Framework) TestServiceIsEventuallyUnreachable(curlPod *corev1.Pod, serv
ExpectNoError(err, "Nginx service is expected to be unreachable. On the last attempt got %s http code and following error:", string(stdoutBuffer), lastError)
}

func (f *Framework) TestHostServiceIsEventuallyUnreachable(curlPod *corev1.Pod, service *corev1.Service) {
var stdoutBuffer, stderrBuffer []byte
var lastError error
err := wait.PollUntilContextTimeout(f.Context, 10*time.Second, PollTimeout, true, func(ctx context.Context) (bool, error) {
stdoutBuffer, stderrBuffer, lastError = f.curlHostService(ctx, curlPod, service)
if lastError != nil && strings.Contains(string(stderrBuffer), "timed out") && string(stdoutBuffer) == "000" {
return true, nil
}
return false, nil
})
ExpectNoError(err, "Nginx service is expected to be unreachable. On the last attempt got %s http code and following error:", string(stdoutBuffer), lastError)
}

func (f *Framework) curlService(_ context.Context, curlPod *corev1.Pod, service *corev1.Service) ([]byte, []byte, error) {
url := fmt.Sprintf("http://%s.%s.svc:%d/", service.GetName(), service.GetNamespace(), service.Spec.Ports[0].Port)
cmd := []string{"curl", "-s", "--show-error", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "2", url}
return podhelper.ExecBuffered(f.Context, f.VClusterConfig, curlPod.GetNamespace(), curlPod.GetName(), curlPod.Spec.Containers[0].Name, cmd, nil)
}

func (f *Framework) curlHostService(_ context.Context, curlPod *corev1.Pod, service *corev1.Service) ([]byte, []byte, error) {
url := fmt.Sprintf("http://%s.%s.svc:%d/", service.GetName(), service.GetNamespace(), service.Spec.Ports[0].Port)
cmd := []string{"curl", "-s", "--show-error", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "2", url}
return podhelper.ExecBuffered(f.Context, f.HostConfig, curlPod.GetNamespace(), curlPod.GetName(), curlPod.Spec.Containers[0].Name, cmd, nil)
}

func (f *Framework) CreateEgressNetworkPolicyForDNS(ctx context.Context, ns string) (*networkingv1.NetworkPolicy, error) {
UDPProtocol := corev1.ProtocolUDP
return f.VClusterClient.NetworkingV1().NetworkPolicies(ns).Create(ctx, &networkingv1.NetworkPolicy{
Expand Down

0 comments on commit 2d5f895

Please sign in to comment.