Skip to content

Commit 4f260ab

Browse files
committed
implement core egress policy
1 parent aa5c341 commit 4f260ab

File tree

3 files changed

+248
-62
lines changed

3 files changed

+248
-62
lines changed

internal/controllers/networkpolicy.go

Lines changed: 131 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ package controllers
1717
import (
1818
"context"
1919
"fmt"
20+
"slices"
2021

2122
resources "github.com/cryostatio/cryostat-operator/internal/controllers/common/resource_definitions"
2223
"github.com/cryostatio/cryostat-operator/internal/controllers/constants"
2324
"github.com/cryostatio/cryostat-operator/internal/controllers/model"
25+
corev1 "k8s.io/api/core/v1"
2426
networkingv1 "k8s.io/api/networking/v1"
2527
"k8s.io/apimachinery/pkg/api/errors"
2628
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/types"
2730
"k8s.io/apimachinery/pkg/util/intstr"
2831
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2932
)
@@ -44,68 +47,158 @@ func installationNamespaceSelector(cr *model.CryostatInstance) *metav1.LabelSele
4447
return namespaceOriginSelector(cr.InstallNamespace)
4548
}
4649

50+
const NAMESPACE_NAME_LABEL = "kubernetes.io/metadata.name"
51+
4752
func namespaceOriginSelector(namespace string) *metav1.LabelSelector {
4853
return &metav1.LabelSelector{
4954
MatchLabels: map[string]string{
50-
"kubernetes.io/metadata.name": namespace,
55+
NAMESPACE_NAME_LABEL: namespace,
5156
},
5257
}
5358
}
5459

5560
func (r *Reconciler) reconcileCoreNetworkPolicy(ctx context.Context, cr *model.CryostatInstance) error {
61+
var err error
62+
5663
ingressPolicy := &networkingv1.NetworkPolicy{
5764
ObjectMeta: metav1.ObjectMeta{
5865
Name: fmt.Sprintf("%s-internal-ingress", cr.Name),
5966
Namespace: cr.InstallNamespace,
6067
},
6168
}
62-
if cr.Spec.NetworkPolicies != nil && cr.Spec.NetworkPolicies.CoreConfig != nil && cr.Spec.NetworkPolicies.CoreConfig.IngressDisabled != nil && *cr.Spec.NetworkPolicies.CoreConfig.IngressDisabled {
63-
return r.deletePolicy(ctx, ingressPolicy)
69+
ingressDisabled := cr.Spec.NetworkPolicies != nil && cr.Spec.NetworkPolicies.CoreConfig != nil && cr.Spec.NetworkPolicies.CoreConfig.IngressDisabled != nil && *cr.Spec.NetworkPolicies.CoreConfig.IngressDisabled
70+
if ingressDisabled {
71+
err = r.deletePolicy(ctx, ingressPolicy)
72+
}
73+
if err != nil {
74+
return err
6475
}
6576

66-
return r.createOrUpdatePolicy(ctx, ingressPolicy, cr.Object, func() error {
67-
ingressPolicy.Spec = networkingv1.NetworkPolicySpec{
68-
PodSelector: metav1.LabelSelector{
69-
MatchLabels: resources.CorePodLabels(cr),
70-
},
71-
Ingress: []networkingv1.NetworkPolicyIngressRule{
72-
// allow ingress to the authproxy/cryostat HTTP(S) port from any namespace or from the Route
73-
{
74-
From: []networkingv1.NetworkPolicyPeer{
75-
AllNamespacesSelector,
76-
RouteSelector,
77-
},
78-
Ports: []networkingv1.NetworkPolicyPort{
79-
{
80-
Port: &intstr.IntOrString{IntVal: constants.AuthProxyHttpContainerPort},
77+
egressPolicy := &networkingv1.NetworkPolicy{
78+
ObjectMeta: metav1.ObjectMeta{
79+
Name: fmt.Sprintf("%s-internal-egress", cr.Name),
80+
Namespace: cr.InstallNamespace,
81+
},
82+
}
83+
egressEnabled := cr.Spec.NetworkPolicies != nil && cr.Spec.NetworkPolicies.CoreConfig != nil && cr.Spec.NetworkPolicies.CoreConfig.EgressEnabled != nil && *cr.Spec.NetworkPolicies.CoreConfig.EgressEnabled
84+
if !egressEnabled {
85+
err = r.deletePolicy(ctx, egressPolicy)
86+
}
87+
if err != nil {
88+
return err
89+
}
90+
91+
if !ingressDisabled {
92+
err = r.createOrUpdatePolicy(ctx, ingressPolicy, cr.Object, func() error {
93+
ingressPolicy.Spec = networkingv1.NetworkPolicySpec{
94+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
95+
PodSelector: metav1.LabelSelector{
96+
MatchLabels: resources.CorePodLabels(cr),
97+
},
98+
Ingress: []networkingv1.NetworkPolicyIngressRule{
99+
// allow ingress to the authproxy/cryostat HTTP(S) port from any namespace or from the Route
100+
{
101+
From: []networkingv1.NetworkPolicyPeer{
102+
AllNamespacesSelector,
103+
RouteSelector,
104+
},
105+
Ports: []networkingv1.NetworkPolicyPort{
106+
{
107+
Port: &intstr.IntOrString{IntVal: constants.AuthProxyHttpContainerPort},
108+
},
81109
},
82110
},
83-
},
84-
// allow ingress to the agent gateway from the target namespaces
85-
{
86-
From: []networkingv1.NetworkPolicyPeer{
87-
{
88-
NamespaceSelector: &metav1.LabelSelector{
89-
MatchExpressions: []metav1.LabelSelectorRequirement{
90-
{
91-
Key: "kubernetes.io/metadata.name",
92-
Operator: metav1.LabelSelectorOpIn,
93-
Values: cr.Spec.TargetNamespaces,
111+
// allow ingress to the agent gateway from the target namespaces
112+
{
113+
From: []networkingv1.NetworkPolicyPeer{
114+
{
115+
NamespaceSelector: &metav1.LabelSelector{
116+
MatchExpressions: []metav1.LabelSelectorRequirement{
117+
{
118+
Key: "kubernetes.io/metadata.name",
119+
Operator: metav1.LabelSelectorOpIn,
120+
Values: cr.Spec.TargetNamespaces,
121+
},
94122
},
95123
},
96124
},
97125
},
98-
},
99-
Ports: []networkingv1.NetworkPolicyPort{
100-
networkingv1.NetworkPolicyPort{
101-
Port: &intstr.IntOrString{IntVal: constants.AgentProxyContainerPort},
126+
Ports: []networkingv1.NetworkPolicyPort{
127+
networkingv1.NetworkPolicyPort{
128+
Port: &intstr.IntOrString{IntVal: constants.AgentProxyContainerPort},
129+
},
102130
},
103131
},
104132
},
133+
}
134+
return nil
135+
})
136+
}
137+
if err != nil {
138+
return err
139+
}
140+
141+
if egressEnabled {
142+
egressDestinations := []networkingv1.NetworkPolicyPeer{}
143+
egressNamespaces := []string{
144+
// allow outgoing connections to Pods in infrastructure namespaces for discovery and auth
145+
"default",
146+
"kube-system",
147+
// allow outgoing connections to Pods in the InstallNamespace, so Cryostat can connect to its database, storage, etc.
148+
cr.InstallNamespace,
149+
}
150+
if r.IsOpenShift {
151+
egressNamespaces = append(egressNamespaces, "openshift")
152+
}
153+
for _, ns := range cr.TargetNamespaces {
154+
// allow outgoing connections to any Pod in the TargetNamespaces
155+
egressNamespaces = append(egressNamespaces, ns)
156+
}
157+
slices.Sort(egressNamespaces)
158+
egressDestinations = append(egressDestinations, networkingv1.NetworkPolicyPeer{
159+
NamespaceSelector: &metav1.LabelSelector{
160+
MatchExpressions: []metav1.LabelSelectorRequirement{
161+
metav1.LabelSelectorRequirement{
162+
Key: NAMESPACE_NAME_LABEL,
163+
Operator: metav1.LabelSelectorOpIn,
164+
Values: slices.Compact(egressNamespaces),
165+
},
166+
},
105167
},
168+
})
169+
k8sApiEndpoint := corev1.Endpoints{}
170+
err = r.Client.Get(ctx, types.NamespacedName{Namespace: "default", Name: "kubernetes"}, &k8sApiEndpoint)
171+
if err != nil {
172+
return err
106173
}
107-
return nil
108-
})
174+
if len(k8sApiEndpoint.Subsets) > 0 && len(k8sApiEndpoint.Subsets[0].Addresses) > 0 {
175+
// allow outgoing connections to the Kubernetes API server
176+
egressDestinations = append(egressDestinations,
177+
networkingv1.NetworkPolicyPeer{
178+
IPBlock: &networkingv1.IPBlock{
179+
CIDR: fmt.Sprintf("%s/32", k8sApiEndpoint.Subsets[0].Addresses[0].IP),
180+
},
181+
},
182+
)
183+
} else {
184+
return fmt.Errorf("Endpoints 'kubernetes' had no .Subsets or subset .Addresses")
185+
}
186+
err = r.createOrUpdatePolicy(ctx, egressPolicy, cr.Object, func() error {
187+
egressPolicy.Spec = networkingv1.NetworkPolicySpec{
188+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeEgress},
189+
PodSelector: metav1.LabelSelector{
190+
MatchLabels: resources.CorePodLabels(cr),
191+
},
192+
Egress: []networkingv1.NetworkPolicyEgressRule{
193+
{
194+
To: egressDestinations,
195+
},
196+
},
197+
}
198+
return nil
199+
})
200+
}
201+
return err
109202
}
110203

111204
func (r *Reconciler) reconcileDatabaseNetworkPolicy(ctx context.Context, cr *model.CryostatInstance) error {
@@ -121,6 +214,7 @@ func (r *Reconciler) reconcileDatabaseNetworkPolicy(ctx context.Context, cr *mod
121214

122215
return r.createOrUpdatePolicy(ctx, ingressPolicy, cr.Object, func() error {
123216
ingressPolicy.Spec = networkingv1.NetworkPolicySpec{
217+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
124218
PodSelector: metav1.LabelSelector{
125219
MatchLabels: resources.DatabasePodLabels(cr),
126220
},
@@ -159,6 +253,7 @@ func (r *Reconciler) reconcileStorageNetworkPolicy(ctx context.Context, cr *mode
159253

160254
return r.createOrUpdatePolicy(ctx, ingressPolicy, cr.Object, func() error {
161255
ingressPolicy.Spec = networkingv1.NetworkPolicySpec{
256+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
162257
PodSelector: metav1.LabelSelector{
163258
MatchLabels: resources.StoragePodLabels(cr),
164259
},
@@ -197,6 +292,7 @@ func (r *Reconciler) reconcileReportsNetworkPolicy(ctx context.Context, cr *mode
197292

198293
return r.createOrUpdatePolicy(ctx, ingressPolicy, cr.Object, func() error {
199294
ingressPolicy.Spec = networkingv1.NetworkPolicySpec{
295+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress},
200296
PodSelector: metav1.LabelSelector{
201297
MatchLabels: resources.ReportsPodLabels(cr),
202298
},

0 commit comments

Comments
 (0)