mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00

Currently just serving a "Hello world" page when running the web cli in --dev mode. Updates tailscale/corp#13775 Co-authored-by: Will Norris <will@tailscale.com> Signed-off-by: Sonia Appasamy <sonia@tailscale.com>
113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
_ "embed"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"net/http"
|
|
"net/http/cgi"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/peterbourgon/ff/v3/ffcli"
|
|
"tailscale.com/client/web"
|
|
"tailscale.com/util/cmpx"
|
|
)
|
|
|
|
var webCmd = &ffcli.Command{
|
|
Name: "web",
|
|
ShortUsage: "web [flags]",
|
|
ShortHelp: "Run a web server for controlling Tailscale",
|
|
|
|
LongHelp: strings.TrimSpace(`
|
|
"tailscale web" runs a webserver for controlling the Tailscale daemon.
|
|
|
|
It's primarily intended for use on Synology, QNAP, and other
|
|
NAS devices where a web interface is the natural place to control
|
|
Tailscale, as opposed to a CLI or a native app.
|
|
`),
|
|
|
|
FlagSet: (func() *flag.FlagSet {
|
|
webf := newFlagSet("web")
|
|
webf.StringVar(&webArgs.listen, "listen", "localhost:8088", "listen address; use port 0 for automatic")
|
|
webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script")
|
|
webf.BoolVar(&webArgs.dev, "dev", false, "run web client in developer mode [this flag is in development, use is unsupported]")
|
|
return webf
|
|
})(),
|
|
Exec: runWeb,
|
|
}
|
|
|
|
var webArgs struct {
|
|
listen string
|
|
cgi bool
|
|
dev bool
|
|
}
|
|
|
|
func tlsConfigFromEnvironment() *tls.Config {
|
|
crt := os.Getenv("TLS_CRT_PEM")
|
|
key := os.Getenv("TLS_KEY_PEM")
|
|
if crt == "" || key == "" {
|
|
return nil
|
|
}
|
|
|
|
// We support passing in the complete certificate and key from environment
|
|
// variables because pfSense stores its cert+key in the PHP config. We populate
|
|
// TLS_CRT_PEM and TLS_KEY_PEM from PHP code before starting tailscale web.
|
|
// These are the PEM-encoded Certificate and Private Key.
|
|
|
|
cert, err := tls.X509KeyPair([]byte(crt), []byte(key))
|
|
if err != nil {
|
|
log.Printf("tlsConfigFromEnvironment: %v", err)
|
|
|
|
// Fallback to unencrypted HTTP.
|
|
return nil
|
|
}
|
|
|
|
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
|
}
|
|
|
|
func runWeb(ctx context.Context, args []string) error {
|
|
if len(args) > 0 {
|
|
return fmt.Errorf("too many non-flag arguments: %q", args)
|
|
}
|
|
|
|
webServer, cleanup := web.NewServer(webArgs.dev, nil)
|
|
defer cleanup()
|
|
|
|
if webArgs.cgi {
|
|
if err := cgi.Serve(webServer); err != nil {
|
|
log.Printf("tailscale.cgi: %v", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
tlsConfig := tlsConfigFromEnvironment()
|
|
if tlsConfig != nil {
|
|
server := &http.Server{
|
|
Addr: webArgs.listen,
|
|
TLSConfig: tlsConfig,
|
|
Handler: webServer,
|
|
}
|
|
|
|
log.Printf("web server running on: https://%s", server.Addr)
|
|
return server.ListenAndServeTLS("", "")
|
|
} else {
|
|
log.Printf("web server running on: %s", urlOfListenAddr(webArgs.listen))
|
|
return http.ListenAndServe(webArgs.listen, webServer)
|
|
}
|
|
}
|
|
|
|
// urlOfListenAddr parses a given listen address into a formatted URL
|
|
func urlOfListenAddr(addr string) string {
|
|
host, port, _ := net.SplitHostPort(addr)
|
|
return fmt.Sprintf("http://%s", net.JoinHostPort(cmpx.Or(host, "127.0.0.1"), port))
|
|
}
|