From 5ff2b6dd7e5b5edabf6ed858137566b16674f251 Mon Sep 17 00:00:00 2001 From: Christian Rohmann Date: Mon, 16 Jun 2025 23:56:49 +0200 Subject: [PATCH] fix: append dot to recordName and target as required by RFC 2782 According to RFC2782 [1], SRV records are to be fully-qualified, ending with a final dot. Most providers are liberal, but e.g. OpenStack Designate expects the record name and targets to end with a dot ([2]) [1] https://datatracker.ietf.org/doc/html/rfc2782 [2] https://github.com/openstack/designate Signed-off-by: Christian Rohmann --- source/service.go | 5 +++-- source/service_test.go | 18 +++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/service.go b/source/service.go index 3ae05a8d5..fbc49ead0 100644 --- a/source/service.go +++ b/source/service.go @@ -35,6 +35,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" + "sigs.k8s.io/external-dns/provider" "sigs.k8s.io/external-dns/source/informers" "sigs.k8s.io/external-dns/source/annotations" @@ -719,7 +720,7 @@ func (sc *serviceSource) extractNodePortEndpoints(svc *v1.Service, hostname stri // see https://en.wikipedia.org/wiki/SRV_record // build a target with a priority of 0, weight of 50, and pointing the given port on the given host - target := fmt.Sprintf("0 50 %d %s", port.NodePort, hostname) + target := fmt.Sprintf("0 50 %d %s", port.NodePort, provider.EnsureTrailingDot(hostname)) // take the service name from the K8s Service object // it is safe to use since it is DNS compatible @@ -732,7 +733,7 @@ func (sc *serviceSource) extractNodePortEndpoints(svc *v1.Service, hostname stri protocol = "tcp" } - recordName := fmt.Sprintf("_%s._%s.%s", serviceName, protocol, hostname) + recordName := fmt.Sprintf("_%s._%s.%s", serviceName, protocol, provider.EnsureTrailingDot(hostname)) var ep *endpoint.Endpoint if ttl.IsConfigured() { diff --git a/source/service_test.go b/source/service_test.go index cad1012f9..28f7ed99e 100644 --- a/source/service_test.go +++ b/source/service_test.go @@ -1718,7 +1718,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -1789,7 +1789,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { svcTrafficPolicy: v1.ServiceExternalTrafficPolicyTypeCluster, fqdnTemplate: "{{.Name}}.bar.example.com", expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.bar.example.com", Targets: endpoint.Targets{"0 50 30192 foo.bar.example.com"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.bar.example.com.", Targets: endpoint.Targets{"0 50 30192 foo.bar.example.com."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.bar.example.com", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -1827,7 +1827,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -1863,7 +1863,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -1907,7 +1907,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -1954,7 +1954,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, }, nodes: []*v1.Node{{ @@ -1998,7 +1998,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { hostnameAnnotationKey: "foo.example.org.", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1"}, RecordType: endpoint.RecordTypeA}, }, nodes: []*v1.Node{{ @@ -2054,7 +2054,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { accessAnnotationKey: "private", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"10.0.1.1", "10.0.1.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, }, @@ -2094,7 +2094,7 @@ func TestServiceSourceNodePortServices(t *testing.T) { accessAnnotationKey: "public", }, expected: []*endpoint.Endpoint{ - {DNSName: "_foo._tcp.foo.example.org", Targets: endpoint.Targets{"0 50 30192 foo.example.org"}, RecordType: endpoint.RecordTypeSRV}, + {DNSName: "_foo._tcp.foo.example.org.", Targets: endpoint.Targets{"0 50 30192 foo.example.org."}, RecordType: endpoint.RecordTypeSRV}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"54.10.11.1", "54.10.11.2"}, RecordType: endpoint.RecordTypeA}, {DNSName: "foo.example.org", Targets: endpoint.Targets{"2001:DB8::1", "2001:DB8::2"}, RecordType: endpoint.RecordTypeAAAA}, },