Kristoffer Dalby 4cca63155d all: apply godoc [Name] link conventions across comments
Every Go-identifier reference in // and /* */ comments now uses
godoc's [Name] linking syntax so pkg.go.dev and `go doc` render
them as clickable cross-references. No behaviour change.

Pattern applied across the tree:
  In-package         [Foo], [Foo.Bar]
  Cross-package      [pkg.Foo], [pkg.Foo.Bar]
  Stdlib             [netip.Prefix], [errors.Is], [context.Context]
  Tailscale          [tailcfg.MapResponse], [tailcfg.Node.CapMap],
                     [tailcfg.NodeAttrSuggestExitNode]

Skip rules:
  - File:line refs left as plain text
  - HuJSON wire keys inside backtick raw strings untouched
  - ACL/policy syntax tokens (tag:foo, autogroup:self, ...) not Go
    symbols, left as plain text
  - JSON/OIDC wire keys, gorm tags, RFC IPv6 placeholders, markdown
    link tags, decorative dividers — all left as-is
2026-05-19 09:55:22 +02:00

62 lines
2.0 KiB
Go

package zlog
import (
"github.com/juanfont/headscale/hscontrol/util/zlog/zf"
"github.com/rs/zerolog"
"tailscale.com/tailcfg"
)
// SafeHostinfo wraps [tailcfg.Hostinfo] for safe logging.
//
// SECURITY: This wrapper intentionally redacts device fingerprinting data
// that could be used to identify or track specific devices:
// - OSVersion, DeviceModel, DistroName, DistroVersion (device fingerprinting)
// - IPNVersion (client version fingerprinting)
// - Machine, FrontendLogID (device identifiers)
//
// Only safe fields are logged:
// - hostname: The device hostname
// - os: The OS family (e.g., "linux", "windows") without version
// - routable_ips_count: Number of advertised routes (not the actual routes)
// - request_tags: Tags requested by the client
// - derp: Preferred DERP region ID
type SafeHostinfo struct {
hi *tailcfg.Hostinfo
}
// Hostinfo creates a [SafeHostinfo] wrapper for safe logging.
func Hostinfo(hi *tailcfg.Hostinfo) SafeHostinfo {
return SafeHostinfo{hi: hi}
}
// MarshalZerologObject implements [zerolog.LogObjectMarshaler].
func (s SafeHostinfo) MarshalZerologObject(e *zerolog.Event) {
if s.hi == nil {
return
}
// Safe fields only - no device fingerprinting data.
e.Str(zf.Hostname, s.hi.Hostname)
e.Str(zf.OS, s.hi.OS) // OS family only, NOT version
if len(s.hi.RoutableIPs) > 0 {
e.Int(zf.RoutableIPCount, len(s.hi.RoutableIPs))
}
if len(s.hi.RequestTags) > 0 {
e.Strs(zf.RequestTags, s.hi.RequestTags)
}
if s.hi.NetInfo != nil && s.hi.NetInfo.PreferredDERP != 0 {
e.Int(zf.DERP, s.hi.NetInfo.PreferredDERP)
}
// SECURITY: The following fields are intentionally NOT logged:
// - OSVersion, DistroName, DistroVersion, DistroCodeName: device fingerprinting
// - DeviceModel: device fingerprinting
// - IPNVersion: client version fingerprinting
// - Machine, FrontendLogID: device identifiers
// - GoArch, GoArchVar, GoVersion: build environment fingerprinting
// - Userspace, UserspaceRouter: network configuration details
}