summaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
authorMarwan Sulaiman <marwan@tailscale.com>2023-09-05 13:51:52 -0400
committerMarwan Sulaiman <marwan@tailscale.com>2023-09-05 13:51:52 -0400
commitce16658ac0e032e9524bac8f5fc926b11f82a7a7 (patch)
tree7ccf2dd7c97b1d5996c4ca709818edc529b34f81 /cmd
parenta4aa6507fa3afa586dc3cbbf85f2cae852622b9e (diff)
downloadtailscale-marwan/postmem.tar.xz
tailscale-marwan/postmem.zip
ipn, ipn/ipnlocal: add Foreground field for ServeConfigmarwan/postmem
This PR adds a new field to the serve config that can be used to identify which serves are in "foreground mode" and then can also be used to ensure they do not get persisted to disk so that if Tailscaled gets ungracefully shutdown, the reloaded ServeConfig will not have those ports opened. Updates #8489 Signed-off-by: Marwan Sulaiman <marwan@tailscale.com>
Diffstat (limited to 'cmd')
-rw-r--r--cmd/tailscale/cli/serve.go1
-rw-r--r--cmd/tailscale/cli/serve_dev.go85
2 files changed, 74 insertions, 12 deletions
diff --git a/cmd/tailscale/cli/serve.go b/cmd/tailscale/cli/serve.go
index 39c0e106e..ab1a16913 100644
--- a/cmd/tailscale/cli/serve.go
+++ b/cmd/tailscale/cli/serve.go
@@ -149,7 +149,6 @@ type localServeClient interface {
QueryFeature(ctx context.Context, feature string) (*tailcfg.QueryFeatureResponse, error)
WatchIPNBus(ctx context.Context, mask ipn.NotifyWatchOpt) (*tailscale.IPNBusWatcher, error)
IncrementCounter(ctx context.Context, name string, delta int) error
- StreamServe(ctx context.Context, req ipn.ServeStreamRequest) (io.ReadCloser, error) // TODO: testing :)
}
// serveEnv is the environment the serve command runs within. All I/O should be
diff --git a/cmd/tailscale/cli/serve_dev.go b/cmd/tailscale/cli/serve_dev.go
index c2c94cc4a..30aa43119 100644
--- a/cmd/tailscale/cli/serve_dev.go
+++ b/cmd/tailscale/cli/serve_dev.go
@@ -5,9 +5,10 @@ package cli
import (
"context"
+ "encoding/json"
+ "errors"
"flag"
"fmt"
- "io"
"log"
"os"
"os/signal"
@@ -29,15 +30,15 @@ var infoMap = map[string]commandInfo{
"serve": {
ShortHelp: "Serve content and local servers on your tailnet",
LongHelp: strings.Join([]string{
- "Serve lets you share a local server securely within your tailnet.",
- "To share a local server on the internet, use \"tailscale funnel\"",
+ `Serve lets you share a local server securely within your tailnet.`,
+ `To share a local server on the internet, use "tailscale funnel"`,
}, "\n"),
},
"funnel": {
ShortHelp: "Serve content and local servers on the internet",
LongHelp: strings.Join([]string{
- "Funnel lets you share a local server on the internet using Tailscale.",
- "To share only within your tailnet, use \"tailscale serve\"",
+ `Funnel lets you share a local server on the internet using Tailscale.`,
+ `To share only within your tailnet, use "tailscale serve"`,
}, "\n"),
},
}
@@ -134,14 +135,76 @@ func (e *serveEnv) runServeDev(funnel bool) execFunc {
}
func (e *serveEnv) streamServe(ctx context.Context, req ipn.ServeStreamRequest) error {
- stream, err := e.lc.StreamServe(ctx, req)
+ watcher, err := e.lc.WatchIPNBus(ctx, ipn.NotifyInitialState|ipn.NotifyServeRequest)
if err != nil {
return err
}
- defer stream.Close()
+ defer watcher.Close()
+ n, err := watcher.Next()
+ if err != nil {
+ return err
+ }
+ if n.SessionID == "" {
+ return errors.New("missing session id")
+ }
+ sc, err := e.lc.GetServeConfig(ctx)
+ if err != nil {
+ return fmt.Errorf("error getting serve config: %w", err)
+ }
+ if sc == nil {
+ sc = &ipn.ServeConfig{}
+ }
+ setHandler(sc, req, n.SessionID)
+ err = e.lc.SetServeConfig(ctx, sc)
+ if err != nil {
+ return fmt.Errorf("error setting serve config: %w", err)
+ }
+
+ fmt.Fprintf(os.Stderr, "Funnel started on \"https://%s\".\n", strings.TrimSuffix(string(req.HostPort), ":443"))
+ fmt.Fprintf(os.Stderr, "Press Ctrl-C to stop Funnel.\n\n")
- fmt.Fprintf(os.Stderr, "Serve started on \"https://%s\".\n", strings.TrimSuffix(string(req.HostPort), ":443"))
- fmt.Fprintf(os.Stderr, "Press Ctrl-C to stop.\n\n")
- _, err = io.Copy(os.Stdout, stream)
- return err
+ for {
+ n, err := watcher.Next()
+ if err != nil {
+ return fmt.Errorf("error calling next: %w", err)
+ }
+ if n.FunnelRequestLog == nil {
+ continue
+ }
+ bts, _ := json.Marshal(n.FunnelRequestLog)
+ fmt.Printf("%s\n", bts)
+ }
+}
+
+func setHandler(sc *ipn.ServeConfig, req ipn.ServeStreamRequest, sessionID string) {
+ if sc.Foreground == nil {
+ sc.Foreground = make(map[string]*ipn.ServeConfig)
+ }
+ if sc.Foreground[sessionID] == nil {
+ sc.Foreground[sessionID] = &ipn.ServeConfig{}
+ }
+ if sc.Foreground[sessionID].TCP == nil {
+ sc.Foreground[sessionID].TCP = make(map[uint16]*ipn.TCPPortHandler)
+ }
+ if _, ok := sc.Foreground[sessionID].TCP[443]; !ok {
+ sc.Foreground[sessionID].TCP[443] = &ipn.TCPPortHandler{HTTPS: true}
+ }
+ if sc.Foreground[sessionID].Web == nil {
+ sc.Foreground[sessionID].Web = make(map[ipn.HostPort]*ipn.WebServerConfig)
+ }
+ wsc, ok := sc.Foreground[sessionID].Web[req.HostPort]
+ if !ok {
+ wsc = &ipn.WebServerConfig{}
+ sc.Foreground[sessionID].Web[req.HostPort] = wsc
+ }
+ if wsc.Handlers == nil {
+ wsc.Handlers = make(map[string]*ipn.HTTPHandler)
+ }
+ wsc.Handlers[req.MountPoint] = &ipn.HTTPHandler{
+ Proxy: req.Source,
+ }
+ if sc.AllowFunnel == nil {
+ sc.AllowFunnel = make(map[ipn.HostPort]bool)
+ }
+ sc.AllowFunnel[req.HostPort] = true
}