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"
2020-09-25 20:10:53 +00:00
"flag"
2020-10-27 04:23:58 +00:00
"fmt"
2020-02-05 22:16:58 +00:00
"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-10-19 14:56:23 +00:00
"strconv"
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
"tailscale.com/ipn/ipnserver"
"tailscale.com/logpolicy"
2020-03-03 17:33:09 +00:00
"tailscale.com/paths"
2020-09-25 20:10:53 +00:00
"tailscale.com/types/flagtype"
2020-05-08 19:21:36 +00:00
"tailscale.com/types/logger"
2020-10-27 04:23:58 +00:00
"tailscale.com/version"
2020-02-05 22:16:58 +00:00
"tailscale.com/wgengine"
2020-02-12 07:09:24 +00:00
"tailscale.com/wgengine/magicsock"
2020-09-03 22:45:41 +00:00
"tailscale.com/wgengine/netstack"
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-07-13 16:23:44 +00:00
// defaultTunName returns the default tun device name for the platform.
func defaultTunName ( ) string {
switch runtime . GOOS {
case "openbsd" :
return "tun"
case "windows" :
return "Tailscale"
2021-02-12 04:10:07 +00:00
case "darwin" :
// "utun" is recognized by wireguard-go/tun/tun_darwin.go
// as a magic value that uses/creates any free number.
return "utun"
2020-07-13 16:23:44 +00:00
}
return "tailscale0"
}
2020-07-23 18:58:28 +00:00
var args struct {
cleanup bool
fake bool
debug string
tunname string
port uint16
statepath string
socketpath string
2020-12-21 18:53:18 +00:00
verbose int
2020-07-23 18:58:28 +00:00
}
2021-02-15 05:11:06 +00:00
var (
installSystemDaemon func ( [ ] string ) error // non-nil on some platforms
uninstallSystemDaemon func ( [ ] string ) error // non-nil on some platforms
)
var subCommands = map [ string ] * func ( [ ] string ) error {
"install-system-daemon" : & installSystemDaemon ,
"uninstall-system-daemon" : & uninstallSystemDaemon ,
"debug" : & debugModeFunc ,
}
2021-02-13 20:57:49 +00:00
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-10-27 04:23:58 +00:00
printVersion := false
2020-12-21 18:53:18 +00:00
flag . IntVar ( & args . verbose , "verbose" , 0 , "log verbosity level; 0 is default, 1 or higher are increasingly verbose" )
2020-09-25 20:10:53 +00:00
flag . BoolVar ( & args . cleanup , "cleanup" , false , "clean up system state and exit" )
flag . BoolVar ( & args . fake , "fake" , false , "use userspace fake tunnel+routing instead of kernel TUN interface" )
flag . StringVar ( & args . debug , "debug" , "" , "listen address ([ip]:port) of optional debug server" )
flag . StringVar ( & args . tunname , "tun" , defaultTunName ( ) , "tunnel interface name" )
flag . Var ( flagtype . PortValue ( & args . port , magicsock . DefaultPort ) , "port" , "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select" )
flag . StringVar ( & args . statepath , "state" , paths . DefaultTailscaledStateFile ( ) , "path of state file" )
flag . StringVar ( & args . socketpath , "socket" , paths . DefaultTailscaledSocket ( ) , "path of the service unix socket" )
2020-10-27 04:23:58 +00:00
flag . BoolVar ( & printVersion , "version" , false , "print version information and exit" )
2020-02-05 22:16:58 +00:00
2021-02-13 20:57:49 +00:00
if len ( os . Args ) > 1 {
2021-02-15 05:11:06 +00:00
sub := os . Args [ 1 ]
if fp , ok := subCommands [ sub ] ; ok {
if * fp == nil {
2021-02-13 20:57:49 +00:00
log . SetFlags ( 0 )
2021-02-15 05:11:06 +00:00
log . Fatalf ( "%s not available on %v" , sub , runtime . GOOS )
}
if err := ( * fp ) ( os . Args [ 2 : ] ) ; err != nil {
2021-02-13 20:57:49 +00:00
log . SetFlags ( 0 )
log . Fatal ( err )
}
return
2021-02-04 20:20:07 +00:00
}
}
2021-02-05 17:53:54 +00:00
if beWindowsSubprocess ( ) {
return
}
2020-09-25 20:10:53 +00:00
flag . Parse ( )
if flag . NArg ( ) > 0 {
log . Fatalf ( "tailscaled does not take non-flag arguments: %q" , flag . Args ( ) )
2020-02-05 22:16:58 +00:00
}
2020-10-27 04:23:58 +00:00
if printVersion {
fmt . Println ( version . String ( ) )
os . Exit ( 0 )
}
2021-02-17 23:45:50 +00:00
if runtime . GOOS == "darwin" && os . Getuid ( ) != 0 {
log . SetFlags ( 0 )
log . Fatalf ( "tailscaled requires root; use sudo tailscaled" )
}
2020-07-23 18:58:28 +00:00
if args . socketpath == "" && runtime . GOOS != "windows" {
2021-02-17 23:45:50 +00:00
log . SetFlags ( 0 )
2020-02-18 20:33:28 +00:00
log . Fatalf ( "--socket is required" )
}
2020-07-23 18:58:28 +00:00
if err := run ( ) ; err != nil {
// No need to log; the func already did
os . Exit ( 1 )
}
}
func run ( ) error {
var err error
pol := logpolicy . New ( "tailnode.log.tailscale.io" )
2020-12-21 18:53:18 +00:00
pol . SetVerbosityLevel ( args . verbose )
2020-07-23 18:58:28 +00:00
defer func ( ) {
// Finish uploading logs after closing everything else.
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Second )
defer cancel ( )
pol . Shutdown ( ctx )
} ( )
2021-02-05 16:46:12 +00:00
if isWindowsService ( ) {
// Run the IPN server from the Windows service manager.
log . Printf ( "Running service..." )
if err := runWindowsService ( pol ) ; err != nil {
log . Printf ( "runservice: %v" , err )
}
log . Printf ( "Service ended." )
return nil
}
2020-10-19 14:56:23 +00:00
var logf logger . Logf = log . Printf
if v , _ := strconv . ParseBool ( os . Getenv ( "TS_DEBUG_MEMORY" ) ) ; v {
logf = logger . RusagePrefixLog ( logf )
}
2020-07-23 18:58:28 +00:00
logf = logger . RateLimitedFn ( logf , 5 * time . Second , 5 , 100 )
if args . cleanup {
router . Cleanup ( logf , args . tunname )
return nil
}
2021-01-22 19:35:22 +00:00
if args . statepath == "" {
log . Fatalf ( "--state is required" )
}
2020-03-26 05:57:46 +00:00
var debugMux * http . ServeMux
2020-07-23 18:58:28 +00:00
if args . debug != "" {
2020-03-26 05:57:46 +00:00
debugMux = newDebugMux ( )
2020-07-23 18:58:28 +00:00
go runDebugServer ( debugMux , args . debug )
2020-02-05 22:16:58 +00:00
}
var e wgengine . Engine
2020-07-23 18:58:28 +00:00
if args . fake {
2020-09-03 22:45:41 +00:00
var impl wgengine . FakeImplFunc
if args . tunname == "userspace-networking" {
impl = netstack . Impl
}
e , err = wgengine . NewFakeUserspaceEngine ( logf , 0 , impl )
2020-02-05 22:16:58 +00:00
} else {
2020-07-23 18:58:28 +00:00
e , err = wgengine . NewUserspaceEngine ( logf , args . tunname , args . port )
2020-02-05 22:16:58 +00:00
}
if err != nil {
2020-07-23 18:58:28 +00:00
logf ( "wgengine.New: %v" , err )
return 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:
2020-08-01 02:07:14 +00:00
// interrupted from the TTY or killed by a service manager.
2020-08-01 02:12:07 +00:00
interrupt := make ( chan os . Signal , 1 )
signal . Notify ( interrupt , syscall . SIGINT , syscall . SIGTERM )
// SIGPIPE sometimes gets generated when CLIs disconnect from
// tailscaled. The default action is to terminate the process, we
// want to keep running.
signal . Ignore ( syscall . SIGPIPE )
2020-07-13 10:17:58 +00:00
go func ( ) {
select {
2020-07-30 15:49:17 +00:00
case s := <- interrupt :
logf ( "tailscaled got signal %v; shutting down" , s )
2020-07-13 10:17:58 +00:00
cancel ( )
case <- ctx . Done ( ) :
// continue
}
} ( )
2020-02-05 22:16:58 +00:00
opts := ipnserver . Options {
2020-07-23 18:58:28 +00:00
SocketPath : args . socketpath ,
2020-03-12 01:00:25 +00:00
Port : 41112 ,
2020-07-23 18:58:28 +00:00
StatePath : args . 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-29 20:38:09 +00:00
err = ipnserver . Run ( ctx , logf , pol . PublicID . String ( ) , ipnserver . FixedEngine ( e ) , opts )
2020-07-23 18:58:28 +00:00
// Cancelation is not an error: it is the only way to stop ipnserver.
if err != nil && err != context . Canceled {
logf ( "ipnserver.Run: %v" , err )
return err
2020-02-05 22:16:58 +00:00
}
2020-07-23 18:58:28 +00:00
return nil
2020-02-05 22:16:58 +00:00
}
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 )
}
}