mirror of
https://github.com/tailscale/tailscale.git
synced 2025-01-10 10:03:43 +00:00
ipn/ipnlocal: connect serve config to c2n endpoint
This commit updates the VIPService c2n endpoint on client to response with actual VIPService configuration stored in the serve config. Fixes tailscale/corp#24510 Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
This commit is contained in:
parent
60daa2adb8
commit
009da8a364
@ -11,6 +11,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -5017,13 +5018,7 @@ func (b *LocalBackend) applyPrefsToHostinfoLocked(hi *tailcfg.Hostinfo, prefs ip
|
|||||||
}
|
}
|
||||||
hi.SSH_HostKeys = sshHostKeys
|
hi.SSH_HostKeys = sshHostKeys
|
||||||
|
|
||||||
services := vipServicesFromPrefs(prefs)
|
hi.ServicesHash = b.vipServiceHashLocked(prefs)
|
||||||
if len(services) > 0 {
|
|
||||||
buf, _ := json.Marshal(services)
|
|
||||||
hi.ServicesHash = fmt.Sprintf("%02x", sha256.Sum256(buf))
|
|
||||||
} else {
|
|
||||||
hi.ServicesHash = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// The Hostinfo.WantIngress field tells control whether this node wants to
|
// The Hostinfo.WantIngress field tells control whether this node wants to
|
||||||
// be wired up for ingress connections. If harmless if it's accidentally
|
// be wired up for ingress connections. If harmless if it's accidentally
|
||||||
@ -7659,28 +7654,38 @@ func maybeUsernameOf(actor ipnauth.Actor) string {
|
|||||||
func (b *LocalBackend) VIPServices() []*tailcfg.VIPService {
|
func (b *LocalBackend) VIPServices() []*tailcfg.VIPService {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
return vipServicesFromPrefs(b.pm.CurrentPrefs())
|
return b.vipServicesFromPrefsLocked(b.pm.CurrentPrefs())
|
||||||
}
|
}
|
||||||
|
|
||||||
func vipServicesFromPrefs(prefs ipn.PrefsView) []*tailcfg.VIPService {
|
func (b *LocalBackend) vipServiceHashLocked(prefs ipn.PrefsView) string {
|
||||||
|
services := b.vipServicesFromPrefsLocked(prefs)
|
||||||
|
if len(services) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
buf, err := json.Marshal(services)
|
||||||
|
if err != nil {
|
||||||
|
b.logf("vipServiceHashLocked: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
hash := sha256.Sum256(buf)
|
||||||
|
return hex.EncodeToString(hash[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *LocalBackend) vipServicesFromPrefsLocked(prefs ipn.PrefsView) []*tailcfg.VIPService {
|
||||||
// keyed by service name
|
// keyed by service name
|
||||||
var services map[string]*tailcfg.VIPService
|
var services map[string]*tailcfg.VIPService
|
||||||
|
if !b.serveConfig.Valid() {
|
||||||
// TODO(naman): this envknob will be replaced with service-specific port
|
return nil
|
||||||
// information once we start storing that.
|
|
||||||
var allPortsServices []string
|
|
||||||
if env := envknob.String("TS_DEBUG_ALLPORTS_SERVICES"); env != "" {
|
|
||||||
allPortsServices = strings.Split(env, ",")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range allPortsServices {
|
for svc, config := range b.serveConfig.Services().All() {
|
||||||
mak.Set(&services, s, &tailcfg.VIPService{
|
mak.Set(&services, svc, &tailcfg.VIPService{
|
||||||
Name: s,
|
Name: svc,
|
||||||
Ports: []tailcfg.ProtoPortRange{{Ports: tailcfg.PortRangeAny}},
|
Ports: config.ServicePortRange(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range prefs.AdvertiseServices().AsSlice() {
|
for _, s := range prefs.AdvertiseServices().All() {
|
||||||
if services == nil || services[s] == nil {
|
if services == nil || services[s] == nil {
|
||||||
mak.Set(&services, s, &tailcfg.VIPService{
|
mak.Set(&services, s, &tailcfg.VIPService{
|
||||||
Name: s,
|
Name: s,
|
||||||
|
@ -30,7 +30,6 @@ import (
|
|||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/drive"
|
"tailscale.com/drive"
|
||||||
"tailscale.com/drive/driveimpl"
|
"tailscale.com/drive/driveimpl"
|
||||||
"tailscale.com/envknob"
|
|
||||||
"tailscale.com/health"
|
"tailscale.com/health"
|
||||||
"tailscale.com/hostinfo"
|
"tailscale.com/hostinfo"
|
||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
@ -4511,13 +4510,13 @@ func TestGetVIPServices(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
advertised []string
|
advertised []string
|
||||||
mapped []string
|
serveConfig *ipn.ServeConfig
|
||||||
want []*tailcfg.VIPService
|
want []*tailcfg.VIPService
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"advertised-only",
|
"advertised-only",
|
||||||
[]string{"svc:abc", "svc:def"},
|
[]string{"svc:abc", "svc:def"},
|
||||||
[]string{},
|
&ipn.ServeConfig{},
|
||||||
[]*tailcfg.VIPService{
|
[]*tailcfg.VIPService{
|
||||||
{
|
{
|
||||||
Name: "svc:abc",
|
Name: "svc:abc",
|
||||||
@ -4530,9 +4529,13 @@ func TestGetVIPServices(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mapped-only",
|
"served-only",
|
||||||
[]string{},
|
[]string{},
|
||||||
[]string{"svc:abc"},
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {Tun: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
[]*tailcfg.VIPService{
|
[]*tailcfg.VIPService{
|
||||||
{
|
{
|
||||||
Name: "svc:abc",
|
Name: "svc:abc",
|
||||||
@ -4541,9 +4544,13 @@ func TestGetVIPServices(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mapped-and-advertised",
|
"served-and-advertised",
|
||||||
[]string{"svc:abc"},
|
|
||||||
[]string{"svc:abc"},
|
[]string{"svc:abc"},
|
||||||
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {Tun: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
[]*tailcfg.VIPService{
|
[]*tailcfg.VIPService{
|
||||||
{
|
{
|
||||||
Name: "svc:abc",
|
Name: "svc:abc",
|
||||||
@ -4553,9 +4560,13 @@ func TestGetVIPServices(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"mapped-and-advertised-separately",
|
"served-and-advertised-different-service",
|
||||||
[]string{"svc:def"},
|
[]string{"svc:def"},
|
||||||
[]string{"svc:abc"},
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {Tun: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
[]*tailcfg.VIPService{
|
[]*tailcfg.VIPService{
|
||||||
{
|
{
|
||||||
Name: "svc:abc",
|
Name: "svc:abc",
|
||||||
@ -4567,14 +4578,78 @@ func TestGetVIPServices(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"served-with-port-ranges-one-range-single",
|
||||||
|
[]string{},
|
||||||
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {TCP: map[uint16]*ipn.TCPPortHandler{
|
||||||
|
80: {HTTPS: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*tailcfg.VIPService{
|
||||||
|
{
|
||||||
|
Name: "svc:abc",
|
||||||
|
Ports: []tailcfg.ProtoPortRange{{Proto: 6, Ports: tailcfg.PortRange{First: 80, Last: 80}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"served-with-port-ranges-one-range-multiple",
|
||||||
|
[]string{},
|
||||||
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {TCP: map[uint16]*ipn.TCPPortHandler{
|
||||||
|
80: {HTTPS: true},
|
||||||
|
81: {HTTPS: true},
|
||||||
|
82: {HTTPS: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*tailcfg.VIPService{
|
||||||
|
{
|
||||||
|
Name: "svc:abc",
|
||||||
|
Ports: []tailcfg.ProtoPortRange{{Proto: 6, Ports: tailcfg.PortRange{First: 80, Last: 82}}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"served-with-port-ranges-multiple-ranges",
|
||||||
|
[]string{},
|
||||||
|
&ipn.ServeConfig{
|
||||||
|
Services: map[string]*ipn.ServiceConfig{
|
||||||
|
"svc:abc": {TCP: map[uint16]*ipn.TCPPortHandler{
|
||||||
|
80: {HTTPS: true},
|
||||||
|
81: {HTTPS: true},
|
||||||
|
82: {HTTPS: true},
|
||||||
|
1212: {HTTPS: true},
|
||||||
|
1213: {HTTPS: true},
|
||||||
|
1214: {HTTPS: true},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]*tailcfg.VIPService{
|
||||||
|
{
|
||||||
|
Name: "svc:abc",
|
||||||
|
Ports: []tailcfg.ProtoPortRange{
|
||||||
|
{Proto: 6, Ports: tailcfg.PortRange{First: 80, Last: 82}},
|
||||||
|
{Proto: 6, Ports: tailcfg.PortRange{First: 1212, Last: 1214}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
envknob.Setenv("TS_DEBUG_ALLPORTS_SERVICES", strings.Join(tt.mapped, ","))
|
lb := newLocalBackendWithTestControl(t, false, func(tb testing.TB, opts controlclient.Options) controlclient.Client {
|
||||||
|
return newClient(tb, opts)
|
||||||
|
})
|
||||||
|
lb.serveConfig = tt.serveConfig.View()
|
||||||
prefs := &ipn.Prefs{
|
prefs := &ipn.Prefs{
|
||||||
AdvertiseServices: tt.advertised,
|
AdvertiseServices: tt.advertised,
|
||||||
}
|
}
|
||||||
got := vipServicesFromPrefs(prefs.View())
|
got := lb.vipServicesFromPrefsLocked(prefs.View())
|
||||||
slices.SortFunc(got, func(a, b *tailcfg.VIPService) int {
|
slices.SortFunc(got, func(a, b *tailcfg.VIPService) int {
|
||||||
return strings.Compare(a.Name, b.Name)
|
return strings.Compare(a.Name, b.Name)
|
||||||
})
|
})
|
||||||
|
40
ipn/serve.go
40
ipn/serve.go
@ -16,7 +16,9 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
|
"tailscale.com/types/ipproto"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
|
"tailscale.com/util/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServeConfigKey returns a StateKey that stores the
|
// ServeConfigKey returns a StateKey that stores the
|
||||||
@ -655,3 +657,41 @@ func (v ServeConfigView) HasFunnelForTarget(target HostPort) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServicePortRange returns the list of tailcfg.ProtoPortRange that represents
|
||||||
|
// the proto/ports pairs that are being served by the service.
|
||||||
|
//
|
||||||
|
// Right now Tun mode is the only thing supports UDP, otherwise serve only supports TCP.
|
||||||
|
func (v ServiceConfigView) ServicePortRange() []tailcfg.ProtoPortRange {
|
||||||
|
if v.Tun() {
|
||||||
|
// If the service is in Tun mode, means service accept TCP/UDP on all ports.
|
||||||
|
return []tailcfg.ProtoPortRange{{Ports: tailcfg.PortRangeAny}}
|
||||||
|
}
|
||||||
|
tcp := int(ipproto.TCP)
|
||||||
|
|
||||||
|
// Deduplicate the ports.
|
||||||
|
servePorts := make(set.Set[uint16])
|
||||||
|
for port := range v.TCP().All() {
|
||||||
|
if port > 0 {
|
||||||
|
servePorts.Add(uint16(port))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dedupedServePorts := servePorts.Slice()
|
||||||
|
slices.Sort(dedupedServePorts)
|
||||||
|
|
||||||
|
var ranges []tailcfg.ProtoPortRange
|
||||||
|
for _, p := range dedupedServePorts {
|
||||||
|
if n := len(ranges); n > 0 && p == ranges[n-1].Ports.Last+1 {
|
||||||
|
ranges[n-1].Ports.Last = p
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ranges = append(ranges, tailcfg.ProtoPortRange{
|
||||||
|
Proto: tcp,
|
||||||
|
Ports: tailcfg.PortRange{
|
||||||
|
First: p,
|
||||||
|
Last: p,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return ranges
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user