Skip to content

Commit 80613c6

Browse files
kabicinkabicin
authored andcommitted
Add bypass deny all egress flag
1 parent 730f777 commit 80613c6

File tree

10 files changed

+160
-12
lines changed

10 files changed

+160
-12
lines changed

api/v1/runtimecomponent_types.go

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -336,24 +336,28 @@ type RuntimeComponentNetworkPolicy struct {
336336
// +operator-sdk:csv:customresourcedefinitions:order=48,type=spec,displayName="Disable Egress",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch"
337337
DisableEgress *bool `json:"disableEgress,omitempty"`
338338

339+
// Bypasses deny all egress rules to allow API server and DNS access. Defaults to false.
340+
// +operator-sdk:csv:customresourcedefinitions:order=49,type=spec,displayName="Bypass Deny All Egress",xDescriptors="urn:alm:descriptor:com.tectonic.ui:booleanSwitch"
341+
BypassDenyAllEgress *bool `json:"bypassDenyAllEgress,omitempty"`
342+
339343
// Deprecated. .spec.networkPolicy.fromNamespaceLabels should be used instead. If both are specified, .spec.networkPolicy.fromNamespaceLabels will override this.
340-
// +operator-sdk:csv:customresourcedefinitions:order=49,type=spec,displayName="Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
344+
// +operator-sdk:csv:customresourcedefinitions:order=50,type=spec,displayName="Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
341345
NamespaceLabels *map[string]string `json:"namespaceLabels,omitempty"`
342346

343347
// Specify the labels of namespaces that incoming traffic is allowed from.
344-
// +operator-sdk:csv:customresourcedefinitions:order=50,type=spec,displayName="From Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
348+
// +operator-sdk:csv:customresourcedefinitions:order=51,type=spec,displayName="From Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
345349
FromNamespaceLabels *map[string]string `json:"fromNamespaceLabels,omitempty"`
346350

347351
// Specify the labels of pod(s) that incoming traffic is allowed from.
348-
// +operator-sdk:csv:customresourcedefinitions:order=51,type=spec,displayName="From Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
352+
// +operator-sdk:csv:customresourcedefinitions:order=52,type=spec,displayName="From Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
349353
FromLabels *map[string]string `json:"fromLabels,omitempty"`
350354

351355
// Specify the labels of namespaces that outgoing traffic is allowed to.
352-
// +operator-sdk:csv:customresourcedefinitions:order=52,type=spec,displayName="To Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
356+
// +operator-sdk:csv:customresourcedefinitions:order=53,type=spec,displayName="To Namespace Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
353357
ToNamespaceLabels *map[string]string `json:"toNamespaceLabels,omitempty"`
354358

355359
// Specify the labels of pod(s) that outgoing traffic is allowed to.
356-
// +operator-sdk:csv:customresourcedefinitions:order=53,type=spec,displayName="To Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
360+
// +operator-sdk:csv:customresourcedefinitions:order=54,type=spec,displayName="To Labels",xDescriptors="urn:alm:descriptor:com.tectonic.ui:text"
357361
ToLabels *map[string]string `json:"toLabels,omitempty"`
358362
}
359363

@@ -974,6 +978,10 @@ func (np *RuntimeComponentNetworkPolicy) IsEgressDisabled() bool {
974978
return np.DisableEgress != nil && *np.DisableEgress
975979
}
976980

981+
func (np *RuntimeComponentNetworkPolicy) IsBypassingDenyAllEgress() bool {
982+
return np.BypassDenyAllEgress != nil && *np.BypassDenyAllEgress
983+
}
984+
977985
// GetLabels returns labels to be added on ServiceMonitor
978986
func (m *RuntimeComponentMonitoring) GetLabels() map[string]string {
979987
return m.Labels

api/v1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/rc.app.stacks_runtimecomponents.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3842,6 +3842,10 @@ spec:
38423842
networkPolicy:
38433843
description: Defines the network policy
38443844
properties:
3845+
bypassDenyAllEgress:
3846+
description: Bypasses deny all egress rules to allow API server
3847+
and DNS access. Defaults to false.
3848+
type: boolean
38453849
disable:
38463850
description: Disable the creation of the network policy. Defaults
38473851
to false.

common/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ type BaseComponentNetworkPolicy interface {
151151
IsDisabled() bool
152152
IsIngressDisabled() bool
153153
IsEgressDisabled() bool
154+
IsBypassingDenyAllEgress() bool
154155
GetToNamespaceLabels() map[string]string
155156
GetToLabels() map[string]string
156157
GetFromNamespaceLabels() map[string]string

config/crd/bases/rc.app.stacks_runtimecomponents.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3838,6 +3838,10 @@ spec:
38383838
networkPolicy:
38393839
description: Defines the network policy
38403840
properties:
3841+
bypassDenyAllEgress:
3842+
description: Bypasses deny all egress rules to allow API server
3843+
and DNS access. Defaults to false.
3844+
type: boolean
38413845
disable:
38423846
description: Disable the creation of the network policy. Defaults
38433847
to false.

config/manifests/bases/runtime-component.clusterserviceversion.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,12 @@ spec:
438438
path: networkPolicy.disableEgress
439439
x-descriptors:
440440
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
441+
- description: Bypasses deny all egress rules to allow API server and DNS access.
442+
Defaults to false.
443+
displayName: Bypass Deny All Egress
444+
path: networkPolicy.bypassDenyAllEgress
445+
x-descriptors:
446+
- urn:alm:descriptor:com.tectonic.ui:booleanSwitch
441447
- description: Deprecated. .spec.networkPolicy.fromNamespaceLabels should be
442448
used instead. If both are specified, .spec.networkPolicy.fromNamespaceLabels
443449
will override this.

internal/controller/runtimecomponent_controller.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424

2525
"github.com/application-stacks/runtime-component-operator/common"
26+
"github.com/application-stacks/runtime-component-operator/utils"
2627
"github.com/pkg/errors"
2728

2829
kcontroller "sigs.k8s.io/controller-runtime/pkg/controller"
@@ -351,7 +352,7 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req
351352
networkPolicy := &networkingv1.NetworkPolicy{ObjectMeta: defaultMeta}
352353
if np := instance.Spec.NetworkPolicy; np == nil || np != nil && !np.IsDisabled() {
353354
err = r.CreateOrUpdate(networkPolicy, instance, func() error {
354-
appstacksutils.CustomizeNetworkPolicy(networkPolicy, r.IsOpenShift(), instance)
355+
appstacksutils.CustomizeNetworkPolicy(networkPolicy, r.IsOpenShift(), r.getDNSEgressRule, r.getEndpoints, instance)
355356
return nil
356357
})
357358
if err != nil {
@@ -560,6 +561,43 @@ func (r *RuntimeComponentReconciler) Reconcile(ctx context.Context, req ctrl.Req
560561
return r.ManageSuccess(common.StatusConditionTypeReconciled, instance)
561562
}
562563

564+
func (r *RuntimeComponentReconciler) getEndpoints(serviceName string, namespace string) (*corev1.Endpoints, error) {
565+
endpoints := &corev1.Endpoints{}
566+
if err := r.GetClient().Get(context.TODO(), types.NamespacedName{Name: serviceName, Namespace: namespace}, endpoints); err != nil {
567+
return nil, err
568+
} else {
569+
return endpoints, nil
570+
}
571+
}
572+
573+
func (r *RuntimeComponentReconciler) getDNSEgressRule(endpointsName string, endpointsNamespace string) (bool, networkingv1.NetworkPolicyEgressRule) {
574+
dnsRule := networkingv1.NetworkPolicyEgressRule{}
575+
if dnsEndpoints, err := r.getEndpoints(endpointsName, endpointsNamespace); err == nil {
576+
if len(dnsEndpoints.Subsets) > 0 {
577+
if endpointPort := utils.GetEndpointPortByName(&dnsEndpoints.Subsets[0].Ports, "dns"); endpointPort != nil {
578+
dnsRule.Ports = append(dnsRule.Ports, utils.CreateNetworkPolicyPortFromEndpointPort(endpointPort))
579+
}
580+
if endpointPort := utils.GetEndpointPortByName(&dnsEndpoints.Subsets[0].Ports, "dns-tcp"); endpointPort != nil {
581+
dnsRule.Ports = append(dnsRule.Ports, utils.CreateNetworkPolicyPortFromEndpointPort(endpointPort))
582+
}
583+
}
584+
peer := networkingv1.NetworkPolicyPeer{}
585+
peer.NamespaceSelector = &metav1.LabelSelector{
586+
MatchLabels: map[string]string{
587+
"kubernetes.io/metadata.name": endpointsNamespace,
588+
},
589+
}
590+
dnsRule.To = append(dnsRule.To, peer)
591+
// reqLogger.Info("Found endpoints for " + endpointsName + " service in the " + endpointsNamespace + " namespace")
592+
return false, dnsRule
593+
}
594+
// use permissive rule
595+
// egress:
596+
// - {}
597+
// reqLogger.Info("Failed to retrieve endpoints for " + endpointsName + " service in the " + endpointsNamespace + " namespace. Using more permissive rule.")
598+
return true, dnsRule
599+
}
600+
563601
// SetupWithManager initializes reconciler
564602
func (r *RuntimeComponentReconciler) SetupWithManager(mgr ctrl.Manager) error {
565603

internal/deploy/kubectl/runtime-component-crd.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3841,6 +3841,10 @@ spec:
38413841
networkPolicy:
38423842
description: Defines the network policy
38433843
properties:
3844+
bypassDenyAllEgress:
3845+
description: Bypasses deny all egress rules to allow API server
3846+
and DNS access. Defaults to false.
3847+
type: boolean
38443848
disable:
38453849
description: Disable the creation of the network policy. Defaults
38463850
to false.

internal/deploy/kustomize/daily/base/runtime-component-crd.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3841,6 +3841,10 @@ spec:
38413841
networkPolicy:
38423842
description: Defines the network policy
38433843
properties:
3844+
bypassDenyAllEgress:
3845+
description: Bypasses deny all egress rules to allow API server
3846+
and DNS access. Defaults to false.
3847+
type: boolean
38443848
disable:
38453849
description: Disable the creation of the network policy. Defaults
38463850
to false.

utils/utils.go

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -340,19 +340,73 @@ func customizeProbeDefaults(config *corev1.Probe, defaultProbe *corev1.Probe) *c
340340
return probe
341341
}
342342

343+
func createEgressBypass(ba common.BaseComponent, isOpenShift bool, getDNSEgressRule func(string, string) (bool, networkingv1.NetworkPolicyEgressRule), getEndpoints func(string, string) (*corev1.Endpoints, error)) (bool, []networkingv1.NetworkPolicyEgressRule) {
344+
egressRules := []networkingv1.NetworkPolicyEgressRule{}
345+
346+
var dnsRule networkingv1.NetworkPolicyEgressRule
347+
var usingPermissiveRule bool
348+
// If allowed, add an Egress rule to access the OpenShift DNS or K8s CoreDNS. Otherwise, use a permissive cluster-wide Egress rule.
349+
if isOpenShift {
350+
usingPermissiveRule, dnsRule = getDNSEgressRule("dns-default", "openshift-dns")
351+
} else {
352+
usingPermissiveRule, dnsRule = getDNSEgressRule("kube-dns", "kube-system")
353+
}
354+
egressRules = append(egressRules, dnsRule)
355+
356+
// If the DNS rule is a specific Egress rule also check if another Egress rule can be created for the API server.
357+
// Otherwise, fallback to a permissive cluster-wide Egress rule.
358+
if !usingPermissiveRule {
359+
if apiServerEndpoints, err := getEndpoints("kubernetes", "default"); err == nil {
360+
rule := networkingv1.NetworkPolicyEgressRule{}
361+
// Define the port
362+
port := networkingv1.NetworkPolicyPort{}
363+
port.Protocol = &apiServerEndpoints.Subsets[0].Ports[0].Protocol
364+
var portNumber intstr.IntOrString = intstr.FromInt((int)(apiServerEndpoints.Subsets[0].Ports[0].Port))
365+
port.Port = &portNumber
366+
rule.Ports = append(rule.Ports, port)
367+
368+
// Add the endpoint address as ipBlock entries
369+
for _, endpoint := range apiServerEndpoints.Subsets {
370+
for _, address := range endpoint.Addresses {
371+
peer := networkingv1.NetworkPolicyPeer{}
372+
ipBlock := networkingv1.IPBlock{}
373+
ipBlock.CIDR = address.IP + "/32"
374+
375+
peer.IPBlock = &ipBlock
376+
rule.To = append(rule.To, peer)
377+
}
378+
}
379+
egressRules = append(egressRules, rule)
380+
} else {
381+
// The operator couldn't create a rule for the K8s API server so add a permissive Egress rule
382+
rule := networkingv1.NetworkPolicyEgressRule{}
383+
egressRules = append(egressRules, rule)
384+
}
385+
}
386+
return usingPermissiveRule, egressRules
387+
}
388+
343389
// createNetworkPolicyEgressRules returns the network policy rules for outgoing traffic to other Pod(s)
344-
func createNetworkPolicyEgressRules(networkPolicy *networkingv1.NetworkPolicy, isOpenShift bool, ba common.BaseComponent) []networkingv1.NetworkPolicyEgressRule {
390+
func createNetworkPolicyEgressRules(networkPolicy *networkingv1.NetworkPolicy, isOpenShift bool, isBypassingDenyAllEgress bool, getDNSEgressRule func(string, string) (bool, networkingv1.NetworkPolicyEgressRule), getEndpoints func(string, string) (*corev1.Endpoints, error), ba common.BaseComponent) []networkingv1.NetworkPolicyEgressRule {
345391
config := ba.GetNetworkPolicy()
392+
egressRules := []networkingv1.NetworkPolicyEgressRule{}
393+
if isBypassingDenyAllEgress {
394+
usingPermissiveRule, bypassRules := createEgressBypass(ba, isOpenShift, getDNSEgressRule, getEndpoints)
395+
egressRules = append(egressRules, bypassRules...)
396+
if usingPermissiveRule {
397+
return egressRules // exit early because permissive egress is set
398+
}
399+
}
400+
// configure the main application egress rule
346401
var rule networkingv1.NetworkPolicyEgressRule
347-
348402
if config == nil || ((config.GetToNamespaceLabels() != nil && len(config.GetToNamespaceLabels()) == 0) &&
349403
(config.GetToLabels() != nil && len(config.GetToLabels()) == 0)) {
350404
rule = createAllowAllNetworkPolicyEgressRule()
351405
} else {
352406
rule = createNetworkPolicyEgressRule(ba.GetApplicationName(), networkPolicy.Namespace, config)
353407
}
354-
355-
return []networkingv1.NetworkPolicyEgressRule{rule}
408+
egressRules = append(egressRules, rule)
409+
return egressRules
356410
}
357411

358412
func createNetworkPolicyEgressRule(appName string, namespace string, config common.BaseComponentNetworkPolicy) networkingv1.NetworkPolicyEgressRule {
@@ -374,7 +428,26 @@ func createAllowAllNetworkPolicyEgressRule() networkingv1.NetworkPolicyEgressRul
374428
}
375429
}
376430

377-
func CustomizeNetworkPolicy(networkPolicy *networkingv1.NetworkPolicy, isOpenShift bool, ba common.BaseComponent) {
431+
func GetEndpointPortByName(endpointPorts *[]corev1.EndpointPort, name string) *corev1.EndpointPort {
432+
if endpointPorts != nil {
433+
for _, endpointPort := range *endpointPorts {
434+
if endpointPort.Name == name {
435+
return &endpointPort
436+
}
437+
}
438+
}
439+
return nil
440+
}
441+
442+
func CreateNetworkPolicyPortFromEndpointPort(endpointPort *corev1.EndpointPort) networkingv1.NetworkPolicyPort {
443+
port := networkingv1.NetworkPolicyPort{}
444+
port.Protocol = &endpointPort.Protocol
445+
var portNumber intstr.IntOrString = intstr.FromInt((int)(endpointPort.Port))
446+
port.Port = &portNumber
447+
return port
448+
}
449+
450+
func CustomizeNetworkPolicy(networkPolicy *networkingv1.NetworkPolicy, isOpenShift bool, getDNSEgressRule func(string, string) (bool, networkingv1.NetworkPolicyEgressRule), getEndpoints func(string, string) (*corev1.Endpoints, error), ba common.BaseComponent) {
378451
obj := ba.(metav1.Object)
379452
networkPolicy.Labels = ba.GetLabels()
380453
networkPolicy.Annotations = MergeMaps(networkPolicy.Annotations, ba.GetAnnotations())
@@ -412,7 +485,8 @@ func CustomizeNetworkPolicy(networkPolicy *networkingv1.NetworkPolicy, isOpenShi
412485
if !hasEgressPolicy {
413486
networkPolicy.Spec.PolicyTypes = append(networkPolicy.Spec.PolicyTypes, networkingv1.PolicyTypeEgress)
414487
}
415-
networkPolicy.Spec.Egress = createNetworkPolicyEgressRules(networkPolicy, isOpenShift, ba)
488+
egressBypass := ba.GetNetworkPolicy() != nil && ba.GetNetworkPolicy().IsBypassingDenyAllEgress() // check if egress should bypass deny all policy to access the API server and DNS
489+
networkPolicy.Spec.Egress = createNetworkPolicyEgressRules(networkPolicy, isOpenShift, egressBypass, getDNSEgressRule, getEndpoints, ba)
416490
} else {
417491
// if egress is not configured, consider the network policy disabled
418492
if hasEgressPolicy {

0 commit comments

Comments
 (0)