From f98e33f58e12fbb4eb555de5d22c642f7f5d64a6 Mon Sep 17 00:00:00 2001 From: craig Date: Mon, 23 Sep 2024 15:28:34 +0100 Subject: [PATCH] add an excludeAddresses option in DNSPolicy Signed-off-by: craig rh-pre-commit.version: 2.2.0 rh-pre-commit.check-secrets: ENABLED fix lint add integration test for excludeAddresses --- api/v1alpha1/dnspolicy_types.go | 12 ++ api/v1alpha1/zz_generated.deepcopy.go | 5 + ...adrant-operator.clusterserviceversion.yaml | 2 +- bundle/manifests/kuadrant.io_dnspolicies.yaml | 9 + config/crd/bases/kuadrant.io_dnspolicies.yaml | 9 + controllers/dnspolicy_controller.go | 2 +- controllers/dnspolicy_dnsrecords.go | 16 +- .../dnspolicy/dnspolicy-exclude-address.yaml | 20 +++ go.mod | 20 +-- go.sum | 50 +++--- pkg/multicluster/gateway_wrapper.go | 35 ++++ pkg/multicluster/gateway_wrapper_test.go | 169 ++++++++++++++++++ .../dnspolicy/dnspolicy_controller_test.go | 133 ++++++++++++++ 13 files changed, 443 insertions(+), 39 deletions(-) create mode 100644 examples/dnspolicy/dnspolicy-exclude-address.yaml create mode 100644 pkg/multicluster/gateway_wrapper_test.go diff --git a/api/v1alpha1/dnspolicy_types.go b/api/v1alpha1/dnspolicy_types.go index 26db6149f..88b2f24dd 100644 --- a/api/v1alpha1/dnspolicy_types.go +++ b/api/v1alpha1/dnspolicy_types.go @@ -68,6 +68,11 @@ type DNSPolicySpec struct { // +kubebuilder:validation:MaxItems=1 // +kubebuilder:validation:MinItems=1 ProviderRefs []dnsv1alpha1.ProviderRef `json:"providerRefs"` + + // ExcludeAddresses is a list of addresses (either hostnames, CIDR or IPAddresses) that DNSPolicy should not use as values in the configured DNS provider records. The default is to allow all addresses configured in the Gateway DNSPolicy is targeting + // +optional + // +kubebuilder:validation:MaxItems=20 + ExcludeAddresses []string `json:"excludeAddresses,omitempty"` } type LoadBalancingSpec struct { @@ -251,6 +256,13 @@ func (p *DNSPolicy) WithProviderSecret(s corev1.Secret) *DNSPolicy { }) } +//excludeAddresses + +func (p *DNSPolicy) WithExcludeAddresses(excluded []string) *DNSPolicy { + p.Spec.ExcludeAddresses = excluded + return p +} + //TargetRef func (p *DNSPolicy) WithTargetGateway(gwName string) *DNSPolicy { diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 51b71361a..53a868b6c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -146,6 +146,11 @@ func (in *DNSPolicySpec) DeepCopyInto(out *DNSPolicySpec) { *out = make([]apiv1alpha1.ProviderRef, len(*in)) copy(*out, *in) } + if in.ExcludeAddresses != nil { + in, out := &in.ExcludeAddresses, &out.ExcludeAddresses + *out = make([]string, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSPolicySpec. diff --git a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml index 81d5ead52..262d1b95d 100644 --- a/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml +++ b/bundle/manifests/kuadrant-operator.clusterserviceversion.yaml @@ -106,7 +106,7 @@ metadata: capabilities: Basic Install categories: Integration & Delivery containerImage: quay.io/kuadrant/kuadrant-operator:latest - createdAt: "2024-09-17T13:54:51Z" + createdAt: "2024-09-23T14:42:42Z" operators.operatorframework.io/builder: operator-sdk-v1.32.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 repository: https://github.com/Kuadrant/kuadrant-operator diff --git a/bundle/manifests/kuadrant.io_dnspolicies.yaml b/bundle/manifests/kuadrant.io_dnspolicies.yaml index 3a2ddd777..2ebe24e16 100644 --- a/bundle/manifests/kuadrant.io_dnspolicies.yaml +++ b/bundle/manifests/kuadrant.io_dnspolicies.yaml @@ -66,6 +66,15 @@ spec: spec: description: DNSPolicySpec defines the desired state of DNSPolicy properties: + excludeAddresses: + description: ExcludeAddresses is a list of addresses (either hostnames, + CIDR or IPAddresses) that DNSPolicy should not use as values in + the configured DNS provider records. The default is to allow all + addresses configured in the Gateway DNSPolicy is targeting + items: + type: string + maxItems: 20 + type: array healthCheck: description: |- HealthCheckSpec configures health checks in the DNS provider. diff --git a/config/crd/bases/kuadrant.io_dnspolicies.yaml b/config/crd/bases/kuadrant.io_dnspolicies.yaml index 32aaf6407..4c282be0b 100644 --- a/config/crd/bases/kuadrant.io_dnspolicies.yaml +++ b/config/crd/bases/kuadrant.io_dnspolicies.yaml @@ -65,6 +65,15 @@ spec: spec: description: DNSPolicySpec defines the desired state of DNSPolicy properties: + excludeAddresses: + description: ExcludeAddresses is a list of addresses (either hostnames, + CIDR or IPAddresses) that DNSPolicy should not use as values in + the configured DNS provider records. The default is to allow all + addresses configured in the Gateway DNSPolicy is targeting + items: + type: string + maxItems: 20 + type: array healthCheck: description: |- HealthCheckSpec configures health checks in the DNS provider. diff --git a/controllers/dnspolicy_controller.go b/controllers/dnspolicy_controller.go index 4147276d1..749c6efc6 100644 --- a/controllers/dnspolicy_controller.go +++ b/controllers/dnspolicy_controller.go @@ -133,7 +133,7 @@ func (r *DNSPolicyReconciler) reconcileResources(ctx context.Context, dnsPolicy } if err = r.reconcileDNSRecords(ctx, dnsPolicy, gatewayDiffObj); err != nil { - return fmt.Errorf("reconcile DNSRecords error %w", err) + return fmt.Errorf("error reconciling DNSRecords %w", err) } // set direct back ref - i.e. claim the target network object as taken asap diff --git a/controllers/dnspolicy_dnsrecords.go b/controllers/dnspolicy_dnsrecords.go index 4b5193c76..75a6f7e12 100644 --- a/controllers/dnspolicy_dnsrecords.go +++ b/controllers/dnspolicy_dnsrecords.go @@ -35,7 +35,7 @@ func (r *DNSPolicyReconciler) reconcileDNSRecords(ctx context.Context, dnsPolicy for _, gw := range append(gwDiffObj.GatewaysWithValidPolicyRef, gwDiffObj.GatewaysMissingPolicyRef...) { log.V(1).Info("reconcileDNSRecords: gateway with valid or missing policy ref", "key", gw.Key()) if err := r.reconcileGatewayDNSRecords(ctx, gw.Gateway, dnsPolicy); err != nil { - return fmt.Errorf("error reconciling dns records for gateway %v: %w", gw.Gateway.Name, err) + return fmt.Errorf("reconciling dns records for gateway %v: error %w", gw.Gateway.Name, err) } } return nil @@ -52,6 +52,12 @@ func (r *DNSPolicyReconciler) reconcileGatewayDNSRecords(ctx context.Context, gw return err } + //ensure only approved addresses are considered for DNS records + if err := gatewayWrapper.SetValidStatusAddresses(dnsPolicy); err != nil { + log.V(3).Info("error setting valid addresses based on DNSPolicy") + return err + } + if err := r.dnsHelper.removeDNSForDeletedListeners(ctx, gatewayWrapper.Gateway); err != nil { log.V(3).Info("error removing DNS for deleted listeners") return err @@ -98,6 +104,14 @@ func (r *DNSPolicyReconciler) reconcileGatewayDNSRecords(ctx context.Context, gw return err } + if len(dnsRecord.Spec.Endpoints) == 0 { + log.V(1).Info("no endpoint addresses for DNSRecord ", "removing any records for listener", listener) + if err := r.dnsHelper.deleteDNSRecordForListener(ctx, gatewayWrapper, listener); client.IgnoreNotFound(err) != nil { + return err + } + return fmt.Errorf("no valid addresses for DNSRecord endpoints. Check allowedAddresses") + } + err = r.ReconcileResource(ctx, &kuadrantdnsv1alpha1.DNSRecord{}, dnsRecord, dnsRecordBasicMutator) if err != nil && !apierrors.IsAlreadyExists(err) { log.Error(err, "ReconcileResource failed to create/update DNSRecord resource") diff --git a/examples/dnspolicy/dnspolicy-exclude-address.yaml b/examples/dnspolicy/dnspolicy-exclude-address.yaml new file mode 100644 index 000000000..64be973a7 --- /dev/null +++ b/examples/dnspolicy/dnspolicy-exclude-address.yaml @@ -0,0 +1,20 @@ +apiVersion: kuadrant.io/v1alpha1 +kind: DNSPolicy +metadata: + name: prod-web + namespace: ${DNSPOLICY_NAMESPACE} +spec: + targetRef: + name: prod-web-istio + group: gateway.networking.k8s.io + kind: Gateway + providerRefs: + - name: aws-credentials + loadBalancing: + weight: 120 + geo: EU + defaultGeo: true + excludeAddresses: + - "10.89.0.0/16" + - "some.local.domain" + - "127.0.0.1" diff --git a/go.mod b/go.mod index 41a95aef0..1575d55a5 100644 --- a/go.mod +++ b/go.mod @@ -15,8 +15,8 @@ require ( github.com/kuadrant/limitador-operator v0.9.0 github.com/kuadrant/policy-machinery v0.2.0 github.com/martinlindhe/base36 v1.1.1 - github.com/onsi/ginkgo/v2 v2.17.2 - github.com/onsi/gomega v1.33.1 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.1 github.com/prometheus/client_golang v1.19.1 github.com/samber/lo v1.39.0 go.uber.org/zap v1.27.0 @@ -86,7 +86,7 @@ require ( github.com/google/btree v1.1.2 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 // indirect + github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect @@ -153,16 +153,16 @@ require ( go.opentelemetry.io/otel/trace v1.28.0 // indirect go.starlark.net v0.0.0-20240520160348-046347dcd104 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.25.0 // indirect - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/net v0.27.0 // indirect + golang.org/x/crypto v0.26.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.28.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.22.0 // indirect - golang.org/x/term v0.22.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240730163845-b1a4ccb954bf // indirect diff --git a/go.sum b/go.sum index 9308c8244..062077f5c 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -251,8 +251,6 @@ github.com/kuadrant/dns-operator v0.0.0-20240809151102-e79ebbca8f70 h1:Jiq7dZWae github.com/kuadrant/dns-operator v0.0.0-20240809151102-e79ebbca8f70/go.mod h1:Aq4LYFwhBzQYUew71KjtWPKr+e0jzgraX10Ki8wIKCY= github.com/kuadrant/limitador-operator v0.9.0 h1:hTQ6CFPayf/sL7cIzwWjCoU8uTn6fzWdsJgKbDlnFts= github.com/kuadrant/limitador-operator v0.9.0/go.mod h1:DQOlg9qFOcnWPrwO529JRCMLLOEXJQxkmOes952S/Hw= -github.com/kuadrant/policy-machinery v0.1.1 h1:8NPwL5U79Y+amCpJpyAJBUqWfntfDrce0JK31ueRMns= -github.com/kuadrant/policy-machinery v0.1.1/go.mod h1:fbBqBlh7iyFdU6dRCzaPadypaI/BSHWKiUVl5kesrYY= github.com/kuadrant/policy-machinery v0.2.0 h1:6kACb+bdEwHXz2tvTs6dlLgvxFgFrowvGTZKMI9p0Qo= github.com/kuadrant/policy-machinery v0.2.0/go.mod h1:ZV4xS0CCxPgu/Xg6gz+YUaS9zqEXKOiAj33bZ67B6Lo= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -329,10 +327,10 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -498,15 +496,15 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -516,8 +514,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -526,8 +524,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -545,19 +543,19 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -565,8 +563,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pkg/multicluster/gateway_wrapper.go b/pkg/multicluster/gateway_wrapper.go index 91326611f..47b7a7380 100644 --- a/pkg/multicluster/gateway_wrapper.go +++ b/pkg/multicluster/gateway_wrapper.go @@ -2,10 +2,13 @@ package multicluster import ( "fmt" + "net" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kuadrant/kuadrant-operator/api/v1alpha1" ) const ( @@ -24,6 +27,38 @@ func NewGatewayWrapper(g *gatewayapiv1.Gateway, clusterID string) *GatewayWrappe return &GatewayWrapper{Gateway: g, ClusterID: clusterID} } +func (g *GatewayWrapper) SetValidStatusAddresses(p *v1alpha1.DNSPolicy) error { + newAddresses := []gatewayapiv1.GatewayStatusAddress{} + for _, address := range g.Gateway.Status.Addresses { + found := false + for _, exclude := range p.Spec.ExcludeAddresses { + //Only a CIDR will have / in the address so attempt to parse fail if not valid + if strings.Contains(exclude, "/") { + _, network, err := net.ParseCIDR(exclude) + if err != nil { + return fmt.Errorf("could not parse the CIDR from the excludeAddresses field %w", err) + } + ip := net.ParseIP(address.Value) + // only check addresses that are actually IPs + if ip != nil && network.Contains(ip) { + found = true + break + } + } + if exclude == address.Value { + found = true + break + } + } + if !found { + newAddresses = append(newAddresses, address) + } + } + // setting this in memory only wont be saved to actual gateway + g.Status.Addresses = newAddresses + return nil +} + func isMultiClusterAddressType(addressType gatewayapiv1.AddressType) bool { return addressType == MultiClusterIPAddressType || addressType == MultiClusterHostnameAddressType } diff --git a/pkg/multicluster/gateway_wrapper_test.go b/pkg/multicluster/gateway_wrapper_test.go new file mode 100644 index 000000000..b13ec159f --- /dev/null +++ b/pkg/multicluster/gateway_wrapper_test.go @@ -0,0 +1,169 @@ +package multicluster_test + +import ( + "testing" + + gatewayapiv1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/kuadrant/kuadrant-operator/api/v1alpha1" + "github.com/kuadrant/kuadrant-operator/pkg/multicluster" +) + +func TestSetValidStatusAddresses(t *testing.T) { + ipaddress := gatewayapiv1.IPAddressType + hostaddress := gatewayapiv1.HostnameAddressType + testCases := []struct { + Name string + Gateway *gatewayapiv1.Gateway + DNSPolicy *v1alpha1.DNSPolicy + Validate func(t *testing.T, g *gatewayapiv1.GatewayStatus) + ExpectErr bool + }{ + { + Name: "ensure addresses in ingore are are removed from status", + Gateway: &gatewayapiv1.Gateway{ + Status: gatewayapiv1.GatewayStatus{ + Addresses: []gatewayapiv1.GatewayStatusAddress{ + { + Type: &ipaddress, + Value: "1.1.1.1", + }, + { + Type: &hostaddress, + Value: "example.com", + }, + }, + }, + }, + DNSPolicy: &v1alpha1.DNSPolicy{ + Spec: v1alpha1.DNSPolicySpec{ + ExcludeAddresses: []string{ + "1.1.1.1", + }, + }, + }, + Validate: func(t *testing.T, g *gatewayapiv1.GatewayStatus) { + if len(g.Addresses) != 1 { + t.Fatalf("expected a single address but got %v ", len(g.Addresses)) + } + for _, addr := range g.Addresses { + if addr.Value == "1.1.1.1" { + t.Fatalf("did not expect address %s to be present", "1.1.1.1") + } + } + }, + }, + { + Name: "ensure all addresses if nothing ignored", + Gateway: &gatewayapiv1.Gateway{ + Status: gatewayapiv1.GatewayStatus{ + Addresses: []gatewayapiv1.GatewayStatusAddress{ + { + Type: &ipaddress, + Value: "1.1.1.1", + }, + { + Type: &hostaddress, + Value: "example.com", + }, + }, + }, + }, + DNSPolicy: &v1alpha1.DNSPolicy{ + Spec: v1alpha1.DNSPolicySpec{ + ExcludeAddresses: []string{}, + }, + }, + Validate: func(t *testing.T, g *gatewayapiv1.GatewayStatus) { + if len(g.Addresses) != 2 { + t.Fatalf("expected a both address but got %v ", len(g.Addresses)) + } + }, + }, + { + Name: "ensure addresses removed if CIDR is set and hostname", + Gateway: &gatewayapiv1.Gateway{ + Status: gatewayapiv1.GatewayStatus{ + Addresses: []gatewayapiv1.GatewayStatusAddress{ + { + Type: &ipaddress, + Value: "1.1.1.1", + }, + { + Type: &hostaddress, + Value: "example.com", + }, + { + Type: &ipaddress, + Value: "81.17.21.22", + }, + }, + }, + }, + DNSPolicy: &v1alpha1.DNSPolicy{ + Spec: v1alpha1.DNSPolicySpec{ + ExcludeAddresses: []string{ + "1.1.0.0/16", + "10.0.0.1/32", + "example.com", + }, + }, + }, + Validate: func(t *testing.T, g *gatewayapiv1.GatewayStatus) { + if len(g.Addresses) != 1 { + t.Fatalf("expected only a single address but got %v %v ", len(g.Addresses), g.Addresses) + } + if g.Addresses[0].Value != "81.17.21.22" { + t.Fatalf("expected the only remaining address to be 81.17.21.22 but got %s", g.Addresses[0].Value) + } + }, + }, + { + Name: "ensure invalid CIDR causes error", + ExpectErr: true, + Gateway: &gatewayapiv1.Gateway{ + Status: gatewayapiv1.GatewayStatus{ + Addresses: []gatewayapiv1.GatewayStatusAddress{ + { + Type: &ipaddress, + Value: "1.1.1.1", + }, + { + Type: &hostaddress, + Value: "example.com", + }, + { + Type: &ipaddress, + Value: "81.17.21.22", + }, + }, + }, + }, + DNSPolicy: &v1alpha1.DNSPolicy{ + Spec: v1alpha1.DNSPolicySpec{ + ExcludeAddresses: []string{ + "1.1.0.0/161", + "example.com", + }, + }, + }, + Validate: func(t *testing.T, g *gatewayapiv1.GatewayStatus) { + return + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + gw := multicluster.NewGatewayWrapper(tc.Gateway, "id") + err := gw.SetValidStatusAddresses(tc.DNSPolicy) + if err != nil && !tc.ExpectErr { + t.Fatalf("unexpected error %s", err) + } + if tc.ExpectErr && err == nil { + t.Fatalf("expected an error but got none") + } + tc.Validate(t, &gw.Status) + }) + } +} diff --git a/tests/common/dnspolicy/dnspolicy_controller_test.go b/tests/common/dnspolicy/dnspolicy_controller_test.go index 5b8b688fa..7542637ec 100644 --- a/tests/common/dnspolicy/dnspolicy_controller_test.go +++ b/tests/common/dnspolicy/dnspolicy_controller_test.go @@ -834,4 +834,137 @@ var _ = Describe("DNSPolicy controller", func() { Expect(err.Error()).To(ContainSubstring("Invalid targetRef.kind. The only supported values are 'Gateway'")) }, testTimeOut) }) + + Context("excludeAddresses from DNS", func() { + BeforeEach(func(ctx SpecContext) { + gateway = tests.NewGatewayBuilder(tests.GatewayName, gatewayClass.Name, testNamespace). + WithHTTPListener(tests.ListenerNameOne, tests.HostOne(domain)). + WithHTTPListener(tests.ListenerNameWildcard, tests.HostWildcard(domain)). + Gateway + Expect(k8sClient.Create(ctx, gateway)).To(Succeed()) + }) + It("should create a DNSPolicy with an invalid CIDR", func(ctx SpecContext) { + dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). + WithTargetGateway(gateway.Name). + WithExcludeAddresses([]string{"1.1.1.1/345"}) + Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(gateway), gateway)).To(Succeed()) + gateway.Status.Addresses = []gatewayapiv1.GatewayStatusAddress{ + { + Type: ptr.To(gatewayapiv1.IPAddressType), + Value: tests.IPAddressOne, + }, + { + Type: ptr.To(gatewayapiv1.IPAddressType), + Value: tests.IPAddressTwo, + }, + } + gateway.Status.Listeners = []gatewayapiv1.ListenerStatus{ + { + Name: tests.ListenerNameOne, + SupportedKinds: []gatewayapiv1.RouteGroupKind{}, + AttachedRoutes: 1, + Conditions: []metav1.Condition{}, + }, + { + Name: tests.ListenerNameWildcard, + SupportedKinds: []gatewayapiv1.RouteGroupKind{}, + AttachedRoutes: 1, + Conditions: []metav1.Condition{}, + }, + } + g.Expect(k8sClient.Status().Update(ctx, gateway)).To(Succeed()) + }, tests.TimeoutMedium, tests.RetryIntervalMedium).Should(Succeed()) + + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsPolicy), dnsPolicy) + g.Expect(err).NotTo(HaveOccurred()) + fmt.Println("conditions", dnsPolicy.Status.Conditions) + g.Expect(dnsPolicy.Status.Conditions).To( + ContainElement(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(string(gatewayapiv1alpha2.PolicyConditionAccepted)), + "Status": Equal(metav1.ConditionFalse), + "Message": ContainSubstring("could not parse the CIDR from the excludeAddresses field"), + })), + ) + }, tests.TimeoutMedium, time.Second).Should(Succeed()) + + }) + + It("should create a DNSPolicy valid exclude addresses", func(ctx SpecContext) { + dnsPolicy = v1alpha1.NewDNSPolicy("test-dns-policy", testNamespace). + WithProviderSecret(*dnsProviderSecret). + WithTargetGateway(gateway.Name). + WithExcludeAddresses([]string{tests.IPAddressOne}) + Expect(k8sClient.Create(ctx, dnsPolicy)).To(Succeed()) + Eventually(func(g Gomega) { + g.Expect(k8sClient.Get(ctx, client.ObjectKeyFromObject(gateway), gateway)).To(Succeed()) + gateway.Status.Addresses = []gatewayapiv1.GatewayStatusAddress{ + { + Type: ptr.To(gatewayapiv1.IPAddressType), + Value: tests.IPAddressOne, + }, + { + Type: ptr.To(gatewayapiv1.IPAddressType), + Value: tests.IPAddressTwo, + }, + } + gateway.Status.Listeners = []gatewayapiv1.ListenerStatus{ + { + Name: tests.ListenerNameOne, + SupportedKinds: []gatewayapiv1.RouteGroupKind{}, + AttachedRoutes: 1, + Conditions: []metav1.Condition{}, + }, + { + Name: tests.ListenerNameWildcard, + SupportedKinds: []gatewayapiv1.RouteGroupKind{}, + AttachedRoutes: 1, + Conditions: []metav1.Condition{}, + }, + } + g.Expect(k8sClient.Status().Update(ctx, gateway)).To(Succeed()) + }, tests.TimeoutMedium, tests.RetryIntervalMedium).Should(Succeed()) + + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(dnsPolicy), dnsPolicy) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(dnsPolicy.Status.Conditions).To( + ContainElement(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(string(gatewayapiv1alpha2.PolicyConditionAccepted)), + "Status": Equal(metav1.ConditionTrue), + })), + ) + g.Expect(dnsPolicy.Status.Conditions).To( + ContainElement(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(string(kuadrant.PolicyConditionEnforced)), + "Status": Equal(metav1.ConditionTrue), + "Reason": Equal(string(kuadrant.PolicyReasonEnforced)), + "Message": Equal("DNSPolicy has been successfully enforced"), + })), + ) + recordName = fmt.Sprintf("%s-%s", tests.GatewayName, tests.ListenerNameOne) + rec := &kuadrantdnsv1alpha1.DNSRecord{} + g.Expect(k8sClient.Get(ctx, client.ObjectKey{Name: recordName, Namespace: testNamespace}, rec)).To(Succeed()) + foundExcluded := false + foundAllowed := false + for _, ep := range rec.Spec.Endpoints { + for _, t := range ep.Targets { + if t == tests.IPAddressOne { + foundExcluded = true + } + if t == tests.IPAddressTwo { + foundAllowed = true + } + } + } + g.Expect(foundExcluded).To(BeFalse()) + g.Expect(foundAllowed).To(BeTrue()) + + }, tests.TimeoutMedium, time.Second).Should(Succeed()) + + }) + }) })