2020-02-05 22:16:58 +00:00
|
|
|
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// The tailscaled program is the Tailscale client daemon. It's configured
|
|
|
|
// and controlled via the tailscale CLI program.
|
|
|
|
//
|
|
|
|
// It primarily supports Linux, though other systems will likely be
|
|
|
|
// supported in the future.
|
2020-02-10 21:32:16 +00:00
|
|
|
package main // import "tailscale.com/cmd/tailscaled"
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"net/http/pprof"
|
2020-05-20 15:40:34 +00:00
|
|
|
"os"
|
2020-07-13 10:17:58 +00:00
|
|
|
"os/signal"
|
2020-04-30 20:20:09 +00:00
|
|
|
"runtime"
|
2020-05-20 15:40:34 +00:00
|
|
|
"runtime/debug"
|
2020-07-13 10:17:58 +00:00
|
|
|
"syscall"
|
2020-05-15 20:13:44 +00:00
|
|
|
"time"
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
"github.com/apenwarr/fixconsole"
|
|
|
|
"github.com/pborman/getopt/v2"
|
|
|
|
"tailscale.com/ipn/ipnserver"
|
|
|
|
"tailscale.com/logpolicy"
|
2020-03-03 17:33:09 +00:00
|
|
|
"tailscale.com/paths"
|
2020-05-08 19:21:36 +00:00
|
|
|
"tailscale.com/types/logger"
|
2020-02-05 22:16:58 +00:00
|
|
|
"tailscale.com/wgengine"
|
2020-02-12 07:09:24 +00:00
|
|
|
"tailscale.com/wgengine/magicsock"
|
2020-07-13 10:17:58 +00:00
|
|
|
"tailscale.com/wgengine/router"
|
2020-02-05 22:16:58 +00:00
|
|
|
)
|
|
|
|
|
2020-02-16 02:14:50 +00:00
|
|
|
// globalStateKey is the ipn.StateKey that tailscaled loads on
|
|
|
|
// startup.
|
|
|
|
//
|
|
|
|
// We have to support multiple state keys for other OSes (Windows in
|
|
|
|
// particular), but right now Unix daemons run with a single
|
|
|
|
// node-global state. To keep open the option of having per-user state
|
|
|
|
// later, the global state key doesn't look like a username.
|
|
|
|
const globalStateKey = "_daemon"
|
|
|
|
|
2020-05-20 14:42:57 +00:00
|
|
|
func main() {
|
2020-05-20 15:40:34 +00:00
|
|
|
// We aren't very performance sensitive, and the parts that are
|
|
|
|
// performance sensitive (wireguard) try hard not to do any memory
|
|
|
|
// allocations. So let's be aggressive about garbage collection,
|
|
|
|
// unless the user specifically overrides it in the usual way.
|
|
|
|
if _, ok := os.LookupEnv("GOGC"); !ok {
|
|
|
|
debug.SetGCPercent(10)
|
|
|
|
}
|
|
|
|
|
2020-05-20 14:42:57 +00:00
|
|
|
defaultTunName := "tailscale0"
|
2020-04-30 20:20:09 +00:00
|
|
|
if runtime.GOOS == "openbsd" {
|
|
|
|
defaultTunName = "tun"
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:17:58 +00:00
|
|
|
cleanup := getopt.BoolLong("cleanup", 0, "clean up system state and exit")
|
2020-02-05 22:16:58 +00:00
|
|
|
fake := getopt.BoolLong("fake", 0, "fake tunnel+routing instead of tuntap")
|
|
|
|
debug := getopt.StringLong("debug", 0, "", "Address of debug server")
|
2020-04-30 20:20:09 +00:00
|
|
|
tunname := getopt.StringLong("tun", 0, defaultTunName, "tunnel interface name")
|
2020-02-12 07:09:24 +00:00
|
|
|
listenport := getopt.Uint16Long("port", 'p', magicsock.DefaultPort, "WireGuard port (0=autoselect)")
|
2020-03-03 17:33:09 +00:00
|
|
|
statepath := getopt.StringLong("state", 0, paths.DefaultTailscaledStateFile(), "Path of state file")
|
|
|
|
socketpath := getopt.StringLong("socket", 's', paths.DefaultTailscaledSocket(), "Path of the service unix socket")
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
logf := wgengine.RusagePrefixLog(log.Printf)
|
2020-05-20 15:49:35 +00:00
|
|
|
logf = logger.RateLimitedFn(logf, 5*time.Second, 5, 100)
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
err := fixconsole.FixConsoleIfNeeded()
|
|
|
|
if err != nil {
|
2020-04-11 15:35:34 +00:00
|
|
|
logf("fixConsoleOutput: %v", err)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-02-19 03:21:02 +00:00
|
|
|
pol := logpolicy.New("tailnode.log.tailscale.io")
|
2020-02-05 22:16:58 +00:00
|
|
|
|
|
|
|
getopt.Parse()
|
|
|
|
if len(getopt.Args()) > 0 {
|
|
|
|
log.Fatalf("too many non-flag arguments: %#v", getopt.Args()[0])
|
|
|
|
}
|
|
|
|
|
2020-07-13 10:17:58 +00:00
|
|
|
if *cleanup {
|
|
|
|
router.Cleanup(logf, *tunname)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-02-16 02:14:50 +00:00
|
|
|
if *statepath == "" {
|
|
|
|
log.Fatalf("--state is required")
|
|
|
|
}
|
|
|
|
|
2020-02-18 20:33:28 +00:00
|
|
|
if *socketpath == "" {
|
|
|
|
log.Fatalf("--socket is required")
|
|
|
|
}
|
|
|
|
|
2020-03-26 05:57:46 +00:00
|
|
|
var debugMux *http.ServeMux
|
2020-02-05 22:16:58 +00:00
|
|
|
if *debug != "" {
|
2020-03-26 05:57:46 +00:00
|
|
|
debugMux = newDebugMux()
|
|
|
|
go runDebugServer(debugMux, *debug)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var e wgengine.Engine
|
|
|
|
if *fake {
|
2020-02-14 23:03:25 +00:00
|
|
|
e, err = wgengine.NewFakeUserspaceEngine(logf, 0)
|
2020-02-05 22:16:58 +00:00
|
|
|
} else {
|
2020-02-14 23:03:25 +00:00
|
|
|
e, err = wgengine.NewUserspaceEngine(logf, *tunname, *listenport)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
if err != nil {
|
2020-04-11 15:35:34 +00:00
|
|
|
log.Fatalf("wgengine.New: %v", err)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
e = wgengine.NewWatchdog(e)
|
|
|
|
|
2020-07-13 10:17:58 +00:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
// Exit gracefully by cancelling the ipnserver context in most common cases:
|
|
|
|
// interrupted from the TTY or killed by a service manager.
|
|
|
|
go func() {
|
|
|
|
interrupt := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
select {
|
|
|
|
case <-interrupt:
|
|
|
|
cancel()
|
|
|
|
case <-ctx.Done():
|
|
|
|
// continue
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-02-05 22:16:58 +00:00
|
|
|
opts := ipnserver.Options{
|
2020-02-18 20:33:28 +00:00
|
|
|
SocketPath: *socketpath,
|
2020-03-12 01:00:25 +00:00
|
|
|
Port: 41112,
|
2020-02-03 18:35:52 +00:00
|
|
|
StatePath: *statepath,
|
2020-02-16 02:14:50 +00:00
|
|
|
AutostartStateKey: globalStateKey,
|
2020-07-13 15:59:01 +00:00
|
|
|
LegacyConfigPath: paths.LegacyConfigPath(),
|
2020-02-05 22:16:58 +00:00
|
|
|
SurviveDisconnects: true,
|
2020-03-26 05:57:46 +00:00
|
|
|
DebugMux: debugMux,
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
2020-07-13 10:17:58 +00:00
|
|
|
err = ipnserver.Run(ctx, logf, pol.PublicID.String(), opts, e)
|
2020-02-05 22:16:58 +00:00
|
|
|
if err != nil {
|
2020-04-11 15:35:34 +00:00
|
|
|
log.Fatalf("tailscaled: %v", err)
|
2020-02-05 22:16:58 +00:00
|
|
|
}
|
|
|
|
|
2020-07-13 10:17:58 +00:00
|
|
|
// Finish uploading logs after closing everything else.
|
|
|
|
ctx, cancel = context.WithTimeout(context.Background(), time.Second)
|
2020-02-05 22:16:58 +00:00
|
|
|
cancel()
|
|
|
|
pol.Shutdown(ctx)
|
|
|
|
}
|
|
|
|
|
2020-03-26 05:57:46 +00:00
|
|
|
func newDebugMux() *http.ServeMux {
|
2020-02-05 22:16:58 +00:00
|
|
|
mux := http.NewServeMux()
|
|
|
|
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
|
|
|
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
|
|
|
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
|
|
|
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
|
|
|
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
2020-03-26 05:57:46 +00:00
|
|
|
return mux
|
|
|
|
}
|
|
|
|
|
|
|
|
func runDebugServer(mux *http.ServeMux, addr string) {
|
|
|
|
srv := &http.Server{
|
2020-02-05 22:16:58 +00:00
|
|
|
Addr: addr,
|
|
|
|
Handler: mux,
|
|
|
|
}
|
|
|
|
if err := srv.ListenAndServe(); err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|