mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-12 17:59:06 +02:00
Match DNS zones case-insensitively and replace only the zone suffix
This commit is contained in:
@@ -349,12 +349,21 @@ func (a *Anonymizer) anonymizeDomain(domain string, newZone bool) string {
|
||||
a.mapDomain(baseForLookup, anonymized)
|
||||
}
|
||||
|
||||
result := strings.Replace(baseDomain, baseForLookup, anonymized, 1)
|
||||
if a.level >= LevelStrict && baseDomain != baseForLookup {
|
||||
prefix := strings.TrimSuffix(baseDomain, "."+baseForLookup)
|
||||
result = a.anonymizeLabels(prefix, "host") + "." + anonymized
|
||||
// The full mapping feeds AnonymizeString so seeded FQDNs are caught
|
||||
// in log lines as a whole, labels included.
|
||||
// baseForLookup is the tail of baseDomain up to case, so cut by length: a
|
||||
// substring match would rewrite an earlier occurrence and leave the real
|
||||
// zone in place, as with a name whose own label repeats the zone.
|
||||
prefix := baseDomain[:len(baseDomain)-len(baseForLookup)]
|
||||
|
||||
result := prefix + anonymized
|
||||
if a.level >= LevelStrict && prefix != "" {
|
||||
result = a.anonymizeLabels(strings.TrimSuffix(prefix, "."), "host") + "." + anonymized
|
||||
}
|
||||
|
||||
// A dotless zone is held out of the substring pass, so a name under it
|
||||
// needs its own mapping to be replaced where a log line mentions it as
|
||||
// free text rather than as a DNS name. Strict level records every name
|
||||
// anyway, labels included.
|
||||
if prefix != "" && (a.level >= LevelStrict || !strings.Contains(baseForLookup, ".")) {
|
||||
a.mapDomain(baseDomain, result)
|
||||
}
|
||||
return withTrailingDot(result, hasDot)
|
||||
@@ -368,7 +377,9 @@ func (a *Anonymizer) mapDomain(key, anonymized string) {
|
||||
}
|
||||
|
||||
// baseForLookup returns the key under which baseDomain's anonymized base is
|
||||
// stored, or empty to leave baseDomain alone.
|
||||
// stored, or empty to leave baseDomain alone. The key is lower case, since DNS
|
||||
// labels compare case-insensitively, and is always the tail of baseDomain so a
|
||||
// caller can cut it off by length.
|
||||
//
|
||||
// The key is normally the last two labels, so every name under a domain shares
|
||||
// one anonymized base. A single-label zone is its own key: names under it then
|
||||
@@ -377,7 +388,7 @@ func (a *Anonymizer) mapDomain(key, anonymized string) {
|
||||
// needs newZone to become one, since free text and address forms also reach
|
||||
// here and must not be rewritten on a guess.
|
||||
func (a *Anonymizer) baseForLookup(baseDomain string, newZone bool) string {
|
||||
parts := strings.Split(baseDomain, ".")
|
||||
parts := strings.Split(strings.ToLower(baseDomain), ".")
|
||||
last := parts[len(parts)-1]
|
||||
|
||||
if _, known := a.domainAnonymizer[last]; known {
|
||||
@@ -387,7 +398,7 @@ func (a *Anonymizer) baseForLookup(baseDomain string, newZone bool) string {
|
||||
return parts[len(parts)-2] + "." + last
|
||||
}
|
||||
|
||||
if !newZone || last == "" || strings.EqualFold(last, "localhost") {
|
||||
if !newZone || last == "" || last == "localhost" {
|
||||
return ""
|
||||
}
|
||||
return last
|
||||
|
||||
@@ -388,6 +388,57 @@ func TestAnonymizeDomain_SingleLabelZone(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnonymizeDomainName_ZoneLabelRepeated covers a name whose own label
|
||||
// repeats its zone. Cutting the key by a substring match would rewrite the
|
||||
// leading occurrence and leave the real zone in the output.
|
||||
func TestAnonymizeDomainName_ZoneLabelRepeated(t *testing.T) {
|
||||
for _, level := range []anonymize.Level{anonymize.LevelDefault, anonymize.LevelStrict} {
|
||||
t.Run(level.String(), func(t *testing.T) {
|
||||
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses())
|
||||
anonymizer.SetLevel(level)
|
||||
|
||||
zone := anonymizer.AnonymizeDomainName("corp.")
|
||||
record := anonymizer.AnonymizeDomainName("corp.corp.")
|
||||
|
||||
// The zone is the tail, so it is the tail that must be replaced.
|
||||
// The leading label is a host label, which the default level
|
||||
// preserves like any other and strict level renumbers.
|
||||
assert.True(t, strings.HasSuffix(record, "."+zone), "the record should sit under the anonymized zone, got %q for zone %q", record, zone)
|
||||
assert.NotContains(t, strings.TrimSuffix(record, "."+zone), ".", "only the host label should remain in front of the zone, got %q", record)
|
||||
if level >= anonymize.LevelStrict {
|
||||
assert.NotContains(t, record, "corp", "strict level should also replace the host label")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAnonymizeDomainName_ZoneCaseInsensitive covers a zone and a record that
|
||||
// disagree on case. DNS labels compare case-insensitively, so both must resolve
|
||||
// to one anonymized base.
|
||||
func TestAnonymizeDomainName_ZoneCaseInsensitive(t *testing.T) {
|
||||
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses())
|
||||
|
||||
zone := anonymizer.AnonymizeDomainName("Corp.")
|
||||
record := anonymizer.AnonymizeDomainName("host.corp.")
|
||||
|
||||
assert.True(t, strings.HasSuffix(record, "."+zone), "a differently cased record should share the zone's base, got %q for zone %q", record, zone)
|
||||
assert.NotContains(t, strings.ToLower(record), "corp", "the original zone name should not survive")
|
||||
}
|
||||
|
||||
// TestAnonymizeString_NameUnderSingleLabelZoneInLogText covers a name under a
|
||||
// dotless zone appearing as free text. The zone itself is held out of the
|
||||
// substring pass, so the name needs its own mapping to be replaced.
|
||||
func TestAnonymizeString_NameUnderSingleLabelZoneInLogText(t *testing.T) {
|
||||
anonymizer := anonymize.NewAnonymizer(anonymize.DefaultAddresses())
|
||||
|
||||
anonymizer.AnonymizeDomainName("corp")
|
||||
record := anonymizer.AnonymizeDomainName("host.corp")
|
||||
|
||||
result := anonymizer.AnonymizeString("registering handler for [host.corp]")
|
||||
assert.Equal(t, "registering handler for ["+record+"]", result,
|
||||
"a name under a dotless zone should be replaced in free log text")
|
||||
}
|
||||
|
||||
// TestAnonymizeString_KnownZoneInLogLine covers the bundle order: the DNS
|
||||
// config establishes the zone, then log lines naming it are anonymized through
|
||||
// the domain= key even though the name carries no dot of its own.
|
||||
|
||||
Reference in New Issue
Block a user