diff --git a/ipn/ipnlocal/c2n.go b/ipn/ipnlocal/c2n.go index ebe07d87b..de6ca2321 100644 --- a/ipn/ipnlocal/c2n.go +++ b/ipn/ipnlocal/c2n.go @@ -318,7 +318,7 @@ func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http res := tailcfg.C2NPostureIdentityResponse{} - // Only collect serial numbers if enabled on the client, + // Only collect posture identity if enabled on the client, // this will first check syspolicy, MDM settings like Registry // on Windows or defaults on macOS. If they are not set, it falls // back to the cli-flag, `--posture-checking`. @@ -337,8 +337,17 @@ func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http http.Error(w, err.Error(), http.StatusInternalServerError) return } - res.SerialNumbers = sns + + // TODO(tailscale/corp#21371, 2024-07-10): once this has landed in a stable release + // and looks good in client metrics, remove this parameter and always report MAC + // addresses. + if r.FormValue("hwaddrs") == "true" { + res.IfaceHardwareAddrs, err = posture.GetHardwareAddrs() + if err != nil { + b.logf("c2n: GetHardwareAddrs returned error: %v", err) + } + } } else { res.PostureDisabled = true } diff --git a/posture/hwaddr.go b/posture/hwaddr.go new file mode 100644 index 000000000..a38cc5be0 --- /dev/null +++ b/posture/hwaddr.go @@ -0,0 +1,26 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause + +package posture + +import ( + "net/netip" + "slices" + + "tailscale.com/net/netmon" +) + +// GetHardwareAddrs returns the hardware addresses of all non-loopback +// network interfaces. +func GetHardwareAddrs() (hwaddrs []string, err error) { + err = netmon.ForeachInterface(func(i netmon.Interface, _ []netip.Prefix) { + if i.IsLoopback() { + return + } + if a := i.HardwareAddr.String(); a != "" { + hwaddrs = append(hwaddrs, a) + } + }) + slices.Sort(hwaddrs) + return +} diff --git a/tailcfg/c2ntypes.go b/tailcfg/c2ntypes.go index 929c50ae2..54efb736e 100644 --- a/tailcfg/c2ntypes.go +++ b/tailcfg/c2ntypes.go @@ -55,13 +55,17 @@ type C2NUpdateResponse struct { Started bool } -// C2NPostureIdentityResponse contains either a set of identifying serial number -// from the client or a boolean indicating that the machine has opted out of -// posture collection. +// C2NPostureIdentityResponse contains either a set of identifying serial +// numbers and hardware addresses from the client, or a boolean flag +// indicating that the machine has opted out of posture collection. type C2NPostureIdentityResponse struct { // SerialNumbers is a list of serial numbers of the client machine. SerialNumbers []string `json:",omitempty"` + // IfaceHardwareAddrs is a list of hardware addresses (MAC addresses) + // of the client machine's network interfaces. + IfaceHardwareAddrs []string `json:",omitempty"` + // PostureDisabled indicates if the machine has opted out of // device posture collection. PostureDisabled bool `json:",omitempty"`