feat(tsconnect): expose service advertisement to JS
Add SetExplicitServices on LocalBackend so the browser WASM node can
declare TCP/UDP services that get uploaded to the control server and
distributed to all peers in the netmap — without the OS port-scanner
(portlist extension) that cannot run in a browser.
The ShouldUploadServices gate in hostInfoWithServicesLocked is bypassed
when services were set explicitly, leaving all other callers unaffected.
On the JS side, a new setServices(services) method accepts an array of
{proto, port, description?} objects. The netmap JSON now includes a
services field on every node (self and peers), populated from
Hostinfo.Services with internal peerapi entries stripped (they are
already reflected in peerAPIURL).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+21
-1
@@ -294,6 +294,7 @@ type LocalBackend struct {
|
||||
capTailnetLock bool // whether netMap contains the tailnet lock capability
|
||||
// hostinfo is mutated in-place while mu is held.
|
||||
hostinfo *tailcfg.Hostinfo // TODO(nickkhyl): move to nodeBackend
|
||||
explicitServices []tailcfg.Service // services set explicitly via SetExplicitServices; always uploaded
|
||||
nmExpiryTimer tstime.TimerController // for updating netMap on node expiry; can be nil; TODO(nickkhyl): move to nodeBackend
|
||||
activeLogin string // last logged LoginName from netMap; TODO(nickkhyl): move to nodeBackend (or remove? it's in [ipn.LoginProfile]).
|
||||
engineStatus ipn.EngineStatus
|
||||
@@ -4967,6 +4968,23 @@ func (b *LocalBackend) setPortlistServices(sl []tailcfg.Service) {
|
||||
b.doSetHostinfoFilterServices()
|
||||
}
|
||||
|
||||
// SetExplicitServices sets the services this node advertises on the netmap.
|
||||
// Unlike the OS port-scan path (setPortlistServices), services set here are
|
||||
// always uploaded to the control server regardless of the ShouldUploadServices
|
||||
// hook — suitable for environments like browser WASM where OS port scanning is
|
||||
// unavailable and services are declared programmatically.
|
||||
func (b *LocalBackend) SetExplicitServices(sl []tailcfg.Service) {
|
||||
b.mu.Lock()
|
||||
if b.hostinfo == nil {
|
||||
b.hostinfo = new(tailcfg.Hostinfo)
|
||||
}
|
||||
b.hostinfo.Services = sl
|
||||
b.explicitServices = sl
|
||||
b.mu.Unlock()
|
||||
|
||||
b.doSetHostinfoFilterServices()
|
||||
}
|
||||
|
||||
// doSetHostinfoFilterServices calls SetHostinfo on the controlclient,
|
||||
// possibly after mangling the given hostinfo.
|
||||
//
|
||||
@@ -5011,7 +5029,9 @@ func (b *LocalBackend) hostInfoWithServicesLocked() *tailcfg.Hostinfo {
|
||||
// Make a shallow copy of hostinfo so we can mutate
|
||||
// at the Service field.
|
||||
if f, ok := b.extHost.Hooks().ShouldUploadServices.GetOk(); !ok || !f() {
|
||||
hi.Services = []tailcfg.Service{}
|
||||
if len(b.explicitServices) == 0 {
|
||||
hi.Services = []tailcfg.Service{}
|
||||
}
|
||||
}
|
||||
|
||||
// Don't mutate hi.Service's underlying array. Append to
|
||||
|
||||
Reference in New Issue
Block a user