summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cmd/tailscale/cli/serve_legacy.go1
-rw-r--r--cmd/tailscale/cli/serve_v2.go1
-rw-r--r--ipn/serve.go12
-rw-r--r--tailcfg/tailcfg.go10
-rw-r--r--wgengine/netstack/netstack.go5
5 files changed, 26 insertions, 3 deletions
diff --git a/cmd/tailscale/cli/serve_legacy.go b/cmd/tailscale/cli/serve_legacy.go
index 443a404ab..c25fd5080 100644
--- a/cmd/tailscale/cli/serve_legacy.go
+++ b/cmd/tailscale/cli/serve_legacy.go
@@ -161,6 +161,7 @@ type serveEnv struct {
tlsTerminatedTCP uint // a TLS terminated TCP port
subcmd serveMode // subcommand
yes bool // update without prompt
+ service string // listen on a virtual service IP
lc localServeClient // localClient interface, specific to serve
diff --git a/cmd/tailscale/cli/serve_v2.go b/cmd/tailscale/cli/serve_v2.go
index 009a61198..6446253f5 100644
--- a/cmd/tailscale/cli/serve_v2.go
+++ b/cmd/tailscale/cli/serve_v2.go
@@ -127,6 +127,7 @@ func newServeV2Command(e *serveEnv, subcmd serveMode) *ffcli.Command {
fs.UintVar(&e.tcp, "tcp", 0, "Expose a TCP forwarder to forward raw TCP packets at the specified port")
fs.UintVar(&e.tlsTerminatedTCP, "tls-terminated-tcp", 0, "Expose a TCP forwarder to forward TLS-terminated TCP packets at the specified port")
fs.BoolVar(&e.yes, "yes", false, "Update without interactive prompts (default false)")
+ fs.StringVar(&e.service, "service", "", "listen for connections on a virtual service IP (example: service:myservice)")
}),
UsageFunc: usageFuncNoDefaultValues,
Subcommands: []*ffcli.Command{
diff --git a/ipn/serve.go b/ipn/serve.go
index 5c0a97ed3..d7c43f193 100644
--- a/ipn/serve.go
+++ b/ipn/serve.go
@@ -24,9 +24,7 @@ func ServeConfigKey(profileID ProfileID) StateKey {
return StateKey("_serve/" + profileID)
}
-// ServeConfig is the JSON type stored in the StateStore for
-// StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
-type ServeConfig struct {
+type ListenerConfig 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"`
@@ -34,6 +32,14 @@ type ServeConfig struct {
// Web maps from "$SNI_NAME:$PORT" to a set of HTTP handlers
// keyed by mount point ("/", "/foo", etc)
Web map[HostPort]*WebServerConfig `json:",omitempty"`
+}
+
+// ServeConfig is the JSON type stored in the StateStore for
+// StateKey "_serve/$PROFILE_ID" as returned by ServeConfigKey.
+type ServeConfig struct {
+ ListenerConfig // local config
+
+ Services map[string]ListenerConfig `json:",omitempty"` // VIP service config
// AllowFunnel is the set of SNI:port values for which funnel
// traffic is allowed, from trusted ingress peers.
diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go
index 0d4fae3d5..04de358b0 100644
--- a/tailcfg/tailcfg.go
+++ b/tailcfg/tailcfg.go
@@ -2346,6 +2346,9 @@ const (
// NodeAttrSSHEnvironmentVariables enables logic for handling environment variables sent
// via SendEnv in the SSH server and applying them to the SSH session.
NodeAttrSSHEnvironmentVariables NodeCapability = "ssh-env-vars"
+
+ // NodeAttrVIPService instructs the client how to configure VIP services.
+ NodeAttrVIPService NodeCapability = "vip-service"
)
// SetDNSRequest is a request to add a DNS record.
@@ -2824,3 +2827,10 @@ type EarlyNoise struct {
// For some request types, the header may have multiple values. (e.g. OldNodeKey
// vs NodeKey)
const LBHeader = "Ts-Lb"
+
+type VIPServicePortMap map[ /*proto*/ int]map[ /*port*/ int]netip.AddrPort
+
+type VIPService struct {
+ Addrs []netip.Addr `json:"addrs,omitempty"`
+ PortMap VIPServicePortMap `json:"portMap,omitempty"`
+}
diff --git a/wgengine/netstack/netstack.go b/wgengine/netstack/netstack.go
index d029b6c19..d35412d33 100644
--- a/wgengine/netstack/netstack.go
+++ b/wgengine/netstack/netstack.go
@@ -19,6 +19,7 @@ import (
"sync/atomic"
"time"
+ "github.com/gaissmai/bart"
"github.com/tailscale/wireguard-go/conn"
"gvisor.dev/gvisor/pkg/refs"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -173,6 +174,10 @@ type Impl struct {
// It can only be set before calling Start.
ProcessSubnets bool
+ // ServiceVIPs is the description of service VIPs that should be handled.
+ // It can only be set before calling Start and is immutable after.
+ ServiceVIPs bart.Table[map[int]netip.AddrPort]
+
ipstack *stack.Stack
linkEP *linkEndpoint
tundev *tstun.Wrapper