Address review comments

This commit is contained in:
Viktor Liu
2026-08-07 16:55:42 +02:00
parent d0bc7d9310
commit fe960bd654
5 changed files with 51 additions and 36 deletions

View File

@@ -43,6 +43,7 @@ func ParseLevel(s string) Level {
}
}
// String returns the wire form of the level: "default" or "strict".
func (l Level) String() string {
if l >= LevelStrict {
return "strict"
@@ -67,17 +68,20 @@ var (
type Anonymizer struct {
ipAnonymizer map[netip.Addr]netip.Addr
domainAnonymizer map[string]string
labelAnonymizer map[string]string
labelAnonymized map[string]struct{}
labelCounter uint32
macAnonymizer map[string]string
macCounter uint32
wgKeyAnonymizer map[string]string
wgKeyAnonymized map[string]struct{}
currentAnonIPv4 netip.Addr
currentAnonIPv6 netip.Addr
startAnonIPv4 netip.Addr
startAnonIPv6 netip.Addr
// domainOrder caches the keys of domainAnonymizer sorted longest-first
// for AnonymizeString; it is rebuilt when the map gains entries.
domainOrder []string
labelAnonymizer map[string]string
labelAnonymized map[string]struct{}
labelCounter uint32
macAnonymizer map[string]string
macCounter uint32
wgKeyAnonymizer map[string]string
wgKeyAnonymized map[string]struct{}
currentAnonIPv4 netip.Addr
currentAnonIPv6 netip.Addr
startAnonIPv4 netip.Addr
startAnonIPv6 netip.Addr
// LevelStrict also anonymizes internal ranges (RFC 1918, CGNAT,
// link-local), replacing them from the dedicated internal pools below so
@@ -136,6 +140,10 @@ func (a *Anonymizer) SetLevel(level Level) {
}
func (a *Anonymizer) AnonymizeIP(ip netip.Addr) netip.Addr {
// Normalize 4-in-6 addresses so ::ffff:192.168.1.1 classifies and maps
// like 192.168.1.1.
ip = ip.Unmap()
if ip.IsLoopback() ||
ip.IsUnspecified() ||
ip.IsMulticast() ||
@@ -393,19 +401,7 @@ func (a *Anonymizer) AnonymizeString(str string) string {
str = ipv4Regex.ReplaceAllStringFunc(str, a.AnonymizeIPString)
str = ipv6Regex.ReplaceAllStringFunc(str, a.AnonymizeIPString)
// Longest mappings first, so a full-FQDN mapping (strict level) is applied
// before the base-domain mapping it contains.
domains := make([]string, 0, len(a.domainAnonymizer))
for domain := range a.domainAnonymizer {
domains = append(domains, domain)
}
slices.SortFunc(domains, func(x, y string) int {
if d := len(y) - len(x); d != 0 {
return d
}
return strings.Compare(x, y)
})
for _, domain := range domains {
for _, domain := range a.sortedDomains() {
str = strings.ReplaceAll(str, domain, a.domainAnonymizer[domain])
}
@@ -425,6 +421,27 @@ func (a *Anonymizer) AnonymizeString(str string) string {
return str
}
// sortedDomains returns the domain mappings longest-first, so a full-FQDN
// mapping (strict level) is applied before the base-domain mapping it
// contains. The order is rebuilt only when domainAnonymizer has grown.
func (a *Anonymizer) sortedDomains() []string {
if len(a.domainOrder) == len(a.domainAnonymizer) {
return a.domainOrder
}
a.domainOrder = a.domainOrder[:0]
for domain := range a.domainAnonymizer {
a.domainOrder = append(a.domainOrder, domain)
}
slices.SortFunc(a.domainOrder, func(x, y string) int {
if d := len(y) - len(x); d != 0 {
return d
}
return strings.Compare(x, y)
})
return a.domainOrder
}
// anonymizeMACsInString replaces MAC addresses matched by re, skipping
// matches that directly adjoin another sep so a six-group run inside a longer
// separated sequence is left alone.

View File

@@ -85,6 +85,9 @@ func TestAnonymizeIP_DefaultLevelInternalRanges(t *testing.T) {
// ULA is anonymized even at the default level: its random global ID
// uniquely fingerprints the network, unlike shared RFC 1918 space.
{"IPv6 ULA", "fd12:3456:789a::1", "2001:db8:ffff::"},
// 4-in-6 addresses classify like their unmapped IPv4 form.
{"4-in-6 RFC1918", "::ffff:192.168.1.1", "192.168.1.1"},
{"4-in-6 CGNAT", "::ffff:100.64.0.5", "100.64.0.5"},
}
for _, tc := range tests {
@@ -124,6 +127,7 @@ func TestAnonymizeIP_StrictLevel(t *testing.T) {
{"Well known split marker", "128.0.0.0", "128.0.0.0"},
{"In internal pool range", "198.18.0.3", "198.18.0.3"},
{"In public pool range", "198.51.100.0", "198.51.100.0"},
{"4-in-6 repeated RFC1918", "::ffff:192.168.1.1", "198.18.0.0"},
}
for _, tc := range tests {

View File

@@ -54,7 +54,11 @@ func (g *BundleGenerator) toWGShowFormat(s *configurer.Stats) string {
if len(peer.AllowedIPs) > 0 {
var ipStrings []string
for _, ipnet := range peer.AllowedIPs {
ipStrings = append(ipStrings, ipnet.String())
ipStr := ipnet.String()
if g.anonymize {
ipStr = g.anonymizer.AnonymizeIPString(ipStr)
}
ipStrings = append(ipStrings, ipStr)
}
sb.WriteString(fmt.Sprintf(" allowed ips: %s\n", strings.Join(ipStrings, ", ")))
}

View File

@@ -1013,7 +1013,6 @@ func anonymizeOverview(a *anonymize.Anonymizer, overview *OutputOverview) {
overview.SignalState.Error = a.AnonymizeString(overview.SignalState.Error)
overview.PubKey = a.AnonymizeWGKey(overview.PubKey)
overview.FQDN = a.AnonymizeDomain(overview.FQDN)
overview.IP = a.AnonymizeIPString(overview.IP)
overview.IPv6 = a.AnonymizeIPString(overview.IPv6)
for i, detail := range overview.Relays.Details {
@@ -1034,15 +1033,6 @@ func anonymizeOverview(a *anonymize.Anonymizer, overview *OutputOverview) {
}
}
for i, event := range overview.Events {
event.Message = a.AnonymizeString(event.Message)
event.UserMessage = a.AnonymizeString(event.UserMessage)
for k, v := range event.Metadata {
event.Metadata[k] = a.AnonymizeString(v)
}
overview.Events[i] = event
}
for i, route := range overview.Networks {
overview.Networks[i] = a.AnonymizeRoute(route)
}

View File

@@ -1012,7 +1012,7 @@
"description": "Helper text under the anonymization dropdown. The level details live in the info tooltip."
},
"settings.troubleshooting.anonymize.info": {
"message": "Default keeps internal IP addresses and peer names readable for support. Strict additionally anonymizes private (RFC 1918), CGNAT, and link-local IP addresses, peer names, and WireGuard public keys. Recurring values map to the same placeholder, so peers stay distinguishable. Use Strict when sharing the bundle outside your organization.",
"message": "Default keeps internal IPv4 addresses and peer names readable for support. Strict additionally anonymizes private (RFC 1918), CGNAT, and link-local IP addresses, peer names, and WireGuard public keys. Recurring values map to the same placeholder, so peers stay distinguishable. Use Strict when sharing the bundle outside your organization.",
"description": "Info tooltip explaining the anonymization levels. 'RFC 1918', 'CGNAT', 'link-local', and 'WireGuard' are technical terms — keep them."
},
"settings.troubleshooting.anonymize.none": {