IPN: Update ServeConfig to accept configuration for Services.

This commit updates ServeConfig to allow configuration to Services (VIPServices for now) via Serve.
The scope of this commit is only adding the Services field to ServeConfig. The field doesn't actually
allow packet flowing yet. The purpose of this commit is to unblock other work on k8s end.

Updates #22953

Signed-off-by: KevinLiang10 <37811973+KevinLiang10@users.noreply.github.com>
This commit is contained in:
KevinLiang10 2024-11-28 12:49:37 -05:00
parent 8d0c690f89
commit 3f54572539
4 changed files with 144 additions and 2 deletions

View File

@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
//go:generate go run tailscale.com/cmd/viewer -type=Prefs,ServeConfig,TCPPortHandler,HTTPHandler,WebServerConfig
//go:generate go run tailscale.com/cmd/viewer -type=Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
// Package ipn implements the interactions between the Tailscale cloud
// control plane and the local network stack.

View File

@ -105,6 +105,16 @@ func (src *ServeConfig) Clone() *ServeConfig {
}
}
}
if dst.Services != nil {
dst.Services = map[string]*ServiceConfig{}
for k, v := range src.Services {
if v == nil {
dst.Services[k] = nil
} else {
dst.Services[k] = v.Clone()
}
}
}
dst.AllowFunnel = maps.Clone(src.AllowFunnel)
if dst.Foreground != nil {
dst.Foreground = map[string]*ServeConfig{}
@ -123,11 +133,50 @@ func (src *ServeConfig) Clone() *ServeConfig {
var _ServeConfigCloneNeedsRegeneration = ServeConfig(struct {
TCP map[uint16]*TCPPortHandler
Web map[HostPort]*WebServerConfig
Services map[string]*ServiceConfig
AllowFunnel map[HostPort]bool
Foreground map[string]*ServeConfig
ETag string
}{})
// Clone makes a deep copy of ServiceConfig.
// The result aliases no memory with the original.
func (src *ServiceConfig) Clone() *ServiceConfig {
if src == nil {
return nil
}
dst := new(ServiceConfig)
*dst = *src
if dst.TCP != nil {
dst.TCP = map[uint16]*TCPPortHandler{}
for k, v := range src.TCP {
if v == nil {
dst.TCP[k] = nil
} else {
dst.TCP[k] = ptr.To(*v)
}
}
}
if dst.Web != nil {
dst.Web = map[HostPort]*WebServerConfig{}
for k, v := range src.Web {
if v == nil {
dst.Web[k] = nil
} else {
dst.Web[k] = v.Clone()
}
}
}
return dst
}
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _ServiceConfigCloneNeedsRegeneration = ServiceConfig(struct {
TCP map[uint16]*TCPPortHandler
Web map[HostPort]*WebServerConfig
Tun bool
}{})
// Clone makes a deep copy of TCPPortHandler.
// The result aliases no memory with the original.
func (src *TCPPortHandler) Clone() *TCPPortHandler {

View File

@ -18,7 +18,7 @@
"tailscale.com/types/views"
)
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=Prefs,ServeConfig,TCPPortHandler,HTTPHandler,WebServerConfig
//go:generate go run tailscale.com/cmd/cloner -clonefunc=false -type=Prefs,ServeConfig,ServiceConfig,TCPPortHandler,HTTPHandler,WebServerConfig
// View returns a readonly view of Prefs.
func (p *Prefs) View() PrefsView {
@ -195,6 +195,12 @@ func (v ServeConfigView) Web() views.MapFn[HostPort, *WebServerConfig, WebServer
})
}
func (v ServeConfigView) Services() views.MapFn[string, *ServiceConfig, ServiceConfigView] {
return views.MapFnOf(v.ж.Services, func(t *ServiceConfig) ServiceConfigView {
return t.View()
})
}
func (v ServeConfigView) AllowFunnel() views.Map[HostPort, bool] {
return views.MapOf(v.ж.AllowFunnel)
}
@ -210,11 +216,77 @@ func (v ServeConfigView) ETag() string { return v.ж.ETag }
var _ServeConfigViewNeedsRegeneration = ServeConfig(struct {
TCP map[uint16]*TCPPortHandler
Web map[HostPort]*WebServerConfig
Services map[string]*ServiceConfig
AllowFunnel map[HostPort]bool
Foreground map[string]*ServeConfig
ETag string
}{})
// View returns a readonly view of ServiceConfig.
func (p *ServiceConfig) View() ServiceConfigView {
return ServiceConfigView{ж: p}
}
// ServiceConfigView provides a read-only view over ServiceConfig.
//
// Its methods should only be called if `Valid()` returns true.
type ServiceConfigView struct {
// ж is the underlying mutable value, named with a hard-to-type
// character that looks pointy like a pointer.
// It is named distinctively to make you think of how dangerous it is to escape
// to callers. You must not let callers be able to mutate it.
ж *ServiceConfig
}
// Valid reports whether underlying value is non-nil.
func (v ServiceConfigView) Valid() bool { return v.ж != nil }
// AsStruct returns a clone of the underlying value which aliases no memory with
// the original.
func (v ServiceConfigView) AsStruct() *ServiceConfig {
if v.ж == nil {
return nil
}
return v.ж.Clone()
}
func (v ServiceConfigView) MarshalJSON() ([]byte, error) { return json.Marshal(v.ж) }
func (v *ServiceConfigView) UnmarshalJSON(b []byte) error {
if v.ж != nil {
return errors.New("already initialized")
}
if len(b) == 0 {
return nil
}
var x ServiceConfig
if err := json.Unmarshal(b, &x); err != nil {
return err
}
v.ж = &x
return nil
}
func (v ServiceConfigView) TCP() views.MapFn[uint16, *TCPPortHandler, TCPPortHandlerView] {
return views.MapFnOf(v.ж.TCP, func(t *TCPPortHandler) TCPPortHandlerView {
return t.View()
})
}
func (v ServiceConfigView) Web() views.MapFn[HostPort, *WebServerConfig, WebServerConfigView] {
return views.MapFnOf(v.ж.Web, func(t *WebServerConfig) WebServerConfigView {
return t.View()
})
}
func (v ServiceConfigView) Tun() bool { return v.ж.Tun }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _ServiceConfigViewNeedsRegeneration = ServiceConfig(struct {
TCP map[uint16]*TCPPortHandler
Web map[HostPort]*WebServerConfig
Tun bool
}{})
// View returns a readonly view of TCPPortHandler.
func (p *TCPPortHandler) View() TCPPortHandlerView {
return TCPPortHandlerView{ж: p}

View File

@ -24,6 +24,23 @@ func ServeConfigKey(profileID ProfileID) StateKey {
return StateKey("_serve/" + profileID)
}
// ServiceConfig contains the config information for a single service.
// it contains a bool to indicate if the service is in Tun mode (L3 forwarding).
// If the service is not in Tun mode, the service is configured by the L4 forwarding
// (TCP ports) and/or the L7 forwarding (http handlers) information.
type ServiceConfig struct {
// TCP are the list of TCP port numbers that tailscaled should handle for
// the Tailscale IP addresses. (not subnet routers, etc)
TCP map[uint16]*TCPPortHandler `json:",omitempty"`
// Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
// keyed by mount point ("/", "/foo", etc)
Web map[HostPort]*WebServerConfig `json:",omitempty"`
// Tun determines if the service should be using L3 forwarding (Tun mode).
Tun bool `json:",omitempty"`
}
// ServeConfig is the JSON type stored in the StateStore for
// StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
type ServeConfig struct {
@ -35,6 +52,10 @@ type ServeConfig struct {
// keyed by mount point ("/", "/foo", etc)
Web map[HostPort]*WebServerConfig `json:",omitempty"`
// Services maps from service name to a ServiceConfig. Which describes the
// L3, L4, and L7 forwarding information for the service.
Services map[string]*ServiceConfig `json:",omitempty"`
// AllowFunnel is the set of SNI:port values for which funnel
// traffic is allowed, from trusted ingress peers.
AllowFunnel map[HostPort]bool `json:",omitempty"`