mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-06 17:46:57 +02:00
fix(idna): fix handling of domains
Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
This commit is contained in:
parent
d8f31eb27e
commit
e9ec3acdaa
@ -25,7 +25,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/idna"
|
|
||||||
|
"sigs.k8s.io/external-dns/internal/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MatchAllDomainFilters []DomainFilterInterface
|
type MatchAllDomainFilters []DomainFilterInterface
|
||||||
@ -247,9 +248,9 @@ func (df *DomainFilter) MatchParent(domain string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// normalizeDomain converts a domain to a canonical form, so that we can filter on it
|
// normalizeDomain converts a domain to a canonical form, so that we can filter on it
|
||||||
// it: trim "." suffix, get Unicode version of domain complient with Section 5 of RFC 5891
|
// it: trim "." suffix, get Unicode version of domain compliant with Section 5 of RFC 5891
|
||||||
func normalizeDomain(domain string) string {
|
func normalizeDomain(domain string) string {
|
||||||
s, err := idna.Lookup.ToUnicode(strings.TrimSuffix(domain, "."))
|
s, err := idna.Profile.ToUnicode(strings.TrimSuffix(domain, "."))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(`Got error while parsing domain %s: %v`, domain, err)
|
log.Warnf(`Got error while parsing domain %s: %v`, domain, err)
|
||||||
}
|
}
|
||||||
|
29
internal/idna/idna.go
Normal file
29
internal/idna/idna.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/idna"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Profile = idna.New(
|
||||||
|
idna.MapForLookup(),
|
||||||
|
idna.Transitional(true),
|
||||||
|
idna.StrictDomainName(false),
|
||||||
|
)
|
||||||
|
)
|
59
internal/idna/idna_test.go
Normal file
59
internal/idna/idna_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package idna
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProfileWithDefault(t *testing.T) {
|
||||||
|
tets := []struct {
|
||||||
|
input string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "*.GÖPHER.com",
|
||||||
|
expected: "*.göpher.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "*._abrakadabra.com",
|
||||||
|
expected: "*._abrakadabra.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "_abrakadabra.com",
|
||||||
|
expected: "_abrakadabra.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "*.foo.kube.example.com",
|
||||||
|
expected: "*.foo.kube.example.com",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "xn--bcher-kva.example.com",
|
||||||
|
expected: "bücher.example.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tets {
|
||||||
|
t.Run(strings.ToLower(tt.input), func(t *testing.T) {
|
||||||
|
result, err := Profile.ToUnicode(tt.input)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
10
plan/plan.go
10
plan/plan.go
@ -23,9 +23,9 @@ import (
|
|||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"golang.org/x/net/idna"
|
|
||||||
|
|
||||||
"sigs.k8s.io/external-dns/endpoint"
|
"sigs.k8s.io/external-dns/endpoint"
|
||||||
|
"sigs.k8s.io/external-dns/internal/idna"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PropertyComparator is used in Plan for comparing the previous and current custom annotations.
|
// PropertyComparator is used in Plan for comparing the previous and current custom annotations.
|
||||||
@ -340,16 +340,10 @@ func filterRecordsForPlan(records []*endpoint.Endpoint, domainFilter endpoint.Ma
|
|||||||
return filtered
|
return filtered
|
||||||
}
|
}
|
||||||
|
|
||||||
var idnaProfile = idna.New(
|
|
||||||
idna.MapForLookup(),
|
|
||||||
idna.Transitional(true),
|
|
||||||
idna.StrictDomainName(false),
|
|
||||||
)
|
|
||||||
|
|
||||||
// normalizeDNSName converts a DNS name to a canonical form, so that we can use string equality
|
// normalizeDNSName converts a DNS name to a canonical form, so that we can use string equality
|
||||||
// it: removes space, get ASCII version of dnsName complient with Section 5 of RFC 5891, ensures there is a trailing dot
|
// it: removes space, get ASCII version of dnsName complient with Section 5 of RFC 5891, ensures there is a trailing dot
|
||||||
func normalizeDNSName(dnsName string) string {
|
func normalizeDNSName(dnsName string) string {
|
||||||
s, err := idnaProfile.ToASCII(strings.TrimSpace(dnsName))
|
s, err := idna.Profile.ToASCII(strings.TrimSpace(dnsName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf(`Got error while parsing DNSName %s: %v`, dnsName, err)
|
log.Warnf(`Got error while parsing DNSName %s: %v`, dnsName, err)
|
||||||
}
|
}
|
||||||
|
@ -734,7 +734,10 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
|
|||||||
log.Infof("Desired change: %s %s %s", c.Action, *c.ResourceRecordSet.Name, c.ResourceRecordSet.Type)
|
log.Infof("Desired change: %s %s %s", c.Action, *c.ResourceRecordSet.Name, c.ResourceRecordSet.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.dryRun {
|
if p.dryRun {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
params := &route53.ChangeResourceRecordSetsInput{
|
params := &route53.ChangeResourceRecordSetsInput{
|
||||||
HostedZoneId: aws.String(z),
|
HostedZoneId: aws.String(z),
|
||||||
ChangeBatch: &route53types.ChangeBatch{
|
ChangeBatch: &route53types.ChangeBatch{
|
||||||
@ -785,7 +788,6 @@ func (p *AWSProvider) submitChanges(ctx context.Context, changes Route53Changes,
|
|||||||
time.Sleep(p.batchChangeInterval)
|
time.Sleep(p.batchChangeInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if failedUpdate {
|
if failedUpdate {
|
||||||
failedZones = append(failedZones, z)
|
failedZones = append(failedZones, z)
|
||||||
|
@ -32,6 +32,7 @@ func TestZoneIDName(t *testing.T) {
|
|||||||
z.Add("654321", "foo.qux.baz")
|
z.Add("654321", "foo.qux.baz")
|
||||||
z.Add("987654", "エイミー.みんな")
|
z.Add("987654", "エイミー.みんな")
|
||||||
z.Add("123123", "_metadata.example.com")
|
z.Add("123123", "_metadata.example.com")
|
||||||
|
z.Add("1231231", "_foo._metadata.example.com")
|
||||||
z.Add("456456", "_metadata.エイミー.みんな")
|
z.Add("456456", "_metadata.エイミー.みんな")
|
||||||
|
|
||||||
assert.Equal(t, ZoneIDName{
|
assert.Equal(t, ZoneIDName{
|
||||||
@ -39,6 +40,7 @@ func TestZoneIDName(t *testing.T) {
|
|||||||
"654321": "foo.qux.baz",
|
"654321": "foo.qux.baz",
|
||||||
"987654": "エイミー.みんな",
|
"987654": "エイミー.みんな",
|
||||||
"123123": "_metadata.example.com",
|
"123123": "_metadata.example.com",
|
||||||
|
"1231231": "_foo._metadata.example.com",
|
||||||
"456456": "_metadata.エイミー.みんな",
|
"456456": "_metadata.エイミー.みんな",
|
||||||
}, z)
|
}, z)
|
||||||
|
|
||||||
@ -77,6 +79,10 @@ func TestZoneIDName(t *testing.T) {
|
|||||||
assert.Equal(t, "エイミー.みんな", zoneName)
|
assert.Equal(t, "エイミー.みんな", zoneName)
|
||||||
assert.Equal(t, "987654", zoneID)
|
assert.Equal(t, "987654", zoneID)
|
||||||
|
|
||||||
|
zoneID, zoneName = z.FindZone("_foo._metadata.example.com")
|
||||||
|
assert.Equal(t, "_foo._metadata.example.com", zoneName)
|
||||||
|
assert.Equal(t, "1231231", zoneID)
|
||||||
|
|
||||||
hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
|
hook := testutils.LogsUnderTestWithLogLevel(log.WarnLevel, t)
|
||||||
_, _ = z.FindZone("???")
|
_, _ = z.FindZone("???")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user