hostinfo, ipnlocal: add optional os-specific callback for querying the hostname (#15647)

updates tailscale/tailscale#13476

On darwin, os.Hostname is no longer reliable when called
from a sandboxed process.  To fix this, we will allow clients
to set an optional callback to query the hostname via an
alternative native API.

We will leave the default implementation as os.Hostname since
this works perfectly well for almost everything besides sandboxed
darwin clients.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
This commit is contained in:
Jonathan Nobels 2025-04-14 15:02:32 -04:00 committed by GitHub
parent 62182fc37d
commit d6fd865d41
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 2 deletions

View File

@ -43,7 +43,7 @@ func RegisterHostinfoNewHook(f func(*tailcfg.Hostinfo)) {
// New returns a partially populated Hostinfo for the current host.
func New() *tailcfg.Hostinfo {
hostname, _ := os.Hostname()
hostname, _ := Hostname()
hostname = dnsname.FirstLabel(hostname)
hi := &tailcfg.Hostinfo{
IPNVersion: version.Long(),
@ -509,3 +509,21 @@ func IsInVM86() bool {
return New().DeviceModel == copyV86DeviceModel
})
}
type hostnameQuery func() (string, error)
var hostnameFn atomic.Value // of func() (string, error)
// SetHostNameFn sets a custom function for querying the system hostname.
func SetHostnameFn(fn hostnameQuery) {
hostnameFn.Store(fn)
}
// Hostname returns the system hostname using the function
// set by SetHostNameFn. We will fallback to os.Hostname.
func Hostname() (string, error) {
if fn, ok := hostnameFn.Load().(hostnameQuery); ok && fn != nil {
return fn()
}
return os.Hostname()
}

View File

@ -5,6 +5,7 @@ package hostinfo
import (
"encoding/json"
"os"
"strings"
"testing"
)
@ -49,3 +50,31 @@ func TestEtcAptSourceFileIsDisabled(t *testing.T) {
})
}
}
func TestCustomHostnameFunc(t *testing.T) {
want := "custom-hostname"
SetHostnameFn(func() (string, error) {
return want, nil
})
got, err := Hostname()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != want {
t.Errorf("got %q, want %q", got, want)
}
SetHostnameFn(os.Hostname)
got, err = Hostname()
want, _ = os.Hostname()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if got != want {
t.Errorf("got %q, want %q", got, want)
}
}

View File

@ -1245,7 +1245,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
}
} else {
ss.HostName, _ = os.Hostname()
ss.HostName, _ = hostinfo.Hostname()
}
for _, pln := range b.peerAPIListeners {
ss.PeerAPIURL = append(ss.PeerAPIURL, pln.urlStr)