ipn/ipnlocal: add peer API endpoints to Hostinfo on initial client creation (#17851)

Previously we only set this when it updated, which was fine for the first
call to Start(), but after that point future updates would be skipped if
nothing had changed. If Start() was called again, it would wipe the peer API
endpoints and they wouldn't get added back again, breaking exit nodes (and
anything else requiring peer API to be advertised).

Updates tailscale/corp#27173

Signed-off-by: James Sanderson <jsanderson@tailscale.com>
This commit is contained in:
James 'zofrex' Sanderson
2025-12-05 13:33:47 +00:00
committed by GitHub
parent f4d34f38be
commit cf40cf5ccb
2 changed files with 102 additions and 10 deletions
+17 -3
View File
@@ -2528,7 +2528,7 @@ func (b *LocalBackend) startLocked(opts ipn.Options) error {
if inServerMode := prefs.ForceDaemon(); inServerMode || runtime.GOOS == "windows" {
logf("serverMode=%v", inServerMode)
}
b.applyPrefsToHostinfoLocked(hostinfo, prefs)
b.applyPrefsToHostinfoLocked(b.hostinfo, prefs)
b.updateWarnSync(prefs)
persistv := prefs.Persist().AsStruct()
@@ -2566,7 +2566,7 @@ func (b *LocalBackend) startLocked(opts ipn.Options) error {
Persist: *persistv,
ServerURL: serverURL,
AuthKey: opts.AuthKey,
Hostinfo: hostinfo,
Hostinfo: b.hostInfoWithServicesLocked(),
HTTPTestClient: httpTestClient,
DiscoPublicKey: discoPublic,
DebugFlags: debugFlags,
@@ -4830,6 +4830,17 @@ func (b *LocalBackend) doSetHostinfoFilterServicesLocked() {
b.logf("[unexpected] doSetHostinfoFilterServices with nil hostinfo")
return
}
hi := b.hostInfoWithServicesLocked()
cc.SetHostinfo(hi)
}
// hostInfoWithServicesLocked returns a shallow clone of b.hostinfo with
// services added.
//
// b.mu must be held.
func (b *LocalBackend) hostInfoWithServicesLocked() *tailcfg.Hostinfo {
peerAPIServices := b.peerAPIServicesLocked()
if b.egg {
peerAPIServices = append(peerAPIServices, tailcfg.Service{Proto: "egg", Port: 1})
@@ -4857,7 +4868,7 @@ func (b *LocalBackend) doSetHostinfoFilterServicesLocked() {
b.logf("Hostinfo peerAPI ports changed: expected %v, got %v", expectedPorts, actualPorts)
}
cc.SetHostinfo(&hi)
return &hi
}
type portPair struct {
@@ -5257,6 +5268,9 @@ func (b *LocalBackend) initPeerAPIListenerLocked() {
if allSame {
// Nothing to do.
b.logf("[v1] initPeerAPIListener: %d netmap addresses match existing listeners", addrs.Len())
// TODO(zofrex): This is fragile. It doesn't check what's actually in hostinfo, and if
// peerAPIListeners gets out of sync with hostinfo.Services, we won't get back into a good
// state. E.G. see tailscale/corp#27173.
return
}
}