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.
2022-03-18 14:44:05 +00:00
//go:build go1.18
// +build go1.18
2021-03-25 15:59:00 +00:00
// 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.
package main // import "tailscale.com/cmd/tailscaled"
2020-02-05 22:16:58 +00:00
import (
"context"
2021-03-12 21:04:47 +00:00
"errors"
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"
2021-03-01 18:08:53 +00:00
"net"
2020-02-05 22:16:58 +00:00
"net/http"
"net/http/pprof"
2020-05-20 15:40:34 +00:00
"os"
2020-07-13 10:17:58 +00:00
"os/signal"
2021-11-03 16:03:11 +00:00
"path/filepath"
2020-04-30 20:20:09 +00:00
"runtime"
2020-05-20 15:40:34 +00:00
"runtime/debug"
2021-03-12 21:04:47 +00:00
"strings"
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
2021-11-30 23:53:34 +00:00
"inet.af/netaddr"
2022-02-24 22:03:36 +00:00
"tailscale.com/cmd/tailscaled/childproc"
2022-02-18 20:55:22 +00:00
"tailscale.com/control/controlclient"
2022-01-24 18:52:57 +00:00
"tailscale.com/envknob"
2021-07-19 21:14:24 +00:00
"tailscale.com/ipn"
2020-02-05 22:16:58 +00:00
"tailscale.com/ipn/ipnserver"
2022-02-28 21:08:45 +00:00
"tailscale.com/ipn/store"
2020-02-05 22:16:58 +00:00
"tailscale.com/logpolicy"
2021-11-16 04:52:43 +00:00
"tailscale.com/logtail"
2021-04-02 05:35:26 +00:00
"tailscale.com/net/dns"
2021-09-09 22:20:08 +00:00
"tailscale.com/net/netns"
2021-11-27 00:00:39 +00:00
"tailscale.com/net/proxymux"
2021-11-30 23:53:34 +00:00
"tailscale.com/net/socks5"
"tailscale.com/net/tsdial"
2021-03-27 05:14:08 +00:00
"tailscale.com/net/tstun"
2020-03-03 17:33:09 +00:00
"tailscale.com/paths"
2021-11-05 21:05:13 +00:00
"tailscale.com/safesocket"
2022-01-20 17:27:00 +00:00
"tailscale.com/tsweb"
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-12-14 02:51:24 +00:00
"tailscale.com/util/clientmetric"
2021-11-02 21:30:48 +00:00
"tailscale.com/util/multierr"
2021-04-22 07:25:00 +00:00
"tailscale.com/util/osshare"
2020-10-27 04:23:58 +00:00
"tailscale.com/version"
2021-03-12 21:04:47 +00:00
"tailscale.com/version/distro"
2020-02-05 22:16:58 +00:00
"tailscale.com/wgengine"
2021-03-02 06:09:43 +00:00
"tailscale.com/wgengine/monitor"
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-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"
2021-03-12 21:04:47 +00:00
case "linux" :
2022-03-02 03:49:24 +00:00
switch distro . Get ( ) {
case distro . Synology :
2021-03-12 21:04:47 +00:00
// Try TUN, but fall back to userspace networking if needed.
// See https://github.com/tailscale/tailscale-synology/issues/35
return "tailscale0,userspace-networking"
2022-03-02 03:49:24 +00:00
case distro . Gokrazy :
// Gokrazy doesn't yet work in tun mode because the whole
// Gokrazy thing is no C code, and Tailscale currently
// depends on the iptables binary for Linux's
// wgengine/router.
// But on Gokrazy there's no legacy iptables, so we could use netlink
// to program nft-iptables directly. It just isn't done yet;
// see https://github.com/tailscale/tailscale/issues/391
//
// But Gokrazy does have the tun module built-in, so users
// can stil run --tun=tailscale0 if they wish, if they
// arrange for iptables to be present or run in "tailscale
// up --netfilter-mode=off" mode, perhaps. Untested.
return "userspace-networking"
2021-03-12 21:04:47 +00:00
}
2022-03-02 03:49:24 +00:00
2020-07-13 16:23:44 +00:00
}
return "tailscale0"
}
2020-07-23 18:58:28 +00:00
var args struct {
2021-07-23 16:45:04 +00:00
// tunname is a /dev/net/tun tunnel name ("tailscale0"), the
// string "userspace-networking", "tap:TAPNAME[:BRIDGENAME]"
// or comma-separated list thereof.
tunname string
2021-08-30 16:45:55 +00:00
cleanup bool
debug string
port uint16
statepath string
2021-11-03 16:03:11 +00:00
statedir string
2021-08-30 16:45:55 +00:00
socketpath string
birdSocketPath string
verbose int
socksAddr string // listen address for SOCKS5 server
2021-09-28 17:16:05 +00:00
httpProxyAddr string // listen address for HTTP proxy server
2020-07-23 18:58:28 +00:00
}
2021-02-15 05:11:06 +00:00
var (
2021-08-30 16:45:55 +00:00
installSystemDaemon func ( [ ] string ) error // non-nil on some platforms
uninstallSystemDaemon func ( [ ] string ) error // non-nil on some platforms
createBIRDClient func ( string ) ( wgengine . BIRDClient , error ) // non-nil on some platforms
2021-02-15 05:11:06 +00:00
)
var subCommands = map [ string ] * func ( [ ] string ) error {
"install-system-daemon" : & installSystemDaemon ,
"uninstall-system-daemon" : & uninstallSystemDaemon ,
"debug" : & debugModeFunc ,
2022-02-24 22:03:36 +00:00
"be-child" : & beChildFunc ,
2021-02-15 05:11:06 +00:00
}
2021-02-13 20:57:49 +00:00
2021-03-25 15:59:00 +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 . StringVar ( & args . debug , "debug" , "" , "listen address ([ip]:port) of optional debug server" )
2021-03-01 18:08:53 +00:00
flag . StringVar ( & args . socksAddr , "socks5-server" , "" , ` optional [ip]:port to run a SOCK5 server (e.g. "localhost:1080") ` )
2021-09-29 18:23:05 +00:00
flag . StringVar ( & args . httpProxyAddr , "outbound-http-proxy-listen" , "" , ` optional [ip]:port to run an outbound HTTP proxy (e.g. "localhost:8080") ` )
2021-03-01 18:08:53 +00:00
flag . StringVar ( & args . tunname , "tun" , defaultTunName ( ) , ` tunnel interface name; use "userspace-networking" (beta) to not use TUN ` )
2021-04-27 20:20:58 +00:00
flag . Var ( flagtype . PortValue ( & args . port , 0 ) , "port" , "UDP port to listen on for WireGuard and peer-to-peer traffic; 0 means automatically select" )
2022-02-18 20:55:22 +00:00
flag . StringVar ( & args . statepath , "state" , paths . DefaultTailscaledStateFile ( ) , "absolute path of state file; use 'kube:<secret-name>' to use Kubernetes secrets or 'arn:aws:ssm:...' to store in AWS SSM; use 'mem:' to not store state and register as an emphemeral node. If empty and --statedir is provided, the default is <statedir>/tailscaled.state" )
2021-11-03 16:03:11 +00:00
flag . StringVar ( & args . statedir , "statedir" , "" , "path to directory for storage of config state, TLS certs, temporary incoming Taildrop files, etc. If empty, it's derived from --state when possible." )
2020-09-25 20:10:53 +00:00
flag . StringVar ( & args . socketpath , "socket" , paths . DefaultTailscaledSocket ( ) , "path of the service unix socket" )
2021-08-30 16:45:55 +00:00
flag . StringVar ( & args . birdSocketPath , "bird-socket" , "" , "path of the bird 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-08-05 22:38:38 +00:00
if runtime . GOOS == "darwin" && os . Getuid ( ) != 0 && ! strings . Contains ( args . tunname , "userspace-networking" ) && ! args . cleanup {
2021-02-17 23:45:50 +00:00
log . SetFlags ( 0 )
2021-03-02 16:36:25 +00:00
log . Fatalf ( "tailscaled requires root; use sudo tailscaled (or use --tun=userspace-networking)" )
2021-02-17 23:45:50 +00:00
}
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" )
}
2021-08-30 16:45:55 +00:00
if args . birdSocketPath != "" && createBIRDClient == nil {
log . SetFlags ( 0 )
log . Fatalf ( "--bird-socket is not supported on %s" , runtime . GOOS )
}
2021-04-22 07:25:00 +00:00
err := run ( )
// Remove file sharing from Windows shell (noop in non-windows)
osshare . SetFileSharingEnabled ( false , logger . Discard )
if err != nil {
2021-12-08 23:13:39 +00:00
log . Fatal ( err )
2020-07-23 18:58:28 +00:00
}
}
2021-08-18 20:34:31 +00:00
func trySynologyMigration ( p string ) error {
if runtime . GOOS != "linux" || distro . Get ( ) != distro . Synology {
return nil
}
fi , err := os . Stat ( p )
if err == nil && fi . Size ( ) > 0 || ! os . IsNotExist ( err ) {
return err
}
// File is empty or doesn't exist, try reading from the old path.
const oldPath = "/var/packages/Tailscale/etc/tailscaled.state"
if _ , err := os . Stat ( oldPath ) ; err != nil {
if os . IsNotExist ( err ) {
return nil
}
return err
}
if err := os . Chown ( oldPath , os . Getuid ( ) , os . Getgid ( ) ) ; err != nil {
return err
}
if err := os . Rename ( oldPath , p ) ; err != nil {
return err
}
return nil
}
2021-11-03 16:03:11 +00:00
func statePathOrDefault ( ) string {
if args . statepath != "" {
return args . statepath
}
if args . statedir != "" {
return filepath . Join ( args . statedir , "tailscaled.state" )
}
return ""
}
2021-07-19 22:01:41 +00:00
func ipnServerOpts ( ) ( o ipnserver . Options ) {
// Allow changing the OS-specific IPN behavior for tests
// so we can e.g. test Windows-specific behaviors on Linux.
2022-01-24 18:52:57 +00:00
goos := envknob . String ( "TS_DEBUG_TAILSCALED_IPN_GOOS" )
2021-07-19 22:01:41 +00:00
if goos == "" {
goos = runtime . GOOS
}
2021-11-03 16:03:11 +00:00
o . VarRoot = args . statedir
// If an absolute --state is provided but not --statedir, try to derive
// a state directory.
if o . VarRoot == "" && filepath . IsAbs ( args . statepath ) {
if dir := filepath . Dir ( args . statepath ) ; strings . EqualFold ( filepath . Base ( dir ) , "tailscale" ) {
o . VarRoot = dir
}
}
2022-02-18 20:55:22 +00:00
if strings . HasPrefix ( statePathOrDefault ( ) , "mem:" ) {
// Register as an ephemeral node.
o . LoginFlags = controlclient . LoginEphemeral
}
2021-07-19 22:01:41 +00:00
switch goos {
2022-02-18 20:55:22 +00:00
case "js" :
// The js/wasm client has no state storage so for now
// treat all interactive logins as ephemeral.
// TODO(bradfitz): if we start using browser LocalStorage
// or something, then rethink this.
o . LoginFlags = controlclient . LoginEphemeral
fallthrough
2021-07-19 22:01:41 +00:00
default :
o . SurviveDisconnects = true
o . AutostartStateKey = ipn . GlobalDaemonStateKey
case "windows" :
// Not those.
}
return o
}
2020-07-23 18:58:28 +00:00
func run ( ) error {
var err error
2021-11-16 04:52:43 +00:00
pol := logpolicy . New ( logtail . CollectionNode )
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
2022-01-24 18:52:57 +00:00
if envknob . Bool ( "TS_DEBUG_MEMORY" ) {
2020-10-19 14:56:23 +00:00
logf = logger . RusagePrefixLog ( logf )
}
2020-07-23 18:58:28 +00:00
logf = logger . RateLimitedFn ( logf , 5 * time . Second , 5 , 100 )
if args . cleanup {
2022-01-24 18:52:57 +00:00
if envknob . Bool ( "TS_PLEASE_PANIC" ) {
2021-07-28 22:17:31 +00:00
panic ( "TS_PLEASE_PANIC asked us to panic" )
}
2021-04-02 05:35:26 +00:00
dns . Cleanup ( logf , args . tunname )
2020-07-23 18:58:28 +00:00
router . Cleanup ( logf , args . tunname )
return nil
}
2021-11-03 16:03:11 +00:00
if args . statepath == "" && args . statedir == "" {
log . Fatalf ( "--statedir (or at least --state) is required" )
2021-01-22 19:35:22 +00:00
}
2021-11-03 16:03:11 +00:00
if err := trySynologyMigration ( statePathOrDefault ( ) ) ; err != nil {
2021-08-18 20:34:31 +00:00
log . Printf ( "error in synology migration: %v" , err )
}
2021-01-22 19:35:22 +00:00
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-02-05 22:16:58 +00:00
}
2021-03-02 06:09:43 +00:00
linkMon , err := monitor . New ( logf )
if err != nil {
2021-12-08 23:13:39 +00:00
return fmt . Errorf ( "monitor.New: %w" , err )
2021-03-02 06:09:43 +00:00
}
pol . Logtail . SetLinkMonitor ( linkMon )
2021-11-27 00:00:39 +00:00
socksListener , httpProxyListener := mustStartProxyListeners ( args . socksAddr , args . httpProxyAddr )
2021-03-01 18:08:53 +00:00
2021-12-01 04:39:12 +00:00
dialer := new ( tsdial . Dialer ) // mutated below (before used)
e , useNetstack , err := createEngine ( logf , linkMon , dialer )
2020-02-05 22:16:58 +00:00
if err != nil {
2021-12-08 23:13:39 +00:00
return fmt . Errorf ( "createEngine: %w" , err )
2020-02-05 22:16:58 +00:00
}
2021-11-23 05:45:34 +00:00
if _ , ok := e . ( wgengine . ResolvingEngine ) . GetResolver ( ) ; ! ok {
panic ( "internal error: exit node resolver not wired up" )
}
2021-12-21 18:26:13 +00:00
if debugMux != nil {
if ig , ok := e . ( wgengine . InternalsGetter ) ; ok {
if _ , mc , ok := ig . GetInternals ( ) ; ok {
debugMux . HandleFunc ( "/debug/magicsock" , mc . ServeHTTPDebug )
}
}
go runDebugServer ( debugMux , args . debug )
}
2021-03-01 18:08:53 +00:00
2021-12-02 19:10:35 +00:00
ns , err := newNetstack ( logf , dialer , e )
2021-10-29 23:21:18 +00:00
if err != nil {
return fmt . Errorf ( "newNetstack: %w" , err )
}
ns . ProcessLocalIPs = useNetstack
ns . ProcessSubnets = useNetstack || wrapNetstack
2021-03-01 18:08:53 +00:00
2021-11-30 23:53:34 +00:00
if useNetstack {
dialer . UseNetstackForIP = func ( ip netaddr . IP ) bool {
_ , ok := e . PeerForIP ( ip )
return ok
}
dialer . NetstackDialTCP = func ( ctx context . Context , dst netaddr . IPPort ) ( net . Conn , error ) {
2021-12-03 16:33:05 +00:00
return ns . DialContextTCP ( ctx , dst )
2021-11-30 23:53:34 +00:00
}
}
2021-09-28 17:16:05 +00:00
if socksListener != nil || httpProxyListener != nil {
if httpProxyListener != nil {
2021-12-01 04:39:12 +00:00
hs := & http . Server { Handler : httpProxyHandler ( dialer . UserDial ) }
2021-09-28 17:16:05 +00:00
go func ( ) {
log . Fatalf ( "HTTP proxy exited: %v" , hs . Serve ( httpProxyListener ) )
} ( )
}
if socksListener != nil {
2021-11-30 23:53:34 +00:00
ss := & socks5 . Server {
Logf : logger . WithPrefix ( logf , "socks5: " ) ,
2021-12-01 04:39:12 +00:00
Dialer : dialer . UserDial ,
2021-11-30 23:53:34 +00:00
}
2021-09-28 17:16:05 +00:00
go func ( ) {
2021-11-30 23:53:34 +00:00
log . Fatalf ( "SOCKS5 server exited: %v" , ss . Serve ( socksListener ) )
2021-09-28 17:16:05 +00:00
} ( )
}
2021-03-01 18:08:53 +00:00
}
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
}
} ( )
2021-07-19 22:01:41 +00:00
opts := ipnServerOpts ( )
2021-11-05 19:40:07 +00:00
2022-02-28 21:08:45 +00:00
store , err := store . New ( logf , statePathOrDefault ( ) )
2021-11-05 19:40:07 +00:00
if err != nil {
2022-02-28 21:08:45 +00:00
return fmt . Errorf ( "store.New: %w" , err )
2021-11-05 19:40:07 +00:00
}
2021-12-01 04:39:12 +00:00
srv , err := ipnserver . New ( logf , pol . PublicID . String ( ) , store , e , dialer , nil , opts )
2021-11-05 19:40:07 +00:00
if err != nil {
2021-12-08 23:13:39 +00:00
return fmt . Errorf ( "ipnserver.New: %w" , err )
2021-11-05 19:40:07 +00:00
}
2021-08-26 21:50:55 +00:00
ns . SetLocalBackend ( srv . LocalBackend ( ) )
if err := ns . Start ( ) ; err != nil {
log . Fatalf ( "failed to start netstack: %v" , err )
}
2021-11-05 19:40:07 +00:00
if debugMux != nil {
debugMux . HandleFunc ( "/debug/ipn" , srv . ServeHTMLStatus )
}
ln , _ , err := safesocket . Listen ( args . socketpath , safesocket . WindowsLocalPort )
if err != nil {
return fmt . Errorf ( "safesocket.Listen: %v" , err )
}
err = srv . Run ( ctx , ln )
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 {
2021-12-08 23:13:39 +00:00
return fmt . Errorf ( "ipnserver.Run: %w" , 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
}
2021-12-01 04:39:12 +00:00
func createEngine ( logf logger . Logf , linkMon * monitor . Mon , dialer * tsdial . Dialer ) ( e wgengine . Engine , useNetstack bool , err error ) {
2021-03-12 21:04:47 +00:00
if args . tunname == "" {
return nil , false , errors . New ( "no --tun value specified" )
}
var errs [ ] error
for _ , name := range strings . Split ( args . tunname , "," ) {
logf ( "wgengine.NewUserspaceEngine(tun %q) ..." , name )
2021-12-01 04:39:12 +00:00
e , useNetstack , err = tryEngine ( logf , linkMon , dialer , name )
2021-03-12 21:04:47 +00:00
if err == nil {
2021-04-01 15:07:54 +00:00
return e , useNetstack , nil
2021-03-12 21:04:47 +00:00
}
logf ( "wgengine.NewUserspaceEngine(tun %q) error: %v" , name , err )
errs = append ( errs , err )
}
2021-11-02 21:30:48 +00:00
return nil , false , multierr . New ( errs ... )
2021-03-12 21:04:47 +00:00
}
2021-04-01 16:35:41 +00:00
var wrapNetstack = shouldWrapNetstack ( )
func shouldWrapNetstack ( ) bool {
2022-01-24 18:52:57 +00:00
if v , ok := envknob . LookupBool ( "TS_DEBUG_WRAP_NETSTACK" ) ; ok {
2021-04-01 16:35:41 +00:00
return v
}
2021-04-10 01:42:21 +00:00
if distro . Get ( ) == distro . Synology {
return true
}
2021-04-01 16:35:41 +00:00
switch runtime . GOOS {
2021-08-15 01:38:26 +00:00
case "windows" , "darwin" , "freebsd" :
2021-04-01 16:35:41 +00:00
// Enable on Windows and tailscaled-on-macOS (this doesn't
2021-08-15 01:38:26 +00:00
// affect the GUI clients), and on FreeBSD.
2021-04-01 16:35:41 +00:00
return true
}
return false
}
2021-12-01 04:39:12 +00:00
func tryEngine ( logf logger . Logf , linkMon * monitor . Mon , dialer * tsdial . Dialer , name string ) ( e wgengine . Engine , useNetstack bool , err error ) {
2021-03-27 04:03:21 +00:00
conf := wgengine . Config {
ListenPort : args . port ,
LinkMonitor : linkMon ,
2021-12-01 04:39:12 +00:00
Dialer : dialer ,
2021-03-27 04:03:21 +00:00
}
2021-09-11 05:24:30 +00:00
useNetstack = name == "userspace-networking"
netns . SetEnabled ( ! useNetstack )
2021-08-30 16:45:55 +00:00
if args . birdSocketPath != "" && createBIRDClient != nil {
log . Printf ( "Connecting to BIRD at %s ..." , args . birdSocketPath )
conf . BIRDClient , err = createBIRDClient ( args . birdSocketPath )
if err != nil {
2021-11-19 16:49:58 +00:00
return nil , false , fmt . Errorf ( "createBIRDClient: %w" , err )
2021-08-30 16:45:55 +00:00
}
}
2022-02-23 17:25:14 +00:00
if useNetstack {
if runtime . GOOS == "linux" && distro . Get ( ) == distro . Synology {
// On Synology in netstack mode, still init a DNS
// manager (directManager) to avoid the health check
// warnings in 'tailscale status' about DNS base
// configuration being unavailable (from the noop
// manager). More in Issue 4017.
// TODO(bradfitz): add a Synology-specific DNS manager.
conf . DNS , err = dns . NewOSConfigurator ( logf , "" ) // empty interface name
if err != nil {
return nil , false , fmt . Errorf ( "dns.NewOSConfigurator: %w" , err )
}
}
} else {
2021-04-06 04:45:56 +00:00
dev , devName , err := tstun . New ( logf , name )
2021-03-27 04:03:21 +00:00
if err != nil {
2021-03-27 05:14:08 +00:00
tstun . Diagnose ( logf , name )
2021-11-19 16:49:58 +00:00
return nil , false , fmt . Errorf ( "tstun.New(%q): %w" , name , err )
2021-03-27 04:03:21 +00:00
}
2021-03-29 02:25:01 +00:00
conf . Tun = dev
2021-07-23 16:45:04 +00:00
if strings . HasPrefix ( name , "tap:" ) {
conf . IsTAP = true
e , err := wgengine . NewUserspaceEngine ( logf , conf )
return e , false , err
}
2021-07-20 20:28:06 +00:00
r , err := router . New ( logf , dev , linkMon )
2021-03-29 01:59:33 +00:00
if err != nil {
dev . Close ( )
2021-11-19 16:49:58 +00:00
return nil , false , fmt . Errorf ( "creating router: %w" , err )
2021-03-29 01:59:33 +00:00
}
2021-04-12 22:51:37 +00:00
d , err := dns . NewOSConfigurator ( logf , devName )
if err != nil {
2021-11-19 16:49:58 +00:00
return nil , false , fmt . Errorf ( "dns.NewOSConfigurator: %w" , err )
2021-04-12 22:51:37 +00:00
}
conf . DNS = d
2021-04-01 16:35:41 +00:00
conf . Router = r
if wrapNetstack {
conf . Router = netstack . NewSubnetRouterWrapper ( conf . Router )
}
2021-03-27 04:03:21 +00:00
}
2021-03-29 02:25:01 +00:00
e , err = wgengine . NewUserspaceEngine ( logf , conf )
2021-03-27 04:03:21 +00:00
if err != nil {
2021-04-01 15:07:54 +00:00
return nil , useNetstack , err
2021-03-27 04:03:21 +00:00
}
2021-04-01 15:07:54 +00:00
return e , useNetstack , nil
2021-03-27 04:03:21 +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 ( )
2020-12-14 02:51:24 +00:00
mux . HandleFunc ( "/debug/metrics" , servePrometheusMetrics )
2020-02-05 22:16:58 +00:00
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
}
2020-12-14 02:51:24 +00:00
func servePrometheusMetrics ( w http . ResponseWriter , r * http . Request ) {
w . Header ( ) . Set ( "Content-Type" , "text/plain" )
2022-01-20 17:27:00 +00:00
tsweb . VarzHandler ( w , r )
2020-12-14 02:51:24 +00:00
clientmetric . WritePrometheusExpositionFormat ( w )
}
2020-03-26 05:57:46 +00:00
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 )
}
}
2021-04-01 16:35:41 +00:00
2021-12-02 19:10:35 +00:00
func newNetstack ( logf logger . Logf , dialer * tsdial . Dialer , e wgengine . Engine ) ( * netstack . Impl , error ) {
2021-04-01 16:35:41 +00:00
tunDev , magicConn , ok := e . ( wgengine . InternalsGetter ) . GetInternals ( )
if ! ok {
2021-10-29 23:21:18 +00:00
return nil , fmt . Errorf ( "%T is not a wgengine.InternalsGetter" , e )
2021-04-01 16:35:41 +00:00
}
2021-12-02 19:10:35 +00:00
return netstack . Create ( logf , tunDev , e , magicConn , dialer )
2021-04-01 16:35:41 +00:00
}
2021-09-28 17:16:05 +00:00
2021-11-27 00:00:39 +00:00
// mustStartProxyListeners creates listeners for local SOCKS and HTTP
// proxies, if the respective addresses are not empty. socksAddr and
// httpAddr can be the same, in which case socksListener will receive
// connections that look like they're speaking SOCKS and httpListener
// will receive everything else.
//
// socksListener and httpListener can be nil, if their respective
// addrs are empty.
func mustStartProxyListeners ( socksAddr , httpAddr string ) ( socksListener , httpListener net . Listener ) {
if socksAddr == httpAddr && socksAddr != "" && ! strings . HasSuffix ( socksAddr , ":0" ) {
ln , err := net . Listen ( "tcp" , socksAddr )
if err != nil {
log . Fatalf ( "proxy listener: %v" , err )
}
return proxymux . SplitSOCKSAndHTTP ( ln )
2021-09-28 17:16:05 +00:00
}
2021-11-27 00:00:39 +00:00
var err error
if socksAddr != "" {
socksListener , err = net . Listen ( "tcp" , socksAddr )
if err != nil {
log . Fatalf ( "SOCKS5 listener: %v" , err )
}
if strings . HasSuffix ( socksAddr , ":0" ) {
// Log kernel-selected port number so integration tests
// can find it portably.
log . Printf ( "SOCKS5 listening on %v" , socksListener . Addr ( ) )
}
2021-09-28 17:16:05 +00:00
}
2021-11-27 00:00:39 +00:00
if httpAddr != "" {
httpListener , err = net . Listen ( "tcp" , httpAddr )
if err != nil {
log . Fatalf ( "HTTP proxy listener: %v" , err )
}
if strings . HasSuffix ( httpAddr , ":0" ) {
// Log kernel-selected port number so integration tests
// can find it portably.
log . Printf ( "HTTP proxy listening on %v" , httpListener . Addr ( ) )
}
2021-09-28 17:16:05 +00:00
}
2021-11-27 00:00:39 +00:00
return socksListener , httpListener
2021-09-28 17:16:05 +00:00
}
2022-02-24 22:03:36 +00:00
var beChildFunc = beChild
func beChild ( args [ ] string ) error {
if len ( args ) == 0 {
return errors . New ( "missing mode argument" )
}
typ := args [ 0 ]
f , ok := childproc . Code [ typ ]
if ! ok {
return fmt . Errorf ( "unknown be-child mode %q" , typ )
}
return f ( args [ 1 : ] )
}