@ -89,8 +89,14 @@ type Server struct {
// If empty, the binary name is used.
// If empty, the binary name is used.
Hostname string
Hostname string
// Logf, if non-nil, specifies the logger to use. By default,
// UserLogf, if non-nil, specifies the logger to use for logs generated by
// log.Printf is used.
// the Server itself intended to be seen by the user such as the AuthURL for
// login and status updates. If unset, log.Printf is used.
UserLogf logger . Logf
// Logf, if set is used for logs generated by the backend such as the
// LocalBackend and MagicSock. It is verbose and intended for debugging.
// If unset, logs are discarded.
Logf logger . Logf
Logf logger . Logf
// Ephemeral, if true, specifies that the instance should register
// Ephemeral, if true, specifies that the instance should register
@ -244,15 +250,15 @@ func (s *Server) Loopback() (addr string, proxyCred, localAPICred string, err er
s . logf ( "localapi tcp serve error: %v" , err )
s . logf ( "localapi tcp serve error: %v" , err )
}
}
} ( )
} ( )
s5l := logger . WithPrefix ( s . logf , "socks5: " )
s5s := & socks5 . Server {
s5s := & socks5 . Server {
Logf : logger. WithPrefix ( s . logf , "socks5: " ) ,
Logf : s5l ,
Dialer : s . dialer . UserDial ,
Dialer : s . dialer . UserDial ,
Username : "tsnet" ,
Username : "tsnet" ,
Password : s . proxyCred ,
Password : s . proxyCred ,
}
}
go func ( ) {
go func ( ) {
s . logf ( "SOCKS5 server exited: %v" , s5s . Serve ( socksLn ) )
s5l ( "SOCKS5 server exited: %v" , s5s . Serve ( socksLn ) )
} ( )
} ( )
}
}
@ -484,14 +490,12 @@ func (s *Server) start() (reterr error) {
}
}
}
}
logf := s . logf
if s . rootPath == "" {
if s . rootPath == "" {
confDir , err := os . UserConfigDir ( )
confDir , err := os . UserConfigDir ( )
if err != nil {
if err != nil {
return err
return err
}
}
s . rootPath , err = getTSNetDir ( logf, confDir , prog )
s . rootPath , err = getTSNetDir ( s. logf, confDir , prog )
if err != nil {
if err != nil {
return err
return err
}
}
@ -505,19 +509,29 @@ func (s *Server) start() (reterr error) {
return fmt . Errorf ( "%v is not a directory" , s . rootPath )
return fmt . Errorf ( "%v is not a directory" , s . rootPath )
}
}
tsLogf := func ( format string , a ... any ) {
if s . logtail != nil {
s . logtail . Logf ( format , a ... )
}
if s . Logf == nil {
return
}
s . Logf ( format , a ... )
}
sys := new ( tsd . System )
sys := new ( tsd . System )
if err := s . startLogger ( & closePool , sys . HealthTracker ( ) ) ; err != nil {
if err := s . startLogger ( & closePool , sys . HealthTracker ( ) , tsLogf ); err != nil {
return err
return err
}
}
s . netMon , err = netmon . New ( logf )
s . netMon , err = netmon . New ( tsL ogf)
if err != nil {
if err != nil {
return err
return err
}
}
closePool . add ( s . netMon )
closePool . add ( s . netMon )
s . dialer = & tsdial . Dialer { Logf : l ogf} // mutated below (before used)
s . dialer = & tsdial . Dialer { Logf : tsL ogf} // mutated below (before used)
eng , err := wgengine . NewUserspaceEngine ( l ogf, wgengine . Config {
eng , err := wgengine . NewUserspaceEngine ( tsL ogf, wgengine . Config {
ListenPort : s . Port ,
ListenPort : s . Port ,
NetMon : s . netMon ,
NetMon : s . netMon ,
Dialer : s . dialer ,
Dialer : s . dialer ,
@ -532,7 +546,7 @@ func (s *Server) start() (reterr error) {
sys . Set ( eng )
sys . Set ( eng )
// TODO(oxtoacart): do we need to support Taildrive on tsnet, and if so, how?
// TODO(oxtoacart): do we need to support Taildrive on tsnet, and if so, how?
ns , err := netstack . Create ( l ogf, sys . Tun . Get ( ) , eng , sys . MagicSock . Get ( ) , s . dialer , sys . DNSManager . Get ( ) , sys . ProxyMapper ( ) , nil )
ns , err := netstack . Create ( tsL ogf, sys . Tun . Get ( ) , eng , sys . MagicSock . Get ( ) , s . dialer , sys . DNSManager . Get ( ) , sys . ProxyMapper ( ) , nil )
if err != nil {
if err != nil {
return fmt . Errorf ( "netstack.Create: %w" , err )
return fmt . Errorf ( "netstack.Create: %w" , err )
}
}
@ -559,8 +573,8 @@ func (s *Server) start() (reterr error) {
if s . Store == nil {
if s . Store == nil {
stateFile := filepath . Join ( s . rootPath , "tailscaled.state" )
stateFile := filepath . Join ( s . rootPath , "tailscaled.state" )
logf ( "tsnet running state path %s" , stateFile )
s . logf ( "tsnet running state path %s" , stateFile )
s . Store , err = store . New ( l ogf, stateFile )
s . Store , err = store . New ( tsL ogf, stateFile )
if err != nil {
if err != nil {
return err
return err
}
}
@ -571,13 +585,13 @@ func (s *Server) start() (reterr error) {
if s . Ephemeral {
if s . Ephemeral {
loginFlags = controlclient . LoginEphemeral
loginFlags = controlclient . LoginEphemeral
}
}
lb , err := ipnlocal . NewLocalBackend ( l ogf, s . logid , sys , loginFlags | controlclient . LocalBackendStartKeyOSNeutral )
lb , err := ipnlocal . NewLocalBackend ( tsL ogf, s . logid , sys , loginFlags | controlclient . LocalBackendStartKeyOSNeutral )
if err != nil {
if err != nil {
return fmt . Errorf ( "NewLocalBackend: %v" , err )
return fmt . Errorf ( "NewLocalBackend: %v" , err )
}
}
lb . SetTCPHandlerForFunnelFlow ( s . getTCPHandlerForFunnelFlow )
lb . SetTCPHandlerForFunnelFlow ( s . getTCPHandlerForFunnelFlow )
lb . SetVarRoot ( s . rootPath )
lb . SetVarRoot ( s . rootPath )
logf ( "tsnet starting with hostname %q, varRoot %q" , s . hostname , s . rootPath )
s . logf ( "tsnet starting with hostname %q, varRoot %q" , s . hostname , s . rootPath )
s . lb = lb
s . lb = lb
if err := ns . Start ( lb ) ; err != nil {
if err := ns . Start ( lb ) ; err != nil {
return fmt . Errorf ( "failed to start netstack: %w" , err )
return fmt . Errorf ( "failed to start netstack: %w" , err )
@ -598,17 +612,17 @@ func (s *Server) start() (reterr error) {
}
}
st := lb . State ( )
st := lb . State ( )
if st == ipn . NeedsLogin || envknob . Bool ( "TSNET_FORCE_LOGIN" ) {
if st == ipn . NeedsLogin || envknob . Bool ( "TSNET_FORCE_LOGIN" ) {
logf ( "LocalBackend state is %v; running StartLoginInteractive..." , st )
s . logf ( "LocalBackend state is %v; running StartLoginInteractive..." , st )
if err := s . lb . StartLoginInteractive ( s . shutdownCtx ) ; err != nil {
if err := s . lb . StartLoginInteractive ( s . shutdownCtx ) ; err != nil {
return fmt . Errorf ( "StartLoginInteractive: %w" , err )
return fmt . Errorf ( "StartLoginInteractive: %w" , err )
}
}
} else if authKey != "" {
} else if authKey != "" {
logf ( "Authkey is set; but state is %v. Ignoring authkey. Re-run with TSNET_FORCE_LOGIN=1 to force use of authkey." , st )
s . logf ( "Authkey is set; but state is %v. Ignoring authkey. Re-run with TSNET_FORCE_LOGIN=1 to force use of authkey." , st )
}
}
go s . printAuthURLLoop ( )
go s . printAuthURLLoop ( )
// Run the localapi handler, to allow fetching LetsEncrypt certs.
// Run the localapi handler, to allow fetching LetsEncrypt certs.
lah := localapi . NewHandler ( lb , l ogf, s . logid )
lah := localapi . NewHandler ( lb , tsL ogf, s . logid )
lah . PermitWrite = true
lah . PermitWrite = true
lah . PermitRead = true
lah . PermitRead = true
@ -621,14 +635,14 @@ func (s *Server) start() (reterr error) {
s . lb . ConfigureWebClient ( s . localClient )
s . lb . ConfigureWebClient ( s . localClient )
go func ( ) {
go func ( ) {
if err := s . localAPIServer . Serve ( lal ) ; err != nil {
if err := s . localAPIServer . Serve ( lal ) ; err != nil {
logf ( "localapi serve error: %v" , err )
s . logf ( "localapi serve error: %v" , err )
}
}
} ( )
} ( )
closePool . add ( s . localAPIListener )
closePool . add ( s . localAPIListener )
return nil
return nil
}
}
func ( s * Server ) startLogger ( closePool * closeOnErrorPool , health * health . Tracker ) error {
func ( s * Server ) startLogger ( closePool * closeOnErrorPool , health * health . Tracker , tsLogf logger . Logf ) error {
if testenv . InTest ( ) {
if testenv . InTest ( ) {
return nil
return nil
}
}
@ -659,10 +673,10 @@ func (s *Server) startLogger(closePool *closeOnErrorPool, health *health.Tracker
Stderr : io . Discard , // log everything to Buffer
Stderr : io . Discard , // log everything to Buffer
Buffer : s . logbuffer ,
Buffer : s . logbuffer ,
CompressLogs : true ,
CompressLogs : true ,
HTTPC : & http . Client { Transport : logpolicy . NewLogtailTransport ( logtail . DefaultHost , s . netMon , health , s. l ogf) } ,
HTTPC : & http . Client { Transport : logpolicy . NewLogtailTransport ( logtail . DefaultHost , s . netMon , health , tsL ogf) } ,
MetricsDelta : clientmetric . EncodeLogTailMetricsDelta ,
MetricsDelta : clientmetric . EncodeLogTailMetricsDelta ,
}
}
s . logtail = logtail . NewLogger ( c , s. l ogf)
s . logtail = logtail . NewLogger ( c , tsL ogf)
closePool . addFunc ( func ( ) { s . logtail . Shutdown ( context . Background ( ) ) } )
closePool . addFunc ( func ( ) { s . logtail . Shutdown ( context . Background ( ) ) } )
return nil
return nil
}
}
@ -683,8 +697,8 @@ func (s *Server) logf(format string, a ...any) {
if s . logtail != nil {
if s . logtail != nil {
s . logtail . Logf ( format , a ... )
s . logtail . Logf ( format , a ... )
}
}
if s . Logf != nil {
if s . User Logf != nil {
s . Logf( format , a ... )
s . User Logf( format , a ... )
return
return
}
}
log . Printf ( format , a ... )
log . Printf ( format , a ... )
@ -697,8 +711,8 @@ func (s *Server) printAuthURLLoop() {
if s . shutdownCtx . Err ( ) != nil {
if s . shutdownCtx . Err ( ) != nil {
return
return
}
}
if st := s . lb . State ( ) ; st != ipn . NeedsLogin {
if st := s . lb . State ( ) ; st != ipn . NeedsLogin && st != ipn . NoState {
s . logf ( " printAuthURLLoop: state is %v; stopping ", st )
s . logf ( " AuthLoop: state is %v; done ", st )
return
return
}
}
st := s . lb . StatusWithoutPeers ( )
st := s . lb . StatusWithoutPeers ( )