TUN-3470: Replace in-house logger calls with zerolog
This commit is contained in:
		
							parent
							
								
									06404bf3e8
								
							
						
					
					
						commit
						870f5fa907
					
				| 
						 | 
				
			
			@ -12,8 +12,9 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/token"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type StartOptions struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +50,7 @@ func (c *StdinoutStream) Write(p []byte) (int, error) {
 | 
			
		|||
// Helper to allow defering the response close with a check that the resp is not nil
 | 
			
		||||
func closeRespBody(resp *http.Response) {
 | 
			
		||||
	if resp != nil {
 | 
			
		||||
		resp.Body.Close()
 | 
			
		||||
		_ = resp.Body.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +104,7 @@ func Serve(remoteConn Connection, listener net.Listener, shutdownC <-chan struct
 | 
			
		|||
// serveConnection handles connections for the Serve() call
 | 
			
		||||
func serveConnection(remoteConn Connection, c net.Conn, options *StartOptions) {
 | 
			
		||||
	defer c.Close()
 | 
			
		||||
	remoteConn.ServeStream(options, c)
 | 
			
		||||
	_ = remoteConn.ServeStream(options, c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsAccessResponse checks the http Response to see if the url location
 | 
			
		||||
| 
						 | 
				
			
			@ -125,13 +126,13 @@ func IsAccessResponse(resp *http.Response) bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// BuildAccessRequest builds an HTTP request with the Access token set
 | 
			
		||||
func BuildAccessRequest(options *StartOptions, logger logger.Service) (*http.Request, error) {
 | 
			
		||||
func BuildAccessRequest(options *StartOptions, log *zerolog.Logger) (*http.Request, error) {
 | 
			
		||||
	req, err := http.NewRequest(http.MethodGet, options.OriginURL, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	token, err := token.FetchTokenWithRedirect(req.URL, logger)
 | 
			
		||||
	token, err := token.FetchTokenWithRedirect(req.URL, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,8 +9,8 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	ws "github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +43,8 @@ func (s *testStreamer) Write(p []byte) (int, error) {
 | 
			
		|||
 | 
			
		||||
func TestStartClient(t *testing.T) {
 | 
			
		||||
	message := "Good morning Austin! Time for another sunny day in the great state of Texas."
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	wsConn := NewWSConnection(logger, false)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
	wsConn := NewWSConnection(&log, false)
 | 
			
		||||
	ts := newTestWebSocketServer()
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -55,10 +55,10 @@ func TestStartClient(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
	err := StartClient(wsConn, buf, options)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	buf.Write([]byte(message))
 | 
			
		||||
	_, _ = buf.Write([]byte(message))
 | 
			
		||||
 | 
			
		||||
	readBuffer := make([]byte, len(message))
 | 
			
		||||
	buf.Read(readBuffer)
 | 
			
		||||
	_, _ = buf.Read(readBuffer)
 | 
			
		||||
	assert.Equal(t, message, string(readBuffer))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,9 +68,9 @@ func TestStartServer(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Error starting listener: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	message := "Good morning Austin! Time for another sunny day in the great state of Texas."
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
	shutdownC := make(chan struct{})
 | 
			
		||||
	wsConn := NewWSConnection(logger, false)
 | 
			
		||||
	wsConn := NewWSConnection(&log, false)
 | 
			
		||||
	ts := newTestWebSocketServer()
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
	options := &StartOptions{
 | 
			
		||||
| 
						 | 
				
			
			@ -86,10 +86,10 @@ func TestStartServer(t *testing.T) {
 | 
			
		|||
	}()
 | 
			
		||||
 | 
			
		||||
	conn, err := net.Dial("tcp", listener.Addr().String())
 | 
			
		||||
	conn.Write([]byte(message))
 | 
			
		||||
	_, _ = conn.Write([]byte(message))
 | 
			
		||||
 | 
			
		||||
	readBuffer := make([]byte, len(message))
 | 
			
		||||
	conn.Read(readBuffer)
 | 
			
		||||
	_, _ = conn.Read(readBuffer)
 | 
			
		||||
	assert.Equal(t, string(readBuffer), message)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,16 +8,17 @@ import (
 | 
			
		|||
	"net/http/httputil"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/token"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/socks"
 | 
			
		||||
	cfwebsocket "github.com/cloudflare/cloudflared/websocket"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Websocket is used to carry data via WS binary frames over the tunnel from client to the origin
 | 
			
		||||
// This implements the functions for glider proxy (sock5) and the carrier interface
 | 
			
		||||
type Websocket struct {
 | 
			
		||||
	logger  logger.Service
 | 
			
		||||
	log     *zerolog.Logger
 | 
			
		||||
	isSocks bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,9 +37,9 @@ func (d *wsdialer) Dial(address string) (io.ReadWriteCloser, *socks.AddrSpec, er
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// NewWSConnection returns a new connection object
 | 
			
		||||
func NewWSConnection(logger logger.Service, isSocks bool) Connection {
 | 
			
		||||
func NewWSConnection(log *zerolog.Logger, isSocks bool) Connection {
 | 
			
		||||
	return &Websocket{
 | 
			
		||||
		logger:  logger,
 | 
			
		||||
		log:     log,
 | 
			
		||||
		isSocks: isSocks,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -46,9 +47,9 @@ func NewWSConnection(logger logger.Service, isSocks bool) Connection {
 | 
			
		|||
// ServeStream will create a Websocket client stream connection to the edge
 | 
			
		||||
// it blocks and writes the raw data from conn over the tunnel
 | 
			
		||||
func (ws *Websocket) ServeStream(options *StartOptions, conn io.ReadWriter) error {
 | 
			
		||||
	wsConn, err := createWebsocketStream(options, ws.logger)
 | 
			
		||||
	wsConn, err := createWebsocketStream(options, ws.log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		ws.logger.Errorf("failed to connect to %s with error: %s", options.OriginURL, err)
 | 
			
		||||
		ws.log.Error().Msgf("failed to connect to %s with error: %s", options.OriginURL, err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer wsConn.Close()
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +59,7 @@ func (ws *Websocket) ServeStream(options *StartOptions, conn io.ReadWriter) erro
 | 
			
		|||
		requestHandler := socks.NewRequestHandler(dialer)
 | 
			
		||||
		socksServer := socks.NewConnectionHandler(requestHandler)
 | 
			
		||||
 | 
			
		||||
		socksServer.Serve(conn)
 | 
			
		||||
		_ = socksServer.Serve(conn)
 | 
			
		||||
	} else {
 | 
			
		||||
		cfwebsocket.Stream(wsConn, conn)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -68,13 +69,13 @@ func (ws *Websocket) ServeStream(options *StartOptions, conn io.ReadWriter) erro
 | 
			
		|||
// StartServer creates a Websocket server to listen for connections.
 | 
			
		||||
// This is used on the origin (tunnel) side to take data from the muxer and send it to the origin
 | 
			
		||||
func (ws *Websocket) StartServer(listener net.Listener, remote string, shutdownC <-chan struct{}) error {
 | 
			
		||||
	return cfwebsocket.StartProxyServer(ws.logger, listener, remote, shutdownC, cfwebsocket.DefaultStreamHandler)
 | 
			
		||||
	return cfwebsocket.StartProxyServer(ws.log, listener, remote, shutdownC, cfwebsocket.DefaultStreamHandler)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// createWebsocketStream will create a WebSocket connection to stream data over
 | 
			
		||||
// It also handles redirects from Access and will present that flow if
 | 
			
		||||
// the token is not present on the request
 | 
			
		||||
func createWebsocketStream(options *StartOptions, logger logger.Service) (*cfwebsocket.Conn, error) {
 | 
			
		||||
func createWebsocketStream(options *StartOptions, log *zerolog.Logger) (*cfwebsocket.Conn, error) {
 | 
			
		||||
	req, err := http.NewRequest(http.MethodGet, options.OriginURL, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -82,13 +83,13 @@ func createWebsocketStream(options *StartOptions, logger logger.Service) (*cfweb
 | 
			
		|||
	req.Header = options.Headers
 | 
			
		||||
 | 
			
		||||
	dump, err := httputil.DumpRequest(req, false)
 | 
			
		||||
	logger.Debugf("Websocket request: %s", string(dump))
 | 
			
		||||
	log.Debug().Msgf("Websocket request: %s", string(dump))
 | 
			
		||||
 | 
			
		||||
	wsConn, resp, err := cfwebsocket.ClientConnect(req, nil)
 | 
			
		||||
	defer closeRespBody(resp)
 | 
			
		||||
 | 
			
		||||
	if err != nil && IsAccessResponse(resp) {
 | 
			
		||||
		wsConn, err = createAccessAuthenticatedStream(options, logger)
 | 
			
		||||
		wsConn, err = createAccessAuthenticatedStream(options, log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -104,8 +105,8 @@ func createWebsocketStream(options *StartOptions, logger logger.Service) (*cfweb
 | 
			
		|||
// this probably means the token in storage is invalid (expired/revoked). If that
 | 
			
		||||
// happens it deletes the token and runs the connection again, so the user can
 | 
			
		||||
// login again and generate a new one.
 | 
			
		||||
func createAccessAuthenticatedStream(options *StartOptions, logger logger.Service) (*websocket.Conn, error) {
 | 
			
		||||
	wsConn, resp, err := createAccessWebSocketStream(options, logger)
 | 
			
		||||
func createAccessAuthenticatedStream(options *StartOptions, log *zerolog.Logger) (*websocket.Conn, error) {
 | 
			
		||||
	wsConn, resp, err := createAccessWebSocketStream(options, log)
 | 
			
		||||
	defer closeRespBody(resp)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return wsConn, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -123,7 +124,7 @@ func createAccessAuthenticatedStream(options *StartOptions, logger logger.Servic
 | 
			
		|||
	if err := token.RemoveTokenIfExists(originReq.URL); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	wsConn, resp, err = createAccessWebSocketStream(options, logger)
 | 
			
		||||
	wsConn, resp, err = createAccessWebSocketStream(options, log)
 | 
			
		||||
	defer closeRespBody(resp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -133,23 +134,23 @@ func createAccessAuthenticatedStream(options *StartOptions, logger logger.Servic
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// createAccessWebSocketStream builds an Access request and makes a connection
 | 
			
		||||
func createAccessWebSocketStream(options *StartOptions, logger logger.Service) (*websocket.Conn, *http.Response, error) {
 | 
			
		||||
	req, err := BuildAccessRequest(options, logger)
 | 
			
		||||
func createAccessWebSocketStream(options *StartOptions, log *zerolog.Logger) (*websocket.Conn, *http.Response, error) {
 | 
			
		||||
	req, err := BuildAccessRequest(options, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dump, err := httputil.DumpRequest(req, false)
 | 
			
		||||
	logger.Debugf("Access Websocket request: %s", string(dump))
 | 
			
		||||
	log.Debug().Msgf("Access Websocket request: %s", string(dump))
 | 
			
		||||
 | 
			
		||||
	conn, resp, err := cfwebsocket.ClientConnect(req, nil)
 | 
			
		||||
 | 
			
		||||
	if resp != nil {
 | 
			
		||||
		r, err := httputil.DumpResponse(resp, true)
 | 
			
		||||
		if r != nil {
 | 
			
		||||
			logger.Debugf("Websocket response: %q", r)
 | 
			
		||||
			log.Debug().Msgf("Websocket response: %q", r)
 | 
			
		||||
		} else if err != nil {
 | 
			
		||||
			logger.Debugf("Websocket response error: %v", err)
 | 
			
		||||
			log.Debug().Msgf("Websocket response error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,17 +5,18 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/carrier"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/validation"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StartForwarder starts a client side websocket forward
 | 
			
		||||
func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger logger.Service) error {
 | 
			
		||||
func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, log *zerolog.Logger) error {
 | 
			
		||||
	validURL, err := validation.ValidateUrl(forwarder.Listener)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error validating origin URL")
 | 
			
		||||
| 
						 | 
				
			
			@ -41,9 +42,9 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
 | 
			
		||||
	wsConn := carrier.NewWSConnection(logger, false)
 | 
			
		||||
	wsConn := carrier.NewWSConnection(log, false)
 | 
			
		||||
 | 
			
		||||
	logger.Infof("Start Websocket listener on: %s", validURL.Host)
 | 
			
		||||
	log.Info().Msgf("Start Websocket listener on: %s", validURL.Host)
 | 
			
		||||
	return carrier.StartForwarder(wsConn, validURL.Host, shutdown, options)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,10 +53,7 @@ func StartForwarder(forwarder config.Forwarder, shutdown <-chan struct{}, logger
 | 
			
		|||
// useful for proxying other protocols (like ssh) over websockets
 | 
			
		||||
// (which you can put Access in front of)
 | 
			
		||||
func ssh(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateSSHLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return cliutil.PrintLoggerSetupError("error setting up logger", err)
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateSSHLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	// get the hostname from the cmdline and error out if its not provided
 | 
			
		||||
	rawHostName := c.String(sshHostnameFlag)
 | 
			
		||||
| 
						 | 
				
			
			@ -85,19 +83,19 @@ func ssh(c *cli.Context) error {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// we could add a cmd line variable for this bool if we want the SOCK5 server to be on the client side
 | 
			
		||||
	wsConn := carrier.NewWSConnection(logger, false)
 | 
			
		||||
	wsConn := carrier.NewWSConnection(log, false)
 | 
			
		||||
 | 
			
		||||
	if c.NArg() > 0 || c.IsSet(sshURLFlag) {
 | 
			
		||||
		forwarder, err := config.ValidateUrl(c, true)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("Error validating origin URL: %s", err)
 | 
			
		||||
			log.Error().Msgf("Error validating origin URL: %s", err)
 | 
			
		||||
			return errors.Wrap(err, "error validating origin URL")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		logger.Infof("Start Websocket listener on: %s", forwarder.Host)
 | 
			
		||||
		log.Info().Msgf("Start Websocket listener on: %s", forwarder.Host)
 | 
			
		||||
		err = carrier.StartForwarder(wsConn, forwarder.Host, shutdownC, options)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("Error on Websocket listener: %s", err)
 | 
			
		||||
			log.Error().Msgf("Error on Websocket listener: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,11 +17,12 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/sshgen"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/validation"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"golang.org/x/net/idna"
 | 
			
		||||
 | 
			
		||||
	"github.com/getsentry/raven-go"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"golang.org/x/net/idna"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -205,20 +206,17 @@ func login(c *cli.Context) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	args := c.Args()
 | 
			
		||||
	rawURL := ensureURLScheme(args.First())
 | 
			
		||||
	appURL, err := url.Parse(rawURL)
 | 
			
		||||
	if args.Len() < 1 || err != nil {
 | 
			
		||||
		logger.Errorf("Please provide the url of the Access application\n")
 | 
			
		||||
		log.Error().Msgf("Please provide the url of the Access application\n")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := verifyTokenAtEdge(appURL, c, logger); err != nil {
 | 
			
		||||
		logger.Errorf("Could not verify token: %s", err)
 | 
			
		||||
	if err := verifyTokenAtEdge(appURL, c, log); err != nil {
 | 
			
		||||
		log.Error().Msgf("Could not verify token: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -250,19 +248,16 @@ func curl(c *cli.Context) error {
 | 
			
		|||
	if err := raven.SetDSN(sentryDSN); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	args := c.Args()
 | 
			
		||||
	if args.Len() < 1 {
 | 
			
		||||
		logger.Error("Please provide the access app and command you wish to run.")
 | 
			
		||||
		log.Error().Msg("Please provide the access app and command you wish to run.")
 | 
			
		||||
		return errors.New("incorrect args")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmdArgs, allowRequest := parseAllowRequest(args.Slice())
 | 
			
		||||
	appURL, err := getAppURL(cmdArgs, logger)
 | 
			
		||||
	appURL, err := getAppURL(cmdArgs, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -270,12 +265,12 @@ func curl(c *cli.Context) error {
 | 
			
		|||
	tok, err := token.GetAppTokenIfExists(appURL)
 | 
			
		||||
	if err != nil || tok == "" {
 | 
			
		||||
		if allowRequest {
 | 
			
		||||
			logger.Info("You don't have an Access token set. Please run access token <access application> to fetch one.")
 | 
			
		||||
			log.Info().Msg("You don't have an Access token set. Please run access token <access application> to fetch one.")
 | 
			
		||||
			return shell.Run("curl", cmdArgs...)
 | 
			
		||||
		}
 | 
			
		||||
		tok, err = token.FetchToken(appURL, logger)
 | 
			
		||||
		tok, err = token.FetchToken(appURL, log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("Failed to refresh token: %s", err)
 | 
			
		||||
			log.Error().Msgf("Failed to refresh token: %s", err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -329,10 +324,7 @@ func sshConfig(c *cli.Context) error {
 | 
			
		|||
 | 
			
		||||
// sshGen generates a short lived certificate for provided hostname
 | 
			
		||||
func sshGen(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	// get the hostname from the cmdline and error out if its not provided
 | 
			
		||||
	rawHostName := c.String(sshHostnameFlag)
 | 
			
		||||
| 
						 | 
				
			
			@ -349,7 +341,7 @@ func sshGen(c *cli.Context) error {
 | 
			
		|||
	// this fetchToken function mutates the appURL param. We should refactor that
 | 
			
		||||
	fetchTokenURL := &url.URL{}
 | 
			
		||||
	*fetchTokenURL = *originURL
 | 
			
		||||
	cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, logger)
 | 
			
		||||
	cfdToken, err := token.FetchTokenWithRedirect(fetchTokenURL, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -362,15 +354,15 @@ func sshGen(c *cli.Context) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// getAppURL will pull the appURL needed for fetching a user's Access token
 | 
			
		||||
func getAppURL(cmdArgs []string, logger logger.Service) (*url.URL, error) {
 | 
			
		||||
func getAppURL(cmdArgs []string, log *zerolog.Logger) (*url.URL, error) {
 | 
			
		||||
	if len(cmdArgs) < 1 {
 | 
			
		||||
		logger.Error("Please provide a valid URL as the first argument to curl.")
 | 
			
		||||
		log.Error().Msg("Please provide a valid URL as the first argument to curl.")
 | 
			
		||||
		return nil, errors.New("not a valid url")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	u, err := processURL(cmdArgs[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Error("Please provide a valid URL as the first argument to curl.")
 | 
			
		||||
		log.Error().Msg("Please provide a valid URL as the first argument to curl.")
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -436,7 +428,7 @@ func isFileThere(candidate string) bool {
 | 
			
		|||
// verifyTokenAtEdge checks for a token on disk, or generates a new one.
 | 
			
		||||
// Then makes a request to to the origin with the token to ensure it is valid.
 | 
			
		||||
// Returns nil if token is valid.
 | 
			
		||||
func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, logger logger.Service) error {
 | 
			
		||||
func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, log *zerolog.Logger) error {
 | 
			
		||||
	headers := buildRequestHeaders(c.StringSlice(sshHeaderFlag))
 | 
			
		||||
	if c.IsSet(sshTokenIDFlag) {
 | 
			
		||||
		headers.Add(h2mux.CFAccessClientIDHeader, c.String(sshTokenIDFlag))
 | 
			
		||||
| 
						 | 
				
			
			@ -446,7 +438,7 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, logger logger.Service) e
 | 
			
		|||
	}
 | 
			
		||||
	options := &carrier.StartOptions{OriginURL: appUrl.String(), Headers: headers}
 | 
			
		||||
 | 
			
		||||
	if valid, err := isTokenValid(options, logger); err != nil {
 | 
			
		||||
	if valid, err := isTokenValid(options, log); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if valid {
 | 
			
		||||
		return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -456,7 +448,7 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, logger logger.Service) e
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if valid, err := isTokenValid(options, logger); err != nil {
 | 
			
		||||
	if valid, err := isTokenValid(options, log); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	} else if !valid {
 | 
			
		||||
		return errors.New("failed to verify token")
 | 
			
		||||
| 
						 | 
				
			
			@ -466,8 +458,8 @@ func verifyTokenAtEdge(appUrl *url.URL, c *cli.Context, logger logger.Service) e
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// isTokenValid makes a request to the origin and returns true if the response was not a 302.
 | 
			
		||||
func isTokenValid(options *carrier.StartOptions, logger logger.Service) (bool, error) {
 | 
			
		||||
	req, err := carrier.BuildAccessRequest(options, logger)
 | 
			
		||||
func isTokenValid(options *carrier.StartOptions, log *zerolog.Logger) (bool, error) {
 | 
			
		||||
	req, err := carrier.BuildAccessRequest(options, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, errors.Wrap(err, "Could not create access request")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,8 @@ package main
 | 
			
		|||
import (
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/access"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ForwardServiceType is used to identify what kind of overwatch service this is
 | 
			
		||||
| 
						 | 
				
			
			@ -15,12 +16,12 @@ const ForwardServiceType = "forward"
 | 
			
		|||
type ForwarderService struct {
 | 
			
		||||
	forwarder config.Forwarder
 | 
			
		||||
	shutdown  chan struct{}
 | 
			
		||||
	logger    logger.Service
 | 
			
		||||
	log       *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewForwardService creates a new forwarder service
 | 
			
		||||
func NewForwardService(f config.Forwarder, logger logger.Service) *ForwarderService {
 | 
			
		||||
	return &ForwarderService{forwarder: f, shutdown: make(chan struct{}, 1), logger: logger}
 | 
			
		||||
func NewForwardService(f config.Forwarder, log *zerolog.Logger) *ForwarderService {
 | 
			
		||||
	return &ForwarderService{forwarder: f, shutdown: make(chan struct{}, 1), log: log}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Name is used to figure out this service is related to the others (normally the addr it binds to)
 | 
			
		||||
| 
						 | 
				
			
			@ -46,5 +47,5 @@ func (s *ForwarderService) Shutdown() {
 | 
			
		|||
 | 
			
		||||
// Run is the run loop that is started by the overwatch service
 | 
			
		||||
func (s *ForwarderService) Run() error {
 | 
			
		||||
	return access.StartForwarder(s.forwarder, s.shutdown, s.logger)
 | 
			
		||||
	return access.StartForwarder(s.forwarder, s.shutdown, s.log)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,9 @@ package main
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunneldns"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ResolverServiceType is used to identify what kind of overwatch service this is
 | 
			
		||||
| 
						 | 
				
			
			@ -15,14 +16,14 @@ const ResolverServiceType = "resolver"
 | 
			
		|||
type ResolverService struct {
 | 
			
		||||
	resolver config.DNSResolver
 | 
			
		||||
	shutdown chan struct{}
 | 
			
		||||
	logger   logger.Service
 | 
			
		||||
	log      *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewResolverService creates a new resolver service
 | 
			
		||||
func NewResolverService(r config.DNSResolver, logger logger.Service) *ResolverService {
 | 
			
		||||
func NewResolverService(r config.DNSResolver, log *zerolog.Logger) *ResolverService {
 | 
			
		||||
	return &ResolverService{resolver: r,
 | 
			
		||||
		shutdown: make(chan struct{}),
 | 
			
		||||
		logger:   logger,
 | 
			
		||||
		log:      log,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -51,7 +52,7 @@ func (s *ResolverService) Shutdown() {
 | 
			
		|||
func (s *ResolverService) Run() error {
 | 
			
		||||
	// create a listener
 | 
			
		||||
	l, err := tunneldns.CreateListener(s.resolver.AddressOrDefault(), s.resolver.PortOrDefault(),
 | 
			
		||||
		s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault(), s.logger)
 | 
			
		||||
		s.resolver.UpstreamsOrDefault(), s.resolver.BootstrapsOrDefault(), s.log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -60,14 +61,14 @@ func (s *ResolverService) Run() error {
 | 
			
		|||
	readySignal := make(chan struct{})
 | 
			
		||||
	err = l.Start(readySignal)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		l.Stop()
 | 
			
		||||
		_ = l.Stop()
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	<-readySignal
 | 
			
		||||
	s.logger.Infof("start resolver on: %s:%d", s.resolver.AddressOrDefault(), s.resolver.PortOrDefault())
 | 
			
		||||
	s.log.Info().Msgf("start resolver on: %s:%d", s.resolver.AddressOrDefault(), s.resolver.PortOrDefault())
 | 
			
		||||
 | 
			
		||||
	// wait for shutdown signal
 | 
			
		||||
	<-s.shutdown
 | 
			
		||||
	s.logger.Infof("shutdown on: %s:%d", s.resolver.AddressOrDefault(), s.resolver.PortOrDefault())
 | 
			
		||||
	s.log.Info().Msgf("shutdown on: %s:%d", s.resolver.AddressOrDefault(), s.resolver.PortOrDefault())
 | 
			
		||||
	return l.Stop()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,8 +2,9 @@ package main
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/overwatch"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AppService is the main service that runs when no command lines flags are passed to cloudflared
 | 
			
		||||
| 
						 | 
				
			
			@ -13,17 +14,17 @@ type AppService struct {
 | 
			
		|||
	serviceManager   overwatch.Manager
 | 
			
		||||
	shutdownC        chan struct{}
 | 
			
		||||
	configUpdateChan chan config.Root
 | 
			
		||||
	logger           logger.Service
 | 
			
		||||
	log              *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewAppService creates a new AppService with needed supporting services
 | 
			
		||||
func NewAppService(configManager config.Manager, serviceManager overwatch.Manager, shutdownC chan struct{}, logger logger.Service) *AppService {
 | 
			
		||||
func NewAppService(configManager config.Manager, serviceManager overwatch.Manager, shutdownC chan struct{}, log *zerolog.Logger) *AppService {
 | 
			
		||||
	return &AppService{
 | 
			
		||||
		configManager:    configManager,
 | 
			
		||||
		serviceManager:   serviceManager,
 | 
			
		||||
		shutdownC:        shutdownC,
 | 
			
		||||
		configUpdateChan: make(chan config.Root),
 | 
			
		||||
		logger:           logger,
 | 
			
		||||
		log:              log,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,14 +68,14 @@ func (s *AppService) handleConfigUpdate(c config.Root) {
 | 
			
		|||
	// handle the client forward listeners
 | 
			
		||||
	activeServices := map[string]struct{}{}
 | 
			
		||||
	for _, f := range c.Forwarders {
 | 
			
		||||
		service := NewForwardService(f, s.logger)
 | 
			
		||||
		service := NewForwardService(f, s.log)
 | 
			
		||||
		s.serviceManager.Add(service)
 | 
			
		||||
		activeServices[service.Name()] = struct{}{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// handle resolver changes
 | 
			
		||||
	if c.Resolver.Enabled {
 | 
			
		||||
		service := NewResolverService(c.Resolver, s.logger)
 | 
			
		||||
		service := NewResolverService(c.Resolver, s.log)
 | 
			
		||||
		s.serviceManager.Add(service)
 | 
			
		||||
		activeServices[service.Name()] = struct{}{}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,8 @@
 | 
			
		|||
package buildinfo
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type BuildInfo struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -22,7 +21,7 @@ func GetBuildInfo(cloudflaredVersion string) *BuildInfo {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BuildInfo) Log(logger logger.Service) {
 | 
			
		||||
	logger.Infof("Version %s", bi.CloudflaredVersion)
 | 
			
		||||
	logger.Infof("GOOS: %s, GOVersion: %s, GoArch: %s", bi.GoOS, bi.GoVersion, bi.GoArch)
 | 
			
		||||
func (bi *BuildInfo) Log(log *zerolog.Logger) {
 | 
			
		||||
	log.Info().Msgf("Version %s", bi.CloudflaredVersion)
 | 
			
		||||
	log.Info().Msgf("GOOS: %s, GOVersion: %s, GoArch: %s", bi.GoOS, bi.GoVersion, bi.GoArch)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,10 +2,6 @@ package cliutil
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +23,6 @@ func UsageError(format string, args ...interface{}) error {
 | 
			
		|||
// Ensures exit with error code if actionFunc returns an error
 | 
			
		||||
func ErrorHandler(actionFunc cli.ActionFunc) cli.ActionFunc {
 | 
			
		||||
	return func(ctx *cli.Context) error {
 | 
			
		||||
		defer logger.SharedWriteManager.Shutdown()
 | 
			
		||||
 | 
			
		||||
		err := actionFunc(ctx)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if _, ok := err.(usageError); ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -41,15 +35,3 @@ func ErrorHandler(actionFunc cli.ActionFunc) cli.ActionFunc {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrintLoggerSetupError returns an error to stdout to notify when a logger can't start
 | 
			
		||||
func PrintLoggerSetupError(msg string, err error) error {
 | 
			
		||||
	l, le := logger.New()
 | 
			
		||||
	if le != nil {
 | 
			
		||||
		log.Printf("%s: %s", msg, err)
 | 
			
		||||
	} else {
 | 
			
		||||
		l.Errorf("%s: %s", msg, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return errors.Wrap(err, msg)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,13 +9,13 @@ import (
 | 
			
		|||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/validation"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +95,7 @@ func FileExists(path string) (bool, error) {
 | 
			
		|||
		}
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	f.Close()
 | 
			
		||||
	_ = f.Close()
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +138,7 @@ func FindOrCreateConfigPath() string {
 | 
			
		|||
		defer file.Close()
 | 
			
		||||
 | 
			
		||||
		logDir := DefaultLogDirectory()
 | 
			
		||||
		os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
 | 
			
		||||
		_ = os.MkdirAll(logDir, os.ModePerm) //try and create it. Doesn't matter if it succeed or not, only byproduct will be no logs
 | 
			
		||||
 | 
			
		||||
		c := Root{
 | 
			
		||||
			LogDirectory: logDir,
 | 
			
		||||
| 
						 | 
				
			
			@ -345,7 +345,7 @@ func GetConfiguration() *Configuration {
 | 
			
		|||
// ReadConfigFile returns InputSourceContext initialized from the configuration file.
 | 
			
		||||
// On repeat calls returns with the same file, returns without reading the file again; however,
 | 
			
		||||
// if value of "config" flag changes, will read the new config file
 | 
			
		||||
func ReadConfigFile(c *cli.Context, log logger.Service) (*configFileSettings, error) {
 | 
			
		||||
func ReadConfigFile(c *cli.Context, log *zerolog.Logger) (*configFileSettings, error) {
 | 
			
		||||
	configFile := c.String("config")
 | 
			
		||||
	if configuration.Source() == configFile || configFile == "" {
 | 
			
		||||
		if configuration.Source() == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +354,7 @@ func ReadConfigFile(c *cli.Context, log logger.Service) (*configFileSettings, er
 | 
			
		|||
		return &configuration, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debugf("Loading configuration from %s", configFile)
 | 
			
		||||
	log.Debug().Msgf("Loading configuration from %s", configFile)
 | 
			
		||||
	file, err := os.Open(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if os.IsNotExist(err) {
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +365,7 @@ func ReadConfigFile(c *cli.Context, log logger.Service) (*configFileSettings, er
 | 
			
		|||
	defer file.Close()
 | 
			
		||||
	if err := yaml.NewDecoder(file).Decode(&configuration); err != nil {
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			log.Errorf("Configuration file %s was empty", configFile)
 | 
			
		||||
			log.Error().Msgf("Configuration file %s was empty", configFile)
 | 
			
		||||
			return &configuration, nil
 | 
			
		||||
		}
 | 
			
		||||
		return nil, errors.Wrap(err, "error parsing YAML in config file at "+configFile)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,10 @@ import (
 | 
			
		|||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/watcher"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,16 +28,16 @@ type FileManager struct {
 | 
			
		|||
	watcher    watcher.Notifier
 | 
			
		||||
	notifier   Notifier
 | 
			
		||||
	configPath string
 | 
			
		||||
	logger     logger.Service
 | 
			
		||||
	ReadConfig func(string, logger.Service) (Root, error)
 | 
			
		||||
	log        *zerolog.Logger
 | 
			
		||||
	ReadConfig func(string, *zerolog.Logger) (Root, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFileManager creates a config manager
 | 
			
		||||
func NewFileManager(watcher watcher.Notifier, configPath string, logger logger.Service) (*FileManager, error) {
 | 
			
		||||
func NewFileManager(watcher watcher.Notifier, configPath string, log *zerolog.Logger) (*FileManager, error) {
 | 
			
		||||
	m := &FileManager{
 | 
			
		||||
		watcher:    watcher,
 | 
			
		||||
		configPath: configPath,
 | 
			
		||||
		logger:     logger,
 | 
			
		||||
		log:        log,
 | 
			
		||||
		ReadConfig: readConfigFromPath,
 | 
			
		||||
	}
 | 
			
		||||
	err := watcher.Add(configPath)
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +61,7 @@ func (m *FileManager) Start(notifier Notifier) error {
 | 
			
		|||
 | 
			
		||||
// GetConfig reads the yaml file from the disk
 | 
			
		||||
func (m *FileManager) GetConfig() (Root, error) {
 | 
			
		||||
	return m.ReadConfig(m.configPath, m.logger)
 | 
			
		||||
	return m.ReadConfig(m.configPath, m.log)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shutdown stops the watcher
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +69,7 @@ func (m *FileManager) Shutdown() {
 | 
			
		|||
	m.watcher.Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readConfigFromPath(configPath string, log logger.Service) (Root, error) {
 | 
			
		||||
func readConfigFromPath(configPath string, log *zerolog.Logger) (Root, error) {
 | 
			
		||||
	if configPath == "" {
 | 
			
		||||
		return Root{}, errors.New("unable to find config file")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +83,7 @@ func readConfigFromPath(configPath string, log logger.Service) (Root, error) {
 | 
			
		|||
	var config Root
 | 
			
		||||
	if err := yaml.NewDecoder(file).Decode(&config); err != nil {
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			log.Errorf("Configuration file %s was empty", configPath)
 | 
			
		||||
			log.Error().Msgf("Configuration file %s was empty", configPath)
 | 
			
		||||
			return Root{}, nil
 | 
			
		||||
		}
 | 
			
		||||
		return Root{}, errors.Wrap(err, "error parsing YAML in config file at "+configPath)
 | 
			
		||||
| 
						 | 
				
			
			@ -98,14 +99,14 @@ func readConfigFromPath(configPath string, log logger.Service) (Root, error) {
 | 
			
		|||
func (m *FileManager) WatcherItemDidChange(filepath string) {
 | 
			
		||||
	config, err := m.GetConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		m.logger.Errorf("Failed to read new config: %s", err)
 | 
			
		||||
		m.log.Error().Msgf("Failed to read new config: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	m.logger.Info("Config file has been updated")
 | 
			
		||||
	m.log.Info().Msg("Config file has been updated")
 | 
			
		||||
	m.notifier.ConfigDidUpdate(config)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WatcherDidError notifies of errors with the file watcher
 | 
			
		||||
func (m *FileManager) WatcherDidError(err error) {
 | 
			
		||||
	m.logger.Errorf("Config watcher encountered an error: %s", err)
 | 
			
		||||
	m.log.Error().Msgf("Config watcher encountered an error: %s", err)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,10 +4,10 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/watcher"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type mockNotifier struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -46,8 +46,8 @@ func TestConfigChanged(t *testing.T) {
 | 
			
		|||
	f, err := os.Create(filePath)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		f.Close()
 | 
			
		||||
		os.Remove(filePath)
 | 
			
		||||
		_ = f.Close()
 | 
			
		||||
		_ = os.Remove(filePath)
 | 
			
		||||
	}()
 | 
			
		||||
	c := &Root{
 | 
			
		||||
		Forwarders: []Forwarder{
 | 
			
		||||
| 
						 | 
				
			
			@ -57,15 +57,15 @@ func TestConfigChanged(t *testing.T) {
 | 
			
		|||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	configRead := func(configPath string, log logger.Service) (Root, error) {
 | 
			
		||||
	configRead := func(configPath string, log *zerolog.Logger) (Root, error) {
 | 
			
		||||
		return *c, nil
 | 
			
		||||
	}
 | 
			
		||||
	wait := make(chan struct{})
 | 
			
		||||
	w := &mockFileWatcher{path: filePath, ready: wait}
 | 
			
		||||
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	service, err := NewFileManager(w, filePath, logger)
 | 
			
		||||
	service, err := NewFileManager(w, filePath, &log)
 | 
			
		||||
	service.ReadConfig = configRead
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,13 +7,13 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	cli "github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		|||
		Name:  "service",
 | 
			
		||||
		Usage: "Manages the Argo Tunnel system service",
 | 
			
		||||
		Subcommands: []*cli.Command{
 | 
			
		||||
			&cli.Command{
 | 
			
		||||
			{
 | 
			
		||||
				Name:   "install",
 | 
			
		||||
				Usage:  "Install Argo Tunnel as a system service",
 | 
			
		||||
				Action: cliutil.ErrorHandler(installLinuxService),
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		|||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			&cli.Command{
 | 
			
		||||
			{
 | 
			
		||||
				Name:   "uninstall",
 | 
			
		||||
				Usage:  "Uninstall the Argo Tunnel service",
 | 
			
		||||
				Action: cliutil.ErrorHandler(uninstallLinuxService),
 | 
			
		||||
| 
						 | 
				
			
			@ -190,7 +190,7 @@ func isSystemd() bool {
 | 
			
		|||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, logger logger.Service) error {
 | 
			
		||||
func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, log *zerolog.Logger) error {
 | 
			
		||||
	srcCredentialPath := filepath.Join(userConfigDir, userCredentialFile)
 | 
			
		||||
	destCredentialPath := filepath.Join(serviceConfigDir, serviceCredentialFile)
 | 
			
		||||
	if srcCredentialPath != destCredentialPath {
 | 
			
		||||
| 
						 | 
				
			
			@ -205,17 +205,14 @@ func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile str
 | 
			
		|||
		if err := copyConfig(srcConfigPath, destConfigPath); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		logger.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
 | 
			
		||||
		log.Info().Msgf("Copied %s to %s", srcConfigPath, destConfigPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func installLinuxService(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	etPath, err := os.Executable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -232,8 +229,8 @@ func installLinuxService(c *cli.Context) error {
 | 
			
		|||
		userConfigDir := filepath.Dir(c.String("config"))
 | 
			
		||||
		userConfigFile := filepath.Base(c.String("config"))
 | 
			
		||||
		userCredentialFile := config.DefaultCredentialFile
 | 
			
		||||
		if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, logger); err != nil {
 | 
			
		||||
			logger.Errorf("Failed to copy user configuration: %s. Before running the service, ensure that %s contains two files, %s and %s", err,
 | 
			
		||||
		if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, log); err != nil {
 | 
			
		||||
			log.Error().Msgf("Failed to copy user configuration: %s. Before running the service, ensure that %s contains two files, %s and %s", err,
 | 
			
		||||
				serviceConfigDir, serviceCredentialFile, serviceConfigFile)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +238,7 @@ func installLinuxService(c *cli.Context) error {
 | 
			
		|||
			"--origincert", serviceConfigDir + "/" + serviceCredentialFile,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		src, err := config.ReadConfigFile(c, logger)
 | 
			
		||||
		src, err := config.ReadConfigFile(c, log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -274,42 +271,42 @@ credentials-file: CREDENTIALS-FILE
 | 
			
		|||
 | 
			
		||||
	switch {
 | 
			
		||||
	case isSystemd():
 | 
			
		||||
		logger.Infof("Using Systemd")
 | 
			
		||||
		return installSystemd(&templateArgs, logger)
 | 
			
		||||
		log.Info().Msgf("Using Systemd")
 | 
			
		||||
		return installSystemd(&templateArgs, log)
 | 
			
		||||
	default:
 | 
			
		||||
		logger.Infof("Using SysV")
 | 
			
		||||
		return installSysv(&templateArgs, logger)
 | 
			
		||||
		log.Info().Msgf("Using SysV")
 | 
			
		||||
		return installSysv(&templateArgs, log)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func installSystemd(templateArgs *ServiceTemplateArgs, logger logger.Service) error {
 | 
			
		||||
func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error {
 | 
			
		||||
	for _, serviceTemplate := range systemdTemplates {
 | 
			
		||||
		err := serviceTemplate.Generate(templateArgs)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("error generating service template: %s", err)
 | 
			
		||||
			log.Error().Msgf("error generating service template: %s", err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := runCommand("systemctl", "enable", "cloudflared.service"); err != nil {
 | 
			
		||||
		logger.Errorf("systemctl enable cloudflared.service error: %s", err)
 | 
			
		||||
		log.Error().Msgf("systemctl enable cloudflared.service error: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil {
 | 
			
		||||
		logger.Errorf("systemctl start cloudflared-update.timer error: %s", err)
 | 
			
		||||
		log.Error().Msgf("systemctl start cloudflared-update.timer error: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("systemctl daemon-reload")
 | 
			
		||||
	log.Info().Msgf("systemctl daemon-reload")
 | 
			
		||||
	return runCommand("systemctl", "daemon-reload")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func installSysv(templateArgs *ServiceTemplateArgs, logger logger.Service) error {
 | 
			
		||||
func installSysv(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error {
 | 
			
		||||
	confPath, err := sysvTemplate.ResolvePath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error resolving system path: %s", err)
 | 
			
		||||
		log.Error().Msgf("error resolving system path: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := sysvTemplate.Generate(templateArgs); err != nil {
 | 
			
		||||
		logger.Errorf("error generating system template: %s", err)
 | 
			
		||||
		log.Error().Msgf("error generating system template: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, i := range [...]string{"2", "3", "4", "5"} {
 | 
			
		||||
| 
						 | 
				
			
			@ -326,43 +323,40 @@ func installSysv(templateArgs *ServiceTemplateArgs, logger logger.Service) error
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func uninstallLinuxService(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case isSystemd():
 | 
			
		||||
		logger.Infof("Using Systemd")
 | 
			
		||||
		return uninstallSystemd(logger)
 | 
			
		||||
		log.Info().Msgf("Using Systemd")
 | 
			
		||||
		return uninstallSystemd(log)
 | 
			
		||||
	default:
 | 
			
		||||
		logger.Infof("Using SysV")
 | 
			
		||||
		return uninstallSysv(logger)
 | 
			
		||||
		log.Info().Msgf("Using SysV")
 | 
			
		||||
		return uninstallSysv(log)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func uninstallSystemd(logger logger.Service) error {
 | 
			
		||||
func uninstallSystemd(log *zerolog.Logger) error {
 | 
			
		||||
	if err := runCommand("systemctl", "disable", "cloudflared.service"); err != nil {
 | 
			
		||||
		logger.Errorf("systemctl disable cloudflared.service error: %s", err)
 | 
			
		||||
		log.Error().Msgf("systemctl disable cloudflared.service error: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil {
 | 
			
		||||
		logger.Errorf("systemctl stop cloudflared-update.timer error: %s", err)
 | 
			
		||||
		log.Error().Msgf("systemctl stop cloudflared-update.timer error: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, serviceTemplate := range systemdTemplates {
 | 
			
		||||
		if err := serviceTemplate.Remove(); err != nil {
 | 
			
		||||
			logger.Errorf("error removing service template: %s", err)
 | 
			
		||||
			log.Error().Msgf("error removing service template: %s", err)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("Successfully uninstall cloudflared service")
 | 
			
		||||
	log.Info().Msgf("Successfully uninstall cloudflared service")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func uninstallSysv(logger logger.Service) error {
 | 
			
		||||
func uninstallSysv(log *zerolog.Logger) error {
 | 
			
		||||
	if err := sysvTemplate.Remove(); err != nil {
 | 
			
		||||
		logger.Errorf("error removing service template: %s", err)
 | 
			
		||||
		log.Error().Msgf("error removing service template: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, i := range [...]string{"2", "3", "4", "5"} {
 | 
			
		||||
| 
						 | 
				
			
			@ -375,6 +369,6 @@ func uninstallSysv(logger logger.Service) error {
 | 
			
		|||
			continue
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("Successfully uninstall cloudflared service")
 | 
			
		||||
	log.Info().Msgf("Successfully uninstall cloudflared service")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		|||
			},
 | 
			
		||||
		},
 | 
			
		||||
	})
 | 
			
		||||
	app.Run(os.Args)
 | 
			
		||||
	_ = app.Run(os.Args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemplate {
 | 
			
		||||
| 
						 | 
				
			
			@ -107,71 +107,61 @@ func stderrPath() (string, error) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func installLaunchd(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	if isRootUser() {
 | 
			
		||||
		logger.Infof("Installing Argo Tunnel client as a system launch daemon. " +
 | 
			
		||||
		log.Info().Msgf("Installing Argo Tunnel client as a system launch daemon. " +
 | 
			
		||||
			"Argo Tunnel client will run at boot")
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.Infof("Installing Argo Tunnel client as an user launch agent. " +
 | 
			
		||||
		log.Info().Msgf("Installing Argo Tunnel client as an user launch agent. " +
 | 
			
		||||
			"Note that Argo Tunnel client will only run when the user is logged in. " +
 | 
			
		||||
			"If you want to run Argo Tunnel client at boot, install with root permission. " +
 | 
			
		||||
			"For more information, visit https://developers.cloudflare.com/argo-tunnel/reference/service/")
 | 
			
		||||
	}
 | 
			
		||||
	etPath, err := os.Executable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Error determining executable path: %s", err)
 | 
			
		||||
		log.Error().Msgf("Error determining executable path: %s", err)
 | 
			
		||||
		return fmt.Errorf("Error determining executable path: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	installPath, err := installPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Error determining install path: %s", err)
 | 
			
		||||
		log.Error().Msgf("Error determining install path: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "Error determining install path")
 | 
			
		||||
	}
 | 
			
		||||
	stdoutPath, err := stdoutPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error determining stdout path: %s", err)
 | 
			
		||||
		log.Error().Msgf("error determining stdout path: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "error determining stdout path")
 | 
			
		||||
	}
 | 
			
		||||
	stderrPath, err := stderrPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error determining stderr path: %s", err)
 | 
			
		||||
		log.Error().Msgf("error determining stderr path: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "error determining stderr path")
 | 
			
		||||
	}
 | 
			
		||||
	launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error creating launchd template: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "error creating launchd template")
 | 
			
		||||
	}
 | 
			
		||||
	templateArgs := ServiceTemplateArgs{Path: etPath}
 | 
			
		||||
	err = launchdTemplate.Generate(&templateArgs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error generating launchd template: %s", err)
 | 
			
		||||
		log.Error().Msgf("error generating launchd template: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	plistPath, err := launchdTemplate.ResolvePath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error resolving launchd template path: %s", err)
 | 
			
		||||
		log.Error().Msgf("error resolving launchd template path: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Infof("Outputs are logged to %s and %s", stderrPath, stdoutPath)
 | 
			
		||||
	log.Info().Msgf("Outputs are logged to %s and %s", stderrPath, stdoutPath)
 | 
			
		||||
	return runCommand("launchctl", "load", plistPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func uninstallLaunchd(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	if isRootUser() {
 | 
			
		||||
		logger.Infof("Uninstalling Argo Tunnel as a system launch daemon")
 | 
			
		||||
		log.Info().Msgf("Uninstalling Argo Tunnel as a system launch daemon")
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.Infof("Uninstalling Argo Tunnel as an user launch agent")
 | 
			
		||||
		log.Info().Msgf("Uninstalling Argo Tunnel as an user launch agent")
 | 
			
		||||
	}
 | 
			
		||||
	installPath, err := installPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -186,20 +176,17 @@ func uninstallLaunchd(c *cli.Context) error {
 | 
			
		|||
		return errors.Wrap(err, "error determining stderr path")
 | 
			
		||||
	}
 | 
			
		||||
	launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error creating launchd template")
 | 
			
		||||
	}
 | 
			
		||||
	plistPath, err := launchdTemplate.ResolvePath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error resolving launchd template path: %s", err)
 | 
			
		||||
		log.Error().Msgf("error resolving launchd template path: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = runCommand("launchctl", "unload", plistPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("error unloading: %s", err)
 | 
			
		||||
		log.Error().Msgf("error unloading: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger.Infof("Outputs are logged to %s and %s", stderrPath, stdoutPath)
 | 
			
		||||
	log.Info().Msgf("Outputs are logged to %s and %s", stderrPath, stdoutPath)
 | 
			
		||||
	return launchdTemplate.Remove()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,16 +10,16 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/updater"
 | 
			
		||||
	log "github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/metrics"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/overwatch"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunneldns"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/watcher"
 | 
			
		||||
 | 
			
		||||
	"github.com/getsentry/raven-go"
 | 
			
		||||
	"github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -184,38 +184,33 @@ func captureError(err error) {
 | 
			
		|||
 | 
			
		||||
// cloudflared was started without any flags
 | 
			
		||||
func handleServiceMode(c *cli.Context, shutdownC chan struct{}) error {
 | 
			
		||||
	defer log.SharedWriteManager.Shutdown()
 | 
			
		||||
 | 
			
		||||
	logger, err := log.CreateLoggerFromContext(c, log.DisableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return cliutil.PrintLoggerSetupError("error setting up logger", err)
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.DisableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	// start the main run loop that reads from the config file
 | 
			
		||||
	f, err := watcher.NewFile()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot load config file: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot load config file: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	configPath := config.FindOrCreateConfigPath()
 | 
			
		||||
	configManager, err := config.NewFileManager(f, configPath, logger)
 | 
			
		||||
	configManager, err := config.NewFileManager(f, configPath, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot setup config file for monitoring: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot setup config file for monitoring: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("monitoring config file at: %s", configPath)
 | 
			
		||||
	log.Info().Msgf("monitoring config file at: %s", configPath)
 | 
			
		||||
 | 
			
		||||
	serviceCallback := func(t string, name string, err error) {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("%s service: %s encountered an error: %s", t, name, err)
 | 
			
		||||
			log.Error().Msgf("%s service: %s encountered an error: %s", t, name, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	serviceManager := overwatch.NewAppManager(serviceCallback)
 | 
			
		||||
 | 
			
		||||
	appService := NewAppService(configManager, serviceManager, shutdownC, logger)
 | 
			
		||||
	appService := NewAppService(configManager, serviceManager, shutdownC, log)
 | 
			
		||||
	if err := appService.Run(); err != nil {
 | 
			
		||||
		logger.Errorf("Failed to start app service: %s", err)
 | 
			
		||||
		log.Error().Msgf("Failed to start app service: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,10 +16,11 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/path"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/transfer"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/origin"
 | 
			
		||||
 | 
			
		||||
	"github.com/coreos/go-oidc/jose"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,7 @@ func newLock(path string) *lock {
 | 
			
		|||
func (l *lock) Acquire() error {
 | 
			
		||||
	// Intercept SIGINT and SIGTERM to release lock before exiting
 | 
			
		||||
	l.sigHandler.register(func() {
 | 
			
		||||
		l.deleteLockFile()
 | 
			
		||||
		_ = l.deleteLockFile()
 | 
			
		||||
		os.Exit(0)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -143,18 +144,18 @@ func isTokenLocked(lockFilePath string) bool {
 | 
			
		|||
 | 
			
		||||
// FetchTokenWithRedirect will either load a stored token or generate a new one
 | 
			
		||||
// it appends the full url as the redirect URL to the access cli request if opening the browser
 | 
			
		||||
func FetchTokenWithRedirect(appURL *url.URL, logger logger.Service) (string, error) {
 | 
			
		||||
	return getToken(appURL, false, logger)
 | 
			
		||||
func FetchTokenWithRedirect(appURL *url.URL, log *zerolog.Logger) (string, error) {
 | 
			
		||||
	return getToken(appURL, false, log)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FetchToken will either load a stored token or generate a new one
 | 
			
		||||
// it appends the host of the appURL as the redirect URL to the access cli request if opening the browser
 | 
			
		||||
func FetchToken(appURL *url.URL, logger logger.Service) (string, error) {
 | 
			
		||||
	return getToken(appURL, true, logger)
 | 
			
		||||
func FetchToken(appURL *url.URL, log *zerolog.Logger) (string, error) {
 | 
			
		||||
	return getToken(appURL, true, log)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getToken will either load a stored token or generate a new one
 | 
			
		||||
func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string, error) {
 | 
			
		||||
func getToken(appURL *url.URL, useHostOnly bool, log *zerolog.Logger) (string, error) {
 | 
			
		||||
	if token, err := GetAppTokenIfExists(appURL); token != "" && err == nil {
 | 
			
		||||
		return token, nil
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +180,7 @@ func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string,
 | 
			
		|||
	var orgTokenPath string
 | 
			
		||||
	// Get auth domain to format into org token file path
 | 
			
		||||
	if authDomain, err := getAuthDomain(appURL); err != nil {
 | 
			
		||||
		logger.Errorf("failed to get auth domain: %s", err)
 | 
			
		||||
		log.Error().Msgf("failed to get auth domain: %s", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		orgToken, err := GetOrgTokenIfExists(authDomain)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -198,7 +199,7 @@ func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string,
 | 
			
		|||
		}
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			if appToken, err := exchangeOrgToken(appURL, orgToken); err != nil {
 | 
			
		||||
				logger.Debugf("failed to exchange org token for app token: %s", err)
 | 
			
		||||
				log.Debug().Msgf("failed to exchange org token for app token: %s", err)
 | 
			
		||||
			} else {
 | 
			
		||||
				if err := ioutil.WriteFile(appTokenPath, []byte(appToken), 0600); err != nil {
 | 
			
		||||
					return "", errors.Wrap(err, "failed to write app token to disk")
 | 
			
		||||
| 
						 | 
				
			
			@ -207,19 +208,19 @@ func getToken(appURL *url.URL, useHostOnly bool, logger logger.Service) (string,
 | 
			
		|||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return getTokensFromEdge(appURL, appTokenPath, orgTokenPath, useHostOnly, logger)
 | 
			
		||||
	return getTokensFromEdge(appURL, appTokenPath, orgTokenPath, useHostOnly, log)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getTokensFromEdge will attempt to use the transfer service to retrieve an app and org token, save them to disk,
 | 
			
		||||
// and return the app token.
 | 
			
		||||
func getTokensFromEdge(appURL *url.URL, appTokenPath, orgTokenPath string, useHostOnly bool, logger logger.Service) (string, error) {
 | 
			
		||||
func getTokensFromEdge(appURL *url.URL, appTokenPath, orgTokenPath string, useHostOnly bool, log *zerolog.Logger) (string, error) {
 | 
			
		||||
	// If no org token exists or if it couldnt be exchanged for an app token, then run the transfer service flow.
 | 
			
		||||
 | 
			
		||||
	// this weird parameter is the resource name (token) and the key/value
 | 
			
		||||
	// we want to send to the transfer service. the key is token and the value
 | 
			
		||||
	// is blank (basically just the id generated in the transfer service)
 | 
			
		||||
	resourceData, err := transfer.Run(appURL, keyName, keyName, "", true, useHostOnly, logger)
 | 
			
		||||
	resourceData, err := transfer.Run(appURL, keyName, keyName, "", true, useHostOnly, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", errors.Wrap(err, "failed to run transfer service")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/encrypter"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/shell"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +27,7 @@ const (
 | 
			
		|||
// The "dance" we refer to is building a HTTP request, opening that in a browser waiting for
 | 
			
		||||
// the user to complete an action, while it long polls in the background waiting for an
 | 
			
		||||
// action to be completed to download the resource.
 | 
			
		||||
func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, logger logger.Service) ([]byte, error) {
 | 
			
		||||
func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bool, useHostOnly bool, log *zerolog.Logger) ([]byte, error) {
 | 
			
		||||
	encrypterClient, err := encrypter.New("cloudflared_priv.pem", "cloudflared_pub.pem")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bo
 | 
			
		|||
	var resourceData []byte
 | 
			
		||||
 | 
			
		||||
	if shouldEncrypt {
 | 
			
		||||
		buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), logger)
 | 
			
		||||
		buf, key, err := transferRequest(baseStoreURL+"transfer/"+encrypterClient.PublicKey(), log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +64,7 @@ func Run(transferURL *url.URL, resourceName, key, value string, shouldEncrypt bo
 | 
			
		|||
 | 
			
		||||
		resourceData = decrypted
 | 
			
		||||
	} else {
 | 
			
		||||
		buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), logger)
 | 
			
		||||
		buf, _, err := transferRequest(baseStoreURL+encrypterClient.PublicKey(), log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -96,17 +96,17 @@ func buildRequestURL(baseURL *url.URL, key, value string, cli, useHostOnly bool)
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// transferRequest downloads the requested resource from the request URL
 | 
			
		||||
func transferRequest(requestURL string, logger logger.Service) ([]byte, string, error) {
 | 
			
		||||
func transferRequest(requestURL string, log *zerolog.Logger) ([]byte, string, error) {
 | 
			
		||||
	client := &http.Client{Timeout: clientTimeout}
 | 
			
		||||
	const pollAttempts = 10
 | 
			
		||||
	// we do "long polling" on the endpoint to get the resource.
 | 
			
		||||
	for i := 0; i < pollAttempts; i++ {
 | 
			
		||||
		buf, key, err := poll(client, requestURL, logger)
 | 
			
		||||
		buf, key, err := poll(client, requestURL, log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, "", err
 | 
			
		||||
		} else if len(buf) > 0 {
 | 
			
		||||
			if err := putSuccess(client, requestURL); err != nil {
 | 
			
		||||
				logger.Errorf("Failed to update resource success: %s", err)
 | 
			
		||||
				log.Error().Msgf("Failed to update resource success: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
			return buf, key, nil
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ func transferRequest(requestURL string, logger logger.Service) ([]byte, string,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// poll the endpoint for the request resource, waiting for the user interaction
 | 
			
		||||
func poll(client *http.Client, requestURL string, logger logger.Service) ([]byte, string, error) {
 | 
			
		||||
func poll(client *http.Client, requestURL string, log *zerolog.Logger) ([]byte, string, error) {
 | 
			
		||||
	resp, err := client.Get(requestURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", err
 | 
			
		||||
| 
						 | 
				
			
			@ -128,7 +128,7 @@ func poll(client *http.Client, requestURL string, logger logger.Service) ([]byte
 | 
			
		|||
		return nil, "", fmt.Errorf("error on request %d", resp.StatusCode)
 | 
			
		||||
	}
 | 
			
		||||
	if resp.StatusCode != 200 {
 | 
			
		||||
		logger.Info("Waiting for login...")
 | 
			
		||||
		log.Info().Msg("Waiting for login...")
 | 
			
		||||
		return nil, "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ import (
 | 
			
		|||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"github.com/urfave/cli/v2/altsrc"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +43,6 @@ import (
 | 
			
		|||
const (
 | 
			
		||||
	sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b:3e8827f6f9f740738eb11138f7bebb68@sentry.io/189878"
 | 
			
		||||
 | 
			
		||||
	sshLogFileDirectory = "/usr/local/var/log/cloudflared/"
 | 
			
		||||
 | 
			
		||||
	// sshPortFlag is the port on localhost the cloudflared ssh server will run on
 | 
			
		||||
	sshPortFlag = "local-ssh-port"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,14 +173,14 @@ func runAdhocNamedTunnel(sc *subcommandContext, name string) error {
 | 
			
		|||
			return errors.Wrap(err, "failed to create tunnel")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		sc.logger.Infof("Tunnel already created with ID %s", tunnel.ID)
 | 
			
		||||
		sc.log.Info().Msgf("Tunnel already created with ID %s", tunnel.ID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if r, ok := routeFromFlag(sc.c); ok {
 | 
			
		||||
		if res, err := sc.route(tunnel.ID, r); err != nil {
 | 
			
		||||
			sc.logger.Errorf("failed to create route, please create it manually. err: %v.", err)
 | 
			
		||||
			sc.log.Error().Msgf("failed to create route, please create it manually. err: %v.", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			sc.logger.Infof(res.SuccessSummary())
 | 
			
		||||
			sc.log.Info().Msgf(res.SuccessSummary())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -194,7 +193,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name string) error {
 | 
			
		|||
 | 
			
		||||
// runClassicTunnel creates a "classic" non-named tunnel
 | 
			
		||||
func runClassicTunnel(sc *subcommandContext) error {
 | 
			
		||||
	return StartServer(sc.c, version, shutdownC, graceShutdownC, nil, sc.logger, sc.isUIEnabled)
 | 
			
		||||
	return StartServer(sc.c, version, shutdownC, graceShutdownC, nil, sc.log, sc.isUIEnabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func routeFromFlag(c *cli.Context) (tunnelstore.Route, bool) {
 | 
			
		||||
| 
						 | 
				
			
			@ -213,7 +212,7 @@ func StartServer(
 | 
			
		|||
	shutdownC,
 | 
			
		||||
	graceShutdownC chan struct{},
 | 
			
		||||
	namedTunnel *connection.NamedTunnelConfig,
 | 
			
		||||
	generalLogger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
	isUIEnabled bool,
 | 
			
		||||
) error {
 | 
			
		||||
	_ = raven.SetDSN(sentryDSN)
 | 
			
		||||
| 
						 | 
				
			
			@ -224,45 +223,45 @@ func StartServer(
 | 
			
		|||
	dnsReadySignal := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	if config.GetConfiguration().Source() == "" {
 | 
			
		||||
		generalLogger.Infof(config.ErrNoConfigFile.Error())
 | 
			
		||||
		log.Info().Msg(config.ErrNoConfigFile.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.IsSet("trace-output") {
 | 
			
		||||
		tmpTraceFile, err := ioutil.TempFile("", "trace")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			generalLogger.Errorf("Failed to create new temporary file to save trace output: %s", err)
 | 
			
		||||
			log.Error().Msgf("Failed to create new temporary file to save trace output: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		defer func() {
 | 
			
		||||
			if err := tmpTraceFile.Close(); err != nil {
 | 
			
		||||
				generalLogger.Errorf("Failed to close trace output file %s with error: %s", tmpTraceFile.Name(), err)
 | 
			
		||||
				log.Error().Msgf("Failed to close trace output file %s with error: %s", tmpTraceFile.Name(), err)
 | 
			
		||||
			}
 | 
			
		||||
			if err := os.Rename(tmpTraceFile.Name(), c.String("trace-output")); err != nil {
 | 
			
		||||
				generalLogger.Errorf("Failed to rename temporary trace output file %s to %s with error: %s", tmpTraceFile.Name(), c.String("trace-output"), err)
 | 
			
		||||
				log.Error().Msgf("Failed to rename temporary trace output file %s to %s with error: %s", tmpTraceFile.Name(), c.String("trace-output"), err)
 | 
			
		||||
			} else {
 | 
			
		||||
				err := os.Remove(tmpTraceFile.Name())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					generalLogger.Errorf("Failed to remove the temporary trace file %s with error: %s", tmpTraceFile.Name(), err)
 | 
			
		||||
					log.Error().Msgf("Failed to remove the temporary trace file %s with error: %s", tmpTraceFile.Name(), err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		if err := trace.Start(tmpTraceFile); err != nil {
 | 
			
		||||
			generalLogger.Errorf("Failed to start trace: %s", err)
 | 
			
		||||
			log.Error().Msgf("Failed to start trace: %s", err)
 | 
			
		||||
			return errors.Wrap(err, "Error starting tracing")
 | 
			
		||||
		}
 | 
			
		||||
		defer trace.Stop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buildInfo := buildinfo.GetBuildInfo(version)
 | 
			
		||||
	buildInfo.Log(generalLogger)
 | 
			
		||||
	logClientOptions(c, generalLogger)
 | 
			
		||||
	buildInfo.Log(log)
 | 
			
		||||
	logClientOptions(c, log)
 | 
			
		||||
 | 
			
		||||
	if c.IsSet("proxy-dns") {
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			errC <- runDNSProxyServer(c, dnsReadySignal, shutdownC, generalLogger)
 | 
			
		||||
			errC <- runDNSProxyServer(c, dnsReadySignal, shutdownC, log)
 | 
			
		||||
		}()
 | 
			
		||||
	} else {
 | 
			
		||||
		close(dnsReadySignal)
 | 
			
		||||
| 
						 | 
				
			
			@ -273,12 +272,12 @@ func StartServer(
 | 
			
		|||
 | 
			
		||||
	go notifySystemd(connectedSignal)
 | 
			
		||||
	if c.IsSet("pidfile") {
 | 
			
		||||
		go writePidFile(connectedSignal, c.String("pidfile"), generalLogger)
 | 
			
		||||
		go writePidFile(connectedSignal, c.String("pidfile"), log)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cloudflaredID, err := uuid.NewRandom()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		generalLogger.Errorf("Cannot generate cloudflared ID: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot generate cloudflared ID: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -289,12 +288,12 @@ func StartServer(
 | 
			
		|||
	}()
 | 
			
		||||
 | 
			
		||||
	// update needs to be after DNS proxy is up to resolve equinox server address
 | 
			
		||||
	if updater.IsAutoupdateEnabled(c, generalLogger) {
 | 
			
		||||
		generalLogger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
 | 
			
		||||
	if updater.IsAutoupdateEnabled(c, log) {
 | 
			
		||||
		log.Info().Msgf("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
 | 
			
		||||
		wg.Add(1)
 | 
			
		||||
		go func() {
 | 
			
		||||
			defer wg.Done()
 | 
			
		||||
			autoupdater := updater.NewAutoUpdater(c.Duration("autoupdate-freq"), &listeners, generalLogger)
 | 
			
		||||
			autoupdater := updater.NewAutoUpdater(c.Duration("autoupdate-freq"), &listeners, log)
 | 
			
		||||
			errC <- autoupdater.Run(ctx)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -303,21 +302,18 @@ func StartServer(
 | 
			
		|||
	if dnsProxyStandAlone(c) {
 | 
			
		||||
		connectedSignal.Notify()
 | 
			
		||||
		// no grace period, handle SIGINT/SIGTERM immediately
 | 
			
		||||
		return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, 0, generalLogger)
 | 
			
		||||
		return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, 0, log)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	url := c.String("url")
 | 
			
		||||
	hostname := c.String("hostname")
 | 
			
		||||
	if url == hostname && url != "" && hostname != "" {
 | 
			
		||||
		errText := "hostname and url shouldn't match. See --help for more information"
 | 
			
		||||
		generalLogger.Error(errText)
 | 
			
		||||
		log.Error().Msg(errText)
 | 
			
		||||
		return fmt.Errorf(errText)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	transportLogger, err := logger.CreateTransportLoggerFromContext(c, isUIEnabled)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up transport logger")
 | 
			
		||||
	}
 | 
			
		||||
	transportLog := logger.CreateTransportLoggerFromContext(c, isUIEnabled)
 | 
			
		||||
 | 
			
		||||
	readinessCh := make(chan connection.Event, 16)
 | 
			
		||||
	uiCh := make(chan connection.Event, 16)
 | 
			
		||||
| 
						 | 
				
			
			@ -325,30 +321,30 @@ func StartServer(
 | 
			
		|||
		readinessCh,
 | 
			
		||||
		uiCh,
 | 
			
		||||
	}
 | 
			
		||||
	tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, generalLogger, transportLogger, namedTunnel, isUIEnabled, eventChannels)
 | 
			
		||||
	tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, log, transportLog, namedTunnel, isUIEnabled, eventChannels)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		generalLogger.Errorf("Couldn't start tunnel: %v", err)
 | 
			
		||||
		log.Error().Msgf("Couldn't start tunnel: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	metricsListener, err := listeners.Listen("tcp", c.String("metrics"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		generalLogger.Errorf("Error opening metrics server listener: %s", err)
 | 
			
		||||
		log.Error().Msgf("Error opening metrics server listener: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "Error opening metrics server listener")
 | 
			
		||||
	}
 | 
			
		||||
	defer metricsListener.Close()
 | 
			
		||||
	wg.Add(1)
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer wg.Done()
 | 
			
		||||
		errC <- metrics.ServeMetrics(metricsListener, shutdownC, readinessCh, generalLogger)
 | 
			
		||||
		errC <- metrics.ServeMetrics(metricsListener, shutdownC, readinessCh, log)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	ingressRules.StartOrigins(&wg, generalLogger, shutdownC, errC)
 | 
			
		||||
	ingressRules.StartOrigins(&wg, log, shutdownC, errC)
 | 
			
		||||
 | 
			
		||||
	reconnectCh := make(chan origin.ReconnectSignal, 1)
 | 
			
		||||
	if c.IsSet("stdin-control") {
 | 
			
		||||
		generalLogger.Info("Enabling control through stdin")
 | 
			
		||||
		go stdinControl(reconnectCh, generalLogger)
 | 
			
		||||
		log.Info().Msg("Enabling control through stdin")
 | 
			
		||||
		go stdinControl(reconnectCh, log)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Add(1)
 | 
			
		||||
| 
						 | 
				
			
			@ -365,31 +361,15 @@ func StartServer(
 | 
			
		|||
			&ingressRules,
 | 
			
		||||
			tunnelConfig.HAConnections,
 | 
			
		||||
		)
 | 
			
		||||
		logLevels, err := logger.ParseLevelString(c.String("loglevel"))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		tunnelInfo.LaunchUI(ctx, generalLogger, transportLogger, logLevels, uiCh)
 | 
			
		||||
		tunnelInfo.LaunchUI(ctx, log, transportLog, uiCh)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"), generalLogger)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forceSetFlag attempts to set the given flag value in the closest context that has it defined
 | 
			
		||||
func forceSetFlag(c *cli.Context, name, value string) {
 | 
			
		||||
	for _, ctx := range c.Lineage() {
 | 
			
		||||
		if err := ctx.Set(name, value); err == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return waitToShutdown(&wg, errC, shutdownC, graceShutdownC, c.Duration("grace-period"), log)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetFlagsFromConfigFile(c *cli.Context) error {
 | 
			
		||||
	const exitCode = 1
 | 
			
		||||
	log, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return cliutil.PrintLoggerSetupError("error setting up logger", err)
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	inputSource, err := config.ReadConfigFile(c, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == config.ErrNoConfigFile {
 | 
			
		||||
| 
						 | 
				
			
			@ -411,20 +391,20 @@ func waitToShutdown(wg *sync.WaitGroup,
 | 
			
		|||
	errC chan error,
 | 
			
		||||
	shutdownC, graceShutdownC chan struct{},
 | 
			
		||||
	gracePeriod time.Duration,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	if gracePeriod > 0 {
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceShutdownC, gracePeriod, logger)
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceShutdownC, gracePeriod, log)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = waitForSignal(errC, shutdownC, logger)
 | 
			
		||||
		err = waitForSignal(errC, shutdownC, log)
 | 
			
		||||
		close(graceShutdownC)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Quitting due to error: %s", err)
 | 
			
		||||
		log.Error().Msgf("Quitting due to error: %s", err)
 | 
			
		||||
	} else {
 | 
			
		||||
		logger.Info("Quitting...")
 | 
			
		||||
		log.Info().Msg("Quitting...")
 | 
			
		||||
	}
 | 
			
		||||
	// Wait for clean exit, discarding all errors
 | 
			
		||||
	go func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -440,16 +420,16 @@ func notifySystemd(waitForSignal *signal.Signal) {
 | 
			
		|||
	daemon.SdNotify(false, "READY=1")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writePidFile(waitForSignal *signal.Signal, pidFile string, logger logger.Service) {
 | 
			
		||||
func writePidFile(waitForSignal *signal.Signal, pidFile string, log *zerolog.Logger) {
 | 
			
		||||
	<-waitForSignal.Wait()
 | 
			
		||||
	expandedPath, err := homedir.Expand(pidFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Unable to expand %s, try to use absolute path in --pidfile: %s", pidFile, err)
 | 
			
		||||
		log.Error().Msgf("Unable to expand %s, try to use absolute path in --pidfile: %s", pidFile, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	file, err := os.Create(expandedPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Unable to write pid to %s: %s", expandedPath, err)
 | 
			
		||||
		log.Error().Msgf("Unable to write pid to %s: %s", expandedPath, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
| 
						 | 
				
			
			@ -1018,7 +998,7 @@ func configureProxyDNSFlags(shouldHide bool) []cli.Flag {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func stdinControl(reconnectCh chan origin.ReconnectSignal, logger logger.Service) {
 | 
			
		||||
func stdinControl(reconnectCh chan origin.ReconnectSignal, log *zerolog.Logger) {
 | 
			
		||||
	for {
 | 
			
		||||
		scanner := bufio.NewScanner(os.Stdin)
 | 
			
		||||
		for scanner.Scan() {
 | 
			
		||||
| 
						 | 
				
			
			@ -1033,17 +1013,17 @@ func stdinControl(reconnectCh chan origin.ReconnectSignal, logger logger.Service
 | 
			
		|||
				if len(parts) > 1 {
 | 
			
		||||
					var err error
 | 
			
		||||
					if reconnect.Delay, err = time.ParseDuration(parts[1]); err != nil {
 | 
			
		||||
						logger.Error(err.Error())
 | 
			
		||||
						log.Error().Msg(err.Error())
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
				logger.Infof("Sending reconnect signal %+v", reconnect)
 | 
			
		||||
				log.Info().Msgf("Sending reconnect signal %+v", reconnect)
 | 
			
		||||
				reconnectCh <- reconnect
 | 
			
		||||
			default:
 | 
			
		||||
				logger.Infof("Unknown command: %s", command)
 | 
			
		||||
				log.Info().Msgf("Unknown command: %s", command)
 | 
			
		||||
				fallthrough
 | 
			
		||||
			case "help":
 | 
			
		||||
				logger.Info(`Supported command:
 | 
			
		||||
				log.Info().Msg(`Supported command:
 | 
			
		||||
reconnect [delay]
 | 
			
		||||
- restarts one randomly chosen connection with optional delay before reconnect`)
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/edgediscovery"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/ingress"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/origin"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tlsconfig"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,6 +22,7 @@ import (
 | 
			
		|||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"golang.org/x/crypto/ssh/terminal"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -46,16 +46,16 @@ func findDefaultOriginCertPath() string {
 | 
			
		|||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateRandomClientID(logger logger.Service) (string, error) {
 | 
			
		||||
func generateRandomClientID(log *zerolog.Logger) (string, error) {
 | 
			
		||||
	u, err := uuid.NewRandom()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("couldn't create UUID for client ID %s", err)
 | 
			
		||||
		log.Error().Msgf("couldn't create UUID for client ID %s", err)
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return u.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func logClientOptions(c *cli.Context, logger logger.Service) {
 | 
			
		||||
func logClientOptions(c *cli.Context, log *zerolog.Logger) {
 | 
			
		||||
	flags := make(map[string]interface{})
 | 
			
		||||
	for _, flag := range c.LocalFlagNames() {
 | 
			
		||||
		flags[flag] = c.Generic(flag)
 | 
			
		||||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ func logClientOptions(c *cli.Context, logger logger.Service) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if len(flags) > 0 {
 | 
			
		||||
		logger.Infof("Environment variables %v", flags)
 | 
			
		||||
		log.Info().Msgf("Environment variables %v", flags)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	envs := make(map[string]string)
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ func logClientOptions(c *cli.Context, logger logger.Service) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(envs) > 0 {
 | 
			
		||||
		logger.Infof("Environmental variables %v", envs)
 | 
			
		||||
		log.Info().Msgf("Environmental variables %v", envs)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,32 +92,32 @@ func dnsProxyStandAlone(c *cli.Context) bool {
 | 
			
		|||
	return c.IsSet("proxy-dns") && (!c.IsSet("hostname") && !c.IsSet("tag") && !c.IsSet("hello-world"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findOriginCert(c *cli.Context, logger logger.Service) (string, error) {
 | 
			
		||||
func findOriginCert(c *cli.Context, log *zerolog.Logger) (string, error) {
 | 
			
		||||
	originCertPath := c.String("origincert")
 | 
			
		||||
	if originCertPath == "" {
 | 
			
		||||
		logger.Infof("Cannot determine default origin certificate path. No file %s in %v", config.DefaultCredentialFile, config.DefaultConfigSearchDirectories())
 | 
			
		||||
		log.Info().Msgf("Cannot determine default origin certificate path. No file %s in %v", config.DefaultCredentialFile, config.DefaultConfigSearchDirectories())
 | 
			
		||||
		if isRunningFromTerminal() {
 | 
			
		||||
			logger.Errorf("You need to specify the origin certificate path with --origincert option, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", argumentsUrl)
 | 
			
		||||
			log.Error().Msgf("You need to specify the origin certificate path with --origincert option, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", argumentsUrl)
 | 
			
		||||
			return "", fmt.Errorf("Client didn't specify origincert path when running from terminal")
 | 
			
		||||
		} else {
 | 
			
		||||
			logger.Errorf("You need to specify the origin certificate path by specifying the origincert option in the configuration file, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", serviceUrl)
 | 
			
		||||
			log.Error().Msgf("You need to specify the origin certificate path by specifying the origincert option in the configuration file, or set TUNNEL_ORIGIN_CERT environment variable. See %s for more information.", serviceUrl)
 | 
			
		||||
			return "", fmt.Errorf("Client didn't specify origincert path")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var err error
 | 
			
		||||
	originCertPath, err = homedir.Expand(originCertPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot resolve path %s: %s", originCertPath, err)
 | 
			
		||||
		log.Error().Msgf("Cannot resolve path %s: %s", originCertPath, err)
 | 
			
		||||
		return "", fmt.Errorf("Cannot resolve path %s", originCertPath)
 | 
			
		||||
	}
 | 
			
		||||
	// Check that the user has acquired a certificate using the login command
 | 
			
		||||
	ok, err := config.FileExists(originCertPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot check if origin cert exists at path %s", originCertPath)
 | 
			
		||||
		log.Error().Msgf("Cannot check if origin cert exists at path %s", originCertPath)
 | 
			
		||||
		return "", fmt.Errorf("Cannot check if origin cert exists at path %s", originCertPath)
 | 
			
		||||
	}
 | 
			
		||||
	if !ok {
 | 
			
		||||
		logger.Errorf(`Cannot find a valid certificate for your origin at the path:
 | 
			
		||||
		log.Error().Msgf(`Cannot find a valid certificate for your origin at the path:
 | 
			
		||||
 | 
			
		||||
    %s
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,23 +132,23 @@ If you don't have a certificate signed by Cloudflare, run the command:
 | 
			
		|||
	return originCertPath, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readOriginCert(originCertPath string, logger logger.Service) ([]byte, error) {
 | 
			
		||||
	logger.Debugf("Reading origin cert from %s", originCertPath)
 | 
			
		||||
func readOriginCert(originCertPath string, log *zerolog.Logger) ([]byte, error) {
 | 
			
		||||
	log.Debug().Msgf("Reading origin cert from %s", originCertPath)
 | 
			
		||||
 | 
			
		||||
	// Easier to send the certificate as []byte via RPC than decoding it at this point
 | 
			
		||||
	originCert, err := ioutil.ReadFile(originCertPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot read %s to load origin certificate: %s", originCertPath, err)
 | 
			
		||||
		log.Error().Msgf("Cannot read %s to load origin certificate: %s", originCertPath, err)
 | 
			
		||||
		return nil, fmt.Errorf("Cannot read %s to load origin certificate", originCertPath)
 | 
			
		||||
	}
 | 
			
		||||
	return originCert, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getOriginCert(c *cli.Context, logger logger.Service) ([]byte, error) {
 | 
			
		||||
	if originCertPath, err := findOriginCert(c, logger); err != nil {
 | 
			
		||||
func getOriginCert(c *cli.Context, log *zerolog.Logger) ([]byte, error) {
 | 
			
		||||
	if originCertPath, err := findOriginCert(c, log); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	} else {
 | 
			
		||||
		return readOriginCert(originCertPath, logger)
 | 
			
		||||
		return readOriginCert(originCertPath, log)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,8 +156,8 @@ func prepareTunnelConfig(
 | 
			
		|||
	c *cli.Context,
 | 
			
		||||
	buildInfo *buildinfo.BuildInfo,
 | 
			
		||||
	version string,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	transportLogger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
	transportLogger *zerolog.Logger,
 | 
			
		||||
	namedTunnel *connection.NamedTunnelConfig,
 | 
			
		||||
	isUIEnabled bool,
 | 
			
		||||
	eventChans []chan connection.Event,
 | 
			
		||||
| 
						 | 
				
			
			@ -166,13 +166,13 @@ func prepareTunnelConfig(
 | 
			
		|||
 | 
			
		||||
	hostname, err := validation.ValidateHostname(c.String("hostname"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Invalid hostname: %s", err)
 | 
			
		||||
		log.Error().Msgf("Invalid hostname: %s", err)
 | 
			
		||||
		return nil, ingress.Ingress{}, errors.Wrap(err, "Invalid hostname")
 | 
			
		||||
	}
 | 
			
		||||
	isFreeTunnel := hostname == ""
 | 
			
		||||
	clientID := c.String("id")
 | 
			
		||||
	if !c.IsSet("id") {
 | 
			
		||||
		clientID, err = generateRandomClientID(logger)
 | 
			
		||||
		clientID, err = generateRandomClientID(log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, ingress.Ingress{}, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -180,7 +180,7 @@ func prepareTunnelConfig(
 | 
			
		|||
 | 
			
		||||
	tags, err := NewTagSliceFromCLI(c.StringSlice("tag"))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Tag parse failure: %s", err)
 | 
			
		||||
		log.Error().Msgf("Tag parse failure: %s", err)
 | 
			
		||||
		return nil, ingress.Ingress{}, errors.Wrap(err, "Tag parse failure")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +188,7 @@ func prepareTunnelConfig(
 | 
			
		|||
 | 
			
		||||
	var originCert []byte
 | 
			
		||||
	if !isFreeTunnel {
 | 
			
		||||
		originCert, err = getOriginCert(c, logger)
 | 
			
		||||
		originCert, err = getOriginCert(c, log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, ingress.Ingress{}, errors.Wrap(err, "Error getting origin cert")
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -227,17 +227,17 @@ func prepareTunnelConfig(
 | 
			
		|||
 | 
			
		||||
	// Convert single-origin configuration into multi-origin configuration.
 | 
			
		||||
	if ingressRules.IsEmpty() {
 | 
			
		||||
		ingressRules, err = ingress.NewSingleOrigin(c, !isNamedTunnel, logger)
 | 
			
		||||
		ingressRules, err = ingress.NewSingleOrigin(c, !isNamedTunnel)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, ingress.Ingress{}, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	protocolSelector, err := connection.NewProtocolSelector(c.String("protocol"), namedTunnel, edgediscovery.HTTP2Percentage, origin.ResolveTTL, logger)
 | 
			
		||||
	protocolSelector, err := connection.NewProtocolSelector(c.String("protocol"), namedTunnel, edgediscovery.HTTP2Percentage, origin.ResolveTTL, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, ingress.Ingress{}, err
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("Initial protocol %s", protocolSelector.Current())
 | 
			
		||||
	log.Info().Msgf("Initial protocol %s", protocolSelector.Current())
 | 
			
		||||
 | 
			
		||||
	edgeTLSConfigs := make(map[connection.Protocol]*tls.Config, len(connection.ProtocolList))
 | 
			
		||||
	for _, p := range connection.ProtocolList {
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +248,7 @@ func prepareTunnelConfig(
 | 
			
		|||
		edgeTLSConfigs[p] = edgeTLSConfig
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	originClient := origin.NewClient(ingressRules, tags, logger)
 | 
			
		||||
	originClient := origin.NewClient(ingressRules, tags, log)
 | 
			
		||||
	connectionConfig := &connection.Config{
 | 
			
		||||
		OriginClient:    originClient,
 | 
			
		||||
		GracePeriod:     c.Duration("grace-period"),
 | 
			
		||||
| 
						 | 
				
			
			@ -272,7 +272,7 @@ func prepareTunnelConfig(
 | 
			
		|||
		IsFreeTunnel:     isFreeTunnel,
 | 
			
		||||
		LBPool:           c.String("lb-pool"),
 | 
			
		||||
		Tags:             tags,
 | 
			
		||||
		Logger:           logger,
 | 
			
		||||
		Log:              log,
 | 
			
		||||
		Observer:         connection.NewObserver(transportLogger, eventChans, isUIEnabled),
 | 
			
		||||
		ReportedVersion:  version,
 | 
			
		||||
		Retries:          c.Uint("retries"),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,8 +5,9 @@ import (
 | 
			
		|||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,25 +40,25 @@ func (a staticPath) Path() (string, error) {
 | 
			
		|||
// Implements CredFinder and looks for the credentials file in several directories
 | 
			
		||||
// searching for a file named <id>.json
 | 
			
		||||
type searchByID struct {
 | 
			
		||||
	id     uuid.UUID
 | 
			
		||||
	c      *cli.Context
 | 
			
		||||
	logger logger.Service
 | 
			
		||||
	fs     fileSystem
 | 
			
		||||
	id  uuid.UUID
 | 
			
		||||
	c   *cli.Context
 | 
			
		||||
	log *zerolog.Logger
 | 
			
		||||
	fs  fileSystem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newSearchByID(id uuid.UUID, c *cli.Context, logger logger.Service, fs fileSystem) CredFinder {
 | 
			
		||||
func newSearchByID(id uuid.UUID, c *cli.Context, log *zerolog.Logger, fs fileSystem) CredFinder {
 | 
			
		||||
	return searchByID{
 | 
			
		||||
		id:     id,
 | 
			
		||||
		c:      c,
 | 
			
		||||
		logger: logger,
 | 
			
		||||
		fs:     fs,
 | 
			
		||||
		id:  id,
 | 
			
		||||
		c:   c,
 | 
			
		||||
		log: log,
 | 
			
		||||
		fs:  fs,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s searchByID) Path() (string, error) {
 | 
			
		||||
 | 
			
		||||
	// Fallback to look for tunnel credentials in the origin cert directory
 | 
			
		||||
	if originCertPath, err := findOriginCert(s.c, s.logger); err == nil {
 | 
			
		||||
	if originCertPath, err := findOriginCert(s.c, s.log); err == nil {
 | 
			
		||||
		originCertDir := filepath.Dir(originCertPath)
 | 
			
		||||
		if filePath, err := tunnelFilePath(s.id, originCertDir); err == nil {
 | 
			
		||||
			if s.fs.validFilePath(filePath) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,12 +4,12 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
	"net/url"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/ingress"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func buildIngressSubcommand() *cli.Command {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,9 +8,9 @@ import (
 | 
			
		|||
	"path/filepath"
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	homedir "github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/mitchellh/go-homedir"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	cli "github.com/urfave/cli/v2"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
| 
						 | 
				
			
			@ -40,10 +40,7 @@ func buildLoginSubcommand(hidden bool) *cli.Command {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func login(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	path, ok, err := checkForExistingCert()
 | 
			
		||||
	if ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +56,15 @@ func login(c *cli.Context) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	resourceData, err := transfer.Run(loginURL, "cert", "callback", callbackStoreURL, false, false, logger)
 | 
			
		||||
	resourceData, err := transfer.Run(
 | 
			
		||||
		loginURL,
 | 
			
		||||
		"cert",
 | 
			
		||||
		"callback",
 | 
			
		||||
		callbackStoreURL,
 | 
			
		||||
		false,
 | 
			
		||||
		false,
 | 
			
		||||
		log,
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Failed to write the certificate due to the following error:\n%v\n\nYour browser will download the certificate instead. You will have to manually\ncopy it to the following path:\n\n%s\n", err, path)
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,20 +1,19 @@
 | 
			
		|||
package tunnel
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunneldns"
 | 
			
		||||
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func runDNSProxyServer(c *cli.Context, dnsReadySignal, shutdownC chan struct{}, logger logger.Service) error {
 | 
			
		||||
func runDNSProxyServer(c *cli.Context, dnsReadySignal, shutdownC chan struct{}, log *zerolog.Logger) error {
 | 
			
		||||
	port := c.Int("proxy-dns-port")
 | 
			
		||||
	if port <= 0 || port > 65535 {
 | 
			
		||||
		return errors.New("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
 | 
			
		||||
	}
 | 
			
		||||
	listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap"), logger)
 | 
			
		||||
	listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"), c.StringSlice("proxy-dns-bootstrap"), log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		close(dnsReadySignal)
 | 
			
		||||
		listener.Stop()
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +25,6 @@ func runDNSProxyServer(c *cli.Context, dnsReadySignal, shutdownC chan struct{},
 | 
			
		|||
		return errors.Wrap(err, "Cannot start the DNS over HTTPS proxy server")
 | 
			
		||||
	}
 | 
			
		||||
	<-shutdownC
 | 
			
		||||
	listener.Stop()
 | 
			
		||||
	_ = listener.Stop()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,24 +6,24 @@ import (
 | 
			
		|||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// waitForSignal notifies all routines to shutdownC immediately by closing the
 | 
			
		||||
// shutdownC when one of the routines in main exits, or when this process receives
 | 
			
		||||
// SIGTERM/SIGINT
 | 
			
		||||
func waitForSignal(errC chan error, shutdownC chan struct{}, logger logger.Service) error {
 | 
			
		||||
func waitForSignal(errC chan error, shutdownC chan struct{}, log *zerolog.Logger) error {
 | 
			
		||||
	signals := make(chan os.Signal, 10)
 | 
			
		||||
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
 | 
			
		||||
	defer signal.Stop(signals)
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case err := <-errC:
 | 
			
		||||
		logger.Infof("terminating due to error: %v", err)
 | 
			
		||||
		log.Info().Msgf("terminating due to error: %v", err)
 | 
			
		||||
		close(shutdownC)
 | 
			
		||||
		return err
 | 
			
		||||
	case s := <-signals:
 | 
			
		||||
		logger.Infof("terminating due to signal %s", s)
 | 
			
		||||
		log.Info().Msgf("terminating due to signal %s", s)
 | 
			
		||||
		close(shutdownC)
 | 
			
		||||
	case <-shutdownC:
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ func waitForSignal(errC chan error, shutdownC chan struct{}, logger logger.Servi
 | 
			
		|||
func waitForSignalWithGraceShutdown(errC chan error,
 | 
			
		||||
	shutdownC, graceShutdownC chan struct{},
 | 
			
		||||
	gracePeriod time.Duration,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	logger *zerolog.Logger,
 | 
			
		||||
) error {
 | 
			
		||||
	signals := make(chan os.Signal, 10)
 | 
			
		||||
	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
 | 
			
		||||
| 
						 | 
				
			
			@ -49,16 +49,16 @@ func waitForSignalWithGraceShutdown(errC chan error,
 | 
			
		|||
 | 
			
		||||
	select {
 | 
			
		||||
	case err := <-errC:
 | 
			
		||||
		logger.Infof("Initiating graceful shutdown due to %v ...", err)
 | 
			
		||||
		logger.Info().Msgf("Initiating graceful shutdown due to %v ...", err)
 | 
			
		||||
		close(graceShutdownC)
 | 
			
		||||
		close(shutdownC)
 | 
			
		||||
		return err
 | 
			
		||||
	case s := <-signals:
 | 
			
		||||
		logger.Infof("Initiating graceful shutdown due to signal %s ...", s)
 | 
			
		||||
		logger.Info().Msgf("Initiating graceful shutdown due to signal %s ...", s)
 | 
			
		||||
		close(graceShutdownC)
 | 
			
		||||
		waitForGracePeriod(signals, errC, shutdownC, gracePeriod, logger)
 | 
			
		||||
		waitForGracePeriod(signals, errC, shutdownC, gracePeriod)
 | 
			
		||||
	case <-graceShutdownC:
 | 
			
		||||
		waitForGracePeriod(signals, errC, shutdownC, gracePeriod, logger)
 | 
			
		||||
		waitForGracePeriod(signals, errC, shutdownC, gracePeriod)
 | 
			
		||||
	case <-shutdownC:
 | 
			
		||||
		close(graceShutdownC)
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,6 @@ func waitForGracePeriod(signals chan os.Signal,
 | 
			
		|||
	errC chan error,
 | 
			
		||||
	shutdownC chan struct{},
 | 
			
		||||
	gracePeriod time.Duration,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
) {
 | 
			
		||||
	// Unregister signal handler early, so the client can send a second SIGTERM/SIGINT
 | 
			
		||||
	// to force shutdown cloudflared
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,11 +2,11 @@ package tunnel
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,7 @@ func testChannelClosed(t *testing.T, c chan struct{}) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestWaitForSignal(t *testing.T) {
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	// Test handling server error
 | 
			
		||||
	errC := make(chan error)
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ func TestWaitForSignal(t *testing.T) {
 | 
			
		|||
	}()
 | 
			
		||||
 | 
			
		||||
	// received error, shutdownC should be closed
 | 
			
		||||
	err := waitForSignal(errC, shutdownC, logger)
 | 
			
		||||
	err := waitForSignal(errC, shutdownC, &log)
 | 
			
		||||
	assert.Equal(t, serverErr, err)
 | 
			
		||||
	testChannelClosed(t, shutdownC)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -56,10 +56,10 @@ func TestWaitForSignal(t *testing.T) {
 | 
			
		|||
		go func(sig syscall.Signal) {
 | 
			
		||||
			// sleep for a tick to prevent sending signal before calling waitForSignal
 | 
			
		||||
			time.Sleep(tick)
 | 
			
		||||
			syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
			_ = syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
		}(sig)
 | 
			
		||||
 | 
			
		||||
		err = waitForSignal(errC, shutdownC, logger)
 | 
			
		||||
		err = waitForSignal(errC, shutdownC, &log)
 | 
			
		||||
		assert.Equal(t, nil, err)
 | 
			
		||||
		assert.Equal(t, shutdownErr, <-errC)
 | 
			
		||||
		testChannelClosed(t, shutdownC)
 | 
			
		||||
| 
						 | 
				
			
			@ -76,10 +76,10 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
 | 
			
		|||
		errC <- serverErr
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	// received error, both shutdownC and graceshutdownC should be closed
 | 
			
		||||
	err := waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger)
 | 
			
		||||
	err := waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, &log)
 | 
			
		||||
	assert.Equal(t, serverErr, err)
 | 
			
		||||
	testChannelClosed(t, shutdownC)
 | 
			
		||||
	testChannelClosed(t, graceshutdownC)
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
 | 
			
		|||
	shutdownC = make(chan struct{})
 | 
			
		||||
	graceshutdownC = make(chan struct{})
 | 
			
		||||
	close(shutdownC)
 | 
			
		||||
	err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger)
 | 
			
		||||
	err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, &log)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	testChannelClosed(t, shutdownC)
 | 
			
		||||
	testChannelClosed(t, graceshutdownC)
 | 
			
		||||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
 | 
			
		|||
	shutdownC = make(chan struct{})
 | 
			
		||||
	graceshutdownC = make(chan struct{})
 | 
			
		||||
	close(graceshutdownC)
 | 
			
		||||
	err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger)
 | 
			
		||||
	err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, &log)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	testChannelClosed(t, shutdownC)
 | 
			
		||||
	testChannelClosed(t, graceshutdownC)
 | 
			
		||||
| 
						 | 
				
			
			@ -119,10 +119,10 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
 | 
			
		|||
		go func(sig syscall.Signal) {
 | 
			
		||||
			// sleep for a tick to prevent sending signal before calling waitForSignalWithGraceShutdown
 | 
			
		||||
			time.Sleep(tick)
 | 
			
		||||
			syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
			_ = syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
		}(sig)
 | 
			
		||||
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger)
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, &log)
 | 
			
		||||
		assert.Equal(t, nil, err)
 | 
			
		||||
		assert.Equal(t, graceShutdownErr, <-errC)
 | 
			
		||||
		testChannelClosed(t, shutdownC)
 | 
			
		||||
| 
						 | 
				
			
			@ -145,10 +145,10 @@ func TestWaitForSignalWithGraceShutdown(t *testing.T) {
 | 
			
		|||
		go func(sig syscall.Signal) {
 | 
			
		||||
			// sleep for a tick to prevent sending signal before calling waitForSignalWithGraceShutdown
 | 
			
		||||
			time.Sleep(tick)
 | 
			
		||||
			syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
			_ = syscall.Kill(syscall.Getpid(), sig)
 | 
			
		||||
		}(sig)
 | 
			
		||||
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, logger)
 | 
			
		||||
		err = waitForSignalWithGraceShutdown(errC, shutdownC, graceshutdownC, tick, &log)
 | 
			
		||||
		assert.Equal(t, nil, err)
 | 
			
		||||
		assert.Equal(t, shutdownErr, <-errC)
 | 
			
		||||
		testChannelClosed(t, shutdownC)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/certutil"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +30,7 @@ func (e errInvalidJSONCredential) Error() string {
 | 
			
		|||
// pass between subcommands, and make sure they are only initialized once
 | 
			
		||||
type subcommandContext struct {
 | 
			
		||||
	c           *cli.Context
 | 
			
		||||
	logger      logger.Service
 | 
			
		||||
	log         *zerolog.Logger
 | 
			
		||||
	isUIEnabled bool
 | 
			
		||||
	fs          fileSystem
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,14 +43,11 @@ func newSubcommandContext(c *cli.Context) (*subcommandContext, error) {
 | 
			
		|||
	isUIEnabled := c.IsSet(uiFlag) && c.String("name") != ""
 | 
			
		||||
 | 
			
		||||
	// If UI is enabled, terminal log output should be disabled -- log should be written into a UI log window instead
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, isUIEnabled)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, isUIEnabled)
 | 
			
		||||
 | 
			
		||||
	return &subcommandContext{
 | 
			
		||||
		c:           c,
 | 
			
		||||
		logger:      logger,
 | 
			
		||||
		log:         log,
 | 
			
		||||
		isUIEnabled: isUIEnabled,
 | 
			
		||||
		fs:          realFileSystem{},
 | 
			
		||||
	}, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +58,7 @@ func (sc *subcommandContext) credentialFinder(tunnelID uuid.UUID) CredFinder {
 | 
			
		|||
	if path := sc.c.String(CredFileFlag); path != "" {
 | 
			
		||||
		return newStaticPath(path, sc.fs)
 | 
			
		||||
	}
 | 
			
		||||
	return newSearchByID(tunnelID, sc.c, sc.logger, sc.fs)
 | 
			
		||||
	return newSearchByID(tunnelID, sc.c, sc.log, sc.fs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type userCredential struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +75,15 @@ func (sc *subcommandContext) client() (tunnelstore.Client, error) {
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	userAgent := fmt.Sprintf("cloudflared/%s", version)
 | 
			
		||||
	client, err := tunnelstore.NewRESTClient(sc.c.String("api-url"), credential.cert.AccountID, credential.cert.ZoneID, credential.cert.ServiceKey, userAgent, sc.logger)
 | 
			
		||||
	client, err := tunnelstore.NewRESTClient(
 | 
			
		||||
		sc.c.String("api-url"),
 | 
			
		||||
		credential.cert.AccountID,
 | 
			
		||||
		credential.cert.ZoneID,
 | 
			
		||||
		credential.cert.ServiceKey,
 | 
			
		||||
		userAgent,
 | 
			
		||||
		sc.log,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -87,11 +93,11 @@ func (sc *subcommandContext) client() (tunnelstore.Client, error) {
 | 
			
		|||
 | 
			
		||||
func (sc *subcommandContext) credential() (*userCredential, error) {
 | 
			
		||||
	if sc.userCredential == nil {
 | 
			
		||||
		originCertPath, err := findOriginCert(sc.c, sc.logger)
 | 
			
		||||
		originCertPath, err := findOriginCert(sc.c, sc.log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.Wrap(err, "Error locating origin cert")
 | 
			
		||||
		}
 | 
			
		||||
		blocks, err := readOriginCert(originCertPath, sc.logger)
 | 
			
		||||
		blocks, err := readOriginCert(originCertPath, sc.log)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, errors.Wrapf(err, "Can't read origin cert from %s", originCertPath)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +169,7 @@ func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
 | 
			
		|||
		TunnelName:   name,
 | 
			
		||||
	}
 | 
			
		||||
	filePath, writeFileErr := writeTunnelCredentials(credential.certPath, &tunnelCredentials)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	if writeFileErr != nil {
 | 
			
		||||
		var errorLines []string
 | 
			
		||||
		errorLines = append(errorLines, fmt.Sprintf("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write to the tunnel credentials file at %v.json.", tunnel.Name, tunnel.ID, tunnel.ID))
 | 
			
		||||
		errorLines = append(errorLines, fmt.Sprintf("The file-writing error is: %v", writeFileErr))
 | 
			
		||||
| 
						 | 
				
			
			@ -176,13 +182,13 @@ func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
 | 
			
		|||
		errorMsg := strings.Join(errorLines, "\n")
 | 
			
		||||
		return nil, errors.New(errorMsg)
 | 
			
		||||
	}
 | 
			
		||||
	sc.logger.Infof("Tunnel credentials written to %v. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.", filePath)
 | 
			
		||||
	sc.log.Info().Msgf("Tunnel credentials written to %v. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.", filePath)
 | 
			
		||||
 | 
			
		||||
	if outputFormat := sc.c.String(outputFormatFlag.Name); outputFormat != "" {
 | 
			
		||||
		return nil, renderOutput(outputFormat, &tunnel)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sc.logger.Infof("Created tunnel %s with id %s", tunnel.Name, tunnel.ID)
 | 
			
		||||
	sc.log.Info().Msgf("Created tunnel %s with id %s", tunnel.Name, tunnel.ID)
 | 
			
		||||
	return tunnel, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +236,7 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
 | 
			
		|||
		credFinder := sc.credentialFinder(id)
 | 
			
		||||
		if tunnelCredentialsPath, err := credFinder.Path(); err == nil {
 | 
			
		||||
			if err = os.Remove(tunnelCredentialsPath); err != nil {
 | 
			
		||||
				sc.logger.Infof("Tunnel %v was deleted, but we could not remove its credentials file  %s: %s. Consider deleting this file manually.", id, tunnelCredentialsPath, err)
 | 
			
		||||
				sc.log.Info().Msgf("Tunnel %v was deleted, but we could not remove its credentials file  %s: %s. Consider deleting this file manually.", id, tunnelCredentialsPath, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -254,18 +260,19 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error {
 | 
			
		|||
	credentials, err := sc.findCredentials(tunnelID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if e, ok := err.(errInvalidJSONCredential); ok {
 | 
			
		||||
			sc.logger.Errorf("The credentials file at %s contained invalid JSON. This is probably caused by passing the wrong filepath. Reminder: the credentials file is a .json file created via `cloudflared tunnel create`.", e.path)
 | 
			
		||||
			sc.logger.Errorf("Invalid JSON when parsing credentials file: %s", e.err.Error())
 | 
			
		||||
			sc.log.Error().Msgf("The credentials file at %s contained invalid JSON. This is probably caused by passing the wrong filepath. Reminder: the credentials file is a .json file created via `cloudflared tunnel create`.", e.path)
 | 
			
		||||
			sc.log.Error().Msgf("Invalid JSON when parsing credentials file: %s", e.err.Error())
 | 
			
		||||
		}
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return StartServer(
 | 
			
		||||
		sc.c,
 | 
			
		||||
		version,
 | 
			
		||||
		shutdownC,
 | 
			
		||||
		graceShutdownC,
 | 
			
		||||
		&connection.NamedTunnelConfig{Credentials: credentials},
 | 
			
		||||
		sc.logger,
 | 
			
		||||
		sc.log,
 | 
			
		||||
		sc.isUIEnabled,
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -276,9 +283,9 @@ func (sc *subcommandContext) cleanupConnections(tunnelIDs []uuid.UUID) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, tunnelID := range tunnelIDs {
 | 
			
		||||
		sc.logger.Infof("Cleanup connection for tunnel %s", tunnelID)
 | 
			
		||||
		sc.log.Info().Msgf("Cleanup connection for tunnel %s", tunnelID)
 | 
			
		||||
		if err := client.CleanupConnections(tunnelID); err != nil {
 | 
			
		||||
			sc.logger.Errorf("Error cleaning up connections for tunnel %v, error :%v", tunnelID, err)
 | 
			
		||||
			sc.log.Error().Msgf("Error cleaning up connections for tunnel %v, error :%v", tunnelID, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,16 +4,15 @@ import (
 | 
			
		|||
	"encoding/base64"
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunnelstore"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +105,7 @@ func (fs mockFileSystem) readFile(filePath string) ([]byte, error) {
 | 
			
		|||
func Test_subcommandContext_findCredentials(t *testing.T) {
 | 
			
		||||
	type fields struct {
 | 
			
		||||
		c                 *cli.Context
 | 
			
		||||
		logger            logger.Service
 | 
			
		||||
		log               *zerolog.Logger
 | 
			
		||||
		isUIEnabled       bool
 | 
			
		||||
		fs                fileSystem
 | 
			
		||||
		tunnelstoreClient tunnelstore.Client
 | 
			
		||||
| 
						 | 
				
			
			@ -137,8 +136,7 @@ func Test_subcommandContext_findCredentials(t *testing.T) {
 | 
			
		|||
		},
 | 
			
		||||
		vfp: func(string) bool { return true },
 | 
			
		||||
	}
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name    string
 | 
			
		||||
| 
						 | 
				
			
			@ -150,13 +148,13 @@ func Test_subcommandContext_findCredentials(t *testing.T) {
 | 
			
		|||
		{
 | 
			
		||||
			name: "Filepath given leads to old credentials file",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				logger: logger,
 | 
			
		||||
				fs:     fs,
 | 
			
		||||
				log: &log,
 | 
			
		||||
				fs:  fs,
 | 
			
		||||
				c: func() *cli.Context {
 | 
			
		||||
					flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
 | 
			
		||||
					flagSet.String(CredFileFlag, oldCertPath, "")
 | 
			
		||||
					c := cli.NewContext(cli.NewApp(), flagSet, nil)
 | 
			
		||||
					err = c.Set(CredFileFlag, oldCertPath)
 | 
			
		||||
					_ = c.Set(CredFileFlag, oldCertPath)
 | 
			
		||||
					return c
 | 
			
		||||
				}(),
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			@ -172,13 +170,13 @@ func Test_subcommandContext_findCredentials(t *testing.T) {
 | 
			
		|||
		{
 | 
			
		||||
			name: "Filepath given leads to new credentials file",
 | 
			
		||||
			fields: fields{
 | 
			
		||||
				logger: logger,
 | 
			
		||||
				fs:     fs,
 | 
			
		||||
				log: &log,
 | 
			
		||||
				fs:  fs,
 | 
			
		||||
				c: func() *cli.Context {
 | 
			
		||||
					flagSet := flag.NewFlagSet("test0", flag.PanicOnError)
 | 
			
		||||
					flagSet.String(CredFileFlag, newCertPath, "")
 | 
			
		||||
					c := cli.NewContext(cli.NewApp(), flagSet, nil)
 | 
			
		||||
					err = c.Set(CredFileFlag, newCertPath)
 | 
			
		||||
					_ = c.Set(CredFileFlag, newCertPath)
 | 
			
		||||
					return c
 | 
			
		||||
				}(),
 | 
			
		||||
			},
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +195,7 @@ func Test_subcommandContext_findCredentials(t *testing.T) {
 | 
			
		|||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			sc := &subcommandContext{
 | 
			
		||||
				c:                 tt.fields.c,
 | 
			
		||||
				logger:            tt.fields.logger,
 | 
			
		||||
				log:               tt.fields.log,
 | 
			
		||||
				isUIEnabled:       tt.fields.isUIEnabled,
 | 
			
		||||
				fs:                tt.fields.fs,
 | 
			
		||||
				tunnelstoreClient: tt.fields.tunnelstoreClient,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -223,7 +223,7 @@ func fmtAndPrintTunnelList(tunnels []*tunnelstore.Tunnel, showRecentlyDisconnect
 | 
			
		|||
	defer writer.Flush()
 | 
			
		||||
 | 
			
		||||
	// Print column headers with tabbed columns
 | 
			
		||||
	fmt.Fprintln(writer, "ID\tNAME\tCREATED\tCONNECTIONS\t")
 | 
			
		||||
	_, _ = fmt.Fprintln(writer, "ID\tNAME\tCREATED\tCONNECTIONS\t")
 | 
			
		||||
 | 
			
		||||
	// Loop through tunnels, create formatted string for each, and print using tabwriter
 | 
			
		||||
	for _, t := range tunnels {
 | 
			
		||||
| 
						 | 
				
			
			@ -234,7 +234,7 @@ func fmtAndPrintTunnelList(tunnels []*tunnelstore.Tunnel, showRecentlyDisconnect
 | 
			
		|||
			t.CreatedAt.Format(time.RFC3339),
 | 
			
		||||
			fmtConnections(t.Connections, showRecentlyDisconnected),
 | 
			
		||||
		)
 | 
			
		||||
		fmt.Fprintln(writer, formattedStr)
 | 
			
		||||
		_, _ = fmt.Fprintln(writer, formattedStr)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -360,7 +360,7 @@ func runNamedTunnel(sc *subcommandContext, tunnelRef string) error {
 | 
			
		|||
		return errors.Wrap(err, "error parsing tunnel ID")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sc.logger.Infof("Starting tunnel %s", tunnelID.String())
 | 
			
		||||
	sc.log.Info().Msgf("Starting tunnel %s", tunnelID.String())
 | 
			
		||||
 | 
			
		||||
	return sc.run(tunnelID)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -515,7 +515,7 @@ func routeCommand(c *cli.Context) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sc.logger.Infof(res.SuccessSummary())
 | 
			
		||||
	sc.log.Info().Msg(res.SuccessSummary())
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,14 +4,13 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/ingress"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/gdamore/tcell"
 | 
			
		||||
	"github.com/rivo/tview"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type connState struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -51,16 +50,16 @@ func NewUIModel(version, hostname, metricsURL string, ing *ingress.Ingress, haCo
 | 
			
		|||
 | 
			
		||||
func (data *uiModel) LaunchUI(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	generalLogger, transportLogger logger.Service,
 | 
			
		||||
	logLevels []logger.Level,
 | 
			
		||||
	log, transportLog *zerolog.Logger,
 | 
			
		||||
	tunnelEventChan <-chan connection.Event,
 | 
			
		||||
) {
 | 
			
		||||
	// Configure the logger to stream logs into the textview
 | 
			
		||||
 | 
			
		||||
	// Add TextView as a group to write output to
 | 
			
		||||
	logTextView := NewDynamicColorTextView()
 | 
			
		||||
	generalLogger.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...)
 | 
			
		||||
	transportLogger.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...)
 | 
			
		||||
	// TODO: Format log for UI
 | 
			
		||||
	//log.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...)
 | 
			
		||||
	//transportLog.Add(logTextView, logger.NewUIFormatter(time.RFC3339), logLevels...)
 | 
			
		||||
 | 
			
		||||
	// Construct the UI
 | 
			
		||||
	palette := palette{
 | 
			
		||||
| 
						 | 
				
			
			@ -125,7 +124,7 @@ func (data *uiModel) LaunchUI(
 | 
			
		|||
				case connection.Connected:
 | 
			
		||||
					data.setConnTableCell(event, connTable, palette)
 | 
			
		||||
				case connection.Disconnected, connection.Reconnecting:
 | 
			
		||||
					data.changeConnStatus(event, connTable, generalLogger, palette)
 | 
			
		||||
					data.changeConnStatus(event, connTable, log, palette)
 | 
			
		||||
				case connection.SetURL:
 | 
			
		||||
					tunnelHostText.SetText(event.URL)
 | 
			
		||||
					data.edgeURL = event.URL
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +140,7 @@ func (data *uiModel) LaunchUI(
 | 
			
		|||
 | 
			
		||||
	go func() {
 | 
			
		||||
		if err := app.SetRoot(frame, true).Run(); err != nil {
 | 
			
		||||
			generalLogger.Errorf("Error launching UI: %s", err)
 | 
			
		||||
			log.Error().Msgf("Error launching UI: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -159,13 +158,13 @@ func handleNewText(app *tview.Application, logTextView *tview.TextView) func() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (data *uiModel) changeConnStatus(event connection.Event, table *tview.Table, logger logger.Service, palette palette) {
 | 
			
		||||
func (data *uiModel) changeConnStatus(event connection.Event, table *tview.Table, log *zerolog.Logger, palette palette) {
 | 
			
		||||
	index := int(event.Index)
 | 
			
		||||
	// Get connection location and state
 | 
			
		||||
	connState := data.getConnState(index)
 | 
			
		||||
	// Check if connection is already displayed in UI
 | 
			
		||||
	if connState == nil {
 | 
			
		||||
		logger.Info("Connection is not in the UI table")
 | 
			
		||||
		log.Info().Msg("Connection is not in the UI table")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package updater
 | 
			
		|||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
| 
						 | 
				
			
			@ -14,12 +15,10 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/facebookgo/grace/gracenet"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DefaultCheckUpdateFreq        = time.Hour * 24
 | 
			
		||||
	appID                         = "app_idCzgxYerVD"
 | 
			
		||||
	noUpdateInShellMessage        = "cloudflared will not automatically update when run from the shell. To enable auto-updates, run cloudflared as a service: https://developers.cloudflare.com/argo-tunnel/reference/service/"
 | 
			
		||||
	noUpdateOnWindowsMessage      = "cloudflared will not automatically update on Windows systems."
 | 
			
		||||
	noUpdateManagedPackageMessage = "cloudflared will not automatically update if installed by a package manager."
 | 
			
		||||
| 
						 | 
				
			
			@ -114,38 +113,35 @@ func checkForUpdateAndApply(options updateOptions) UpdateOutcome {
 | 
			
		|||
 | 
			
		||||
// Update is the handler for the update command from the command line
 | 
			
		||||
func Update(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	if wasInstalledFromPackageManager() {
 | 
			
		||||
		logger.Error("cloudflared was installed by a package manager. Please update using the same method.")
 | 
			
		||||
		log.Error().Msg("cloudflared was installed by a package manager. Please update using the same method.")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isBeta := c.Bool("beta")
 | 
			
		||||
	if isBeta {
 | 
			
		||||
		logger.Info("cloudflared is set to update to the latest beta version")
 | 
			
		||||
		log.Info().Msg("cloudflared is set to update to the latest beta version")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isStaging := c.Bool("staging")
 | 
			
		||||
	if isStaging {
 | 
			
		||||
		logger.Info("cloudflared is set to update from staging")
 | 
			
		||||
		log.Info().Msg("cloudflared is set to update from staging")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isForced := c.Bool("force")
 | 
			
		||||
	if isForced {
 | 
			
		||||
		logger.Info("cloudflared is set to upgrade to the latest publish version regardless of the current version")
 | 
			
		||||
		log.Info().Msg("cloudflared is set to upgrade to the latest publish version regardless of the current version")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updateOutcome := loggedUpdate(logger, updateOptions{isBeta: isBeta, isStaging: isStaging, isForced: isForced, version: c.String("version")})
 | 
			
		||||
	updateOutcome := loggedUpdate(log, updateOptions{isBeta: isBeta, isStaging: isStaging, isForced: isForced, version: c.String("version")})
 | 
			
		||||
	if updateOutcome.Error != nil {
 | 
			
		||||
		return &statusErr{updateOutcome.Error}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if updateOutcome.noUpdate() {
 | 
			
		||||
		logger.Infof("cloudflared is up to date (%s)", updateOutcome.Version)
 | 
			
		||||
		log.Info().Msgf("cloudflared is up to date (%s)", updateOutcome.Version)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,13 +149,13 @@ func Update(c *cli.Context) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Checks for an update and applies it if one is available
 | 
			
		||||
func loggedUpdate(logger logger.Service, options updateOptions) UpdateOutcome {
 | 
			
		||||
func loggedUpdate(log *zerolog.Logger, options updateOptions) UpdateOutcome {
 | 
			
		||||
	updateOutcome := checkForUpdateAndApply(options)
 | 
			
		||||
	if updateOutcome.Updated {
 | 
			
		||||
		logger.Infof("cloudflared has been updated to version %s", updateOutcome.Version)
 | 
			
		||||
		log.Info().Msgf("cloudflared has been updated to version %s", updateOutcome.Version)
 | 
			
		||||
	}
 | 
			
		||||
	if updateOutcome.Error != nil {
 | 
			
		||||
		logger.Errorf("update check failed: %s", updateOutcome.Error)
 | 
			
		||||
		log.Error().Msgf("update check failed: %s", updateOutcome.Error)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return updateOutcome
 | 
			
		||||
| 
						 | 
				
			
			@ -170,7 +166,7 @@ type AutoUpdater struct {
 | 
			
		|||
	configurable     *configurable
 | 
			
		||||
	listeners        *gracenet.Net
 | 
			
		||||
	updateConfigChan chan *configurable
 | 
			
		||||
	logger           logger.Service
 | 
			
		||||
	log              *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AutoUpdaterConfigurable is the attributes of AutoUpdater that can be reconfigured during runtime
 | 
			
		||||
| 
						 | 
				
			
			@ -179,7 +175,7 @@ type configurable struct {
 | 
			
		|||
	freq    time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net, logger logger.Service) *AutoUpdater {
 | 
			
		||||
func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net, log *zerolog.Logger) *AutoUpdater {
 | 
			
		||||
	updaterConfigurable := &configurable{
 | 
			
		||||
		enabled: true,
 | 
			
		||||
		freq:    freq,
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +188,7 @@ func NewAutoUpdater(freq time.Duration, listeners *gracenet.Net, logger logger.S
 | 
			
		|||
		configurable:     updaterConfigurable,
 | 
			
		||||
		listeners:        listeners,
 | 
			
		||||
		updateConfigChan: make(chan *configurable),
 | 
			
		||||
		logger:           logger,
 | 
			
		||||
		log:              log,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,19 +196,19 @@ func (a *AutoUpdater) Run(ctx context.Context) error {
 | 
			
		|||
	ticker := time.NewTicker(a.configurable.freq)
 | 
			
		||||
	for {
 | 
			
		||||
		if a.configurable.enabled {
 | 
			
		||||
			updateOutcome := loggedUpdate(a.logger, updateOptions{})
 | 
			
		||||
			updateOutcome := loggedUpdate(a.log, updateOptions{})
 | 
			
		||||
			if updateOutcome.Updated {
 | 
			
		||||
				if IsSysV() {
 | 
			
		||||
					// SysV doesn't have a mechanism to keep service alive, we have to restart the process
 | 
			
		||||
					a.logger.Info("Restarting service managed by SysV...")
 | 
			
		||||
					a.log.Info().Msg("Restarting service managed by SysV...")
 | 
			
		||||
					pid, err := a.listeners.StartProcess()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						a.logger.Errorf("Unable to restart server automatically: %s", err)
 | 
			
		||||
						a.log.Error().Msgf("Unable to restart server automatically: %s", err)
 | 
			
		||||
						return &statusErr{err: err}
 | 
			
		||||
					}
 | 
			
		||||
					// stop old process after autoupdate. Otherwise we create a new process
 | 
			
		||||
					// after each update
 | 
			
		||||
					a.logger.Infof("PID of the new process is %d", pid)
 | 
			
		||||
					a.log.Info().Msgf("PID of the new process is %d", pid)
 | 
			
		||||
				}
 | 
			
		||||
				return &statusSuccess{newVersion: updateOutcome.Version}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -244,26 +240,26 @@ func (a *AutoUpdater) Update(newFreq time.Duration) {
 | 
			
		|||
	a.updateConfigChan <- newConfigurable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func IsAutoupdateEnabled(c *cli.Context, l logger.Service) bool {
 | 
			
		||||
	if !SupportAutoUpdate(l) {
 | 
			
		||||
func IsAutoupdateEnabled(c *cli.Context, log *zerolog.Logger) bool {
 | 
			
		||||
	if !SupportAutoUpdate(log) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return !c.Bool("no-autoupdate") && c.Duration("autoupdate-freq") != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SupportAutoUpdate(logger logger.Service) bool {
 | 
			
		||||
func SupportAutoUpdate(log *zerolog.Logger) bool {
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		logger.Info(noUpdateOnWindowsMessage)
 | 
			
		||||
		log.Info().Msg(noUpdateOnWindowsMessage)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if wasInstalledFromPackageManager() {
 | 
			
		||||
		logger.Info(noUpdateManagedPackageMessage)
 | 
			
		||||
		log.Info().Msg(noUpdateManagedPackageMessage)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if isRunningFromTerminal() {
 | 
			
		||||
		logger.Info(noUpdateInShellMessage)
 | 
			
		||||
		log.Info().Msg(noUpdateInShellMessage)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,15 +4,15 @@ import (
 | 
			
		|||
	"context"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/facebookgo/grace/gracenet"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestDisabledAutoUpdater(t *testing.T) {
 | 
			
		||||
	listeners := &gracenet.Net{}
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	autoupdater := NewAutoUpdater(0, listeners, logger)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
	autoupdater := NewAutoUpdater(0, listeners, &log)
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	errC := make(chan error)
 | 
			
		||||
	go func() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,9 +13,8 @@ import (
 | 
			
		|||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	cli "github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
	"golang.org/x/sys/windows"
 | 
			
		||||
	"golang.org/x/sys/windows/svc"
 | 
			
		||||
	"golang.org/x/sys/windows/svc/eventlog"
 | 
			
		||||
| 
						 | 
				
			
			@ -67,15 +66,11 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		|||
	//     2. get ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
 | 
			
		||||
	// This involves actually trying to start the service.
 | 
			
		||||
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.Create(nil)
 | 
			
		||||
 | 
			
		||||
	isIntSess, err := svc.IsAnInteractiveSession()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Fatalf("failed to determine if we are running in an interactive session: %v", err)
 | 
			
		||||
		log.Fatal().Msgf("failed to determine if we are running in an interactive session: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if isIntSess {
 | 
			
		||||
		app.Run(os.Args)
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +88,7 @@ func runApp(app *cli.App, shutdownC, graceShutdownC chan struct{}) {
 | 
			
		|||
			app.Run(os.Args)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		logger.Fatalf("%s service failed: %v", windowsServiceName, err)
 | 
			
		||||
		log.Fatal().Msgf("%s service failed: %v", windowsServiceName, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -105,15 +100,10 @@ type windowsService struct {
 | 
			
		|||
 | 
			
		||||
// called by the package code at the start of the service
 | 
			
		||||
func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeRequest, statusChan chan<- svc.Status) (ssec bool, errno uint32) {
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log := logger.Create(nil)
 | 
			
		||||
	elog, err := eventlog.Open(windowsServiceName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot open event log for %s with error: %s", windowsServiceName, err)
 | 
			
		||||
		log.Error().Msgf("Cannot open event log for %s with error: %s", windowsServiceName, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer elog.Close()
 | 
			
		||||
| 
						 | 
				
			
			@ -173,79 +163,73 @@ func (s *windowsService) Execute(serviceArgs []string, r <-chan svc.ChangeReques
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func installWindowsService(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	logger.Infof("Installing Argo Tunnel Windows service")
 | 
			
		||||
	log.Info().Msgf("Installing Argo Tunnel Windows service")
 | 
			
		||||
	exepath, err := os.Executable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot find path name that start the process")
 | 
			
		||||
		log.Error().Msgf("Cannot find path name that start the process")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	m, err := mgr.Connect()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot establish a connection to the service control manager: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot establish a connection to the service control manager: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer m.Disconnect()
 | 
			
		||||
	s, err := m.OpenService(windowsServiceName)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		s.Close()
 | 
			
		||||
		logger.Errorf("service %s already exists", windowsServiceName)
 | 
			
		||||
		log.Error().Msgf("service %s already exists", windowsServiceName)
 | 
			
		||||
		return fmt.Errorf("service %s already exists", windowsServiceName)
 | 
			
		||||
	}
 | 
			
		||||
	config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription}
 | 
			
		||||
	s, err = m.CreateService(windowsServiceName, exepath, config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot install service %s", windowsServiceName)
 | 
			
		||||
		log.Error().Msgf("Cannot install service %s", windowsServiceName)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer s.Close()
 | 
			
		||||
	logger.Infof("Argo Tunnel agent service is installed")
 | 
			
		||||
	log.Info().Msgf("Argo Tunnel agent service is installed")
 | 
			
		||||
	err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.Delete()
 | 
			
		||||
		logger.Errorf("Cannot install event logger: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot install event logger: %s", err)
 | 
			
		||||
		return fmt.Errorf("SetupEventLogSource() failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = configRecoveryOption(s.Handle)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot set service recovery actions: %s", err)
 | 
			
		||||
		logger.Infof("See %s to manually configure service recovery actions", windowsServiceUrl)
 | 
			
		||||
		log.Error().Msgf("Cannot set service recovery actions: %s", err)
 | 
			
		||||
		log.Info().Msgf("See %s to manually configure service recovery actions", windowsServiceUrl)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func uninstallWindowsService(c *cli.Context) error {
 | 
			
		||||
	logger, err := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
 | 
			
		||||
 | 
			
		||||
	logger.Infof("Uninstalling Argo Tunnel Windows Service")
 | 
			
		||||
	log.Info().Msgf("Uninstalling Argo Tunnel Windows Service")
 | 
			
		||||
	m, err := mgr.Connect()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot establish a connection to the service control manager")
 | 
			
		||||
		log.Error().Msgf("Cannot establish a connection to the service control manager")
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer m.Disconnect()
 | 
			
		||||
	s, err := m.OpenService(windowsServiceName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("service %s is not installed", windowsServiceName)
 | 
			
		||||
		log.Error().Msgf("service %s is not installed", windowsServiceName)
 | 
			
		||||
		return fmt.Errorf("service %s is not installed", windowsServiceName)
 | 
			
		||||
	}
 | 
			
		||||
	defer s.Close()
 | 
			
		||||
	err = s.Delete()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot delete service %s", windowsServiceName)
 | 
			
		||||
		log.Error().Msgf("Cannot delete service %s", windowsServiceName)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	logger.Infof("Argo Tunnel agent service is uninstalled")
 | 
			
		||||
	log.Info().Msgf("Argo Tunnel agent service is uninstalled")
 | 
			
		||||
	err = eventlog.Remove(windowsServiceName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		logger.Errorf("Cannot remove event logger")
 | 
			
		||||
		log.Error().Msgf("Cannot remove event logger")
 | 
			
		||||
		return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,8 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/gobwas/ws/wsutil"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -22,14 +22,14 @@ var (
 | 
			
		|||
		OriginClient: &mockOriginClient{},
 | 
			
		||||
		GracePeriod:  time.Millisecond * 100,
 | 
			
		||||
	}
 | 
			
		||||
	testLogger, _ = logger.New()
 | 
			
		||||
	log           = zerolog.Nop()
 | 
			
		||||
	testOriginURL = &url.URL{
 | 
			
		||||
		Scheme: "https",
 | 
			
		||||
		Host:   "connectiontest.argotunnel.com",
 | 
			
		||||
	}
 | 
			
		||||
	testTunnelEventChan = make(chan Event)
 | 
			
		||||
	testObserver        = &Observer{
 | 
			
		||||
		testLogger,
 | 
			
		||||
		&log,
 | 
			
		||||
		m,
 | 
			
		||||
		[]chan Event{testTunnelEventChan},
 | 
			
		||||
		false,
 | 
			
		||||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ func wsEndpoint(w ResponseWriter, r *http.Request) error {
 | 
			
		|||
	resp := &http.Response{
 | 
			
		||||
		StatusCode: http.StatusSwitchingProtocols,
 | 
			
		||||
	}
 | 
			
		||||
	w.WriteRespHeaders(resp)
 | 
			
		||||
	_ = w.WriteRespHeaders(resp)
 | 
			
		||||
	clientReader := nowriter{r.Body}
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,8 +102,8 @@ func originRespEndpoint(w ResponseWriter, status int, data []byte) {
 | 
			
		|||
	resp := &http.Response{
 | 
			
		||||
		StatusCode: status,
 | 
			
		||||
	}
 | 
			
		||||
	w.WriteRespHeaders(resp)
 | 
			
		||||
	w.Write(data)
 | 
			
		||||
	_ = w.WriteRespHeaders(resp)
 | 
			
		||||
	_, _ = w.Write(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type mockConnectedFuse struct{}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,11 +65,11 @@ func (e muxerShutdownError) Error() string {
 | 
			
		|||
func isHandshakeErrRecoverable(err error, connIndex uint8, observer *Observer) bool {
 | 
			
		||||
	switch err.(type) {
 | 
			
		||||
	case edgediscovery.DialError:
 | 
			
		||||
		observer.Errorf("Connection %d unable to dial edge: %s", connIndex, err)
 | 
			
		||||
		observer.log.Error().Msgf("Connection %d unable to dial edge: %s", connIndex, err)
 | 
			
		||||
	case h2mux.MuxerHandshakeError:
 | 
			
		||||
		observer.Errorf("Connection %d handshake with edge server failed: %s", connIndex, err)
 | 
			
		||||
		observer.log.Error().Msgf("Connection %d handshake with edge server failed: %s", connIndex, err)
 | 
			
		||||
	default:
 | 
			
		||||
		observer.Errorf("Connection %d failed: %s", connIndex, err)
 | 
			
		||||
		observer.log.Error().Msgf("Connection %d failed: %s", connIndex, err)
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,11 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/websocket"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/sync/errgroup"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -37,14 +38,14 @@ type MuxerConfig struct {
 | 
			
		|||
	MetricsUpdateFreq  time.Duration
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mc *MuxerConfig) H2MuxerConfig(h h2mux.MuxedStreamHandler, logger logger.Service) *h2mux.MuxerConfig {
 | 
			
		||||
func (mc *MuxerConfig) H2MuxerConfig(h h2mux.MuxedStreamHandler, log *zerolog.Logger) *h2mux.MuxerConfig {
 | 
			
		||||
	return &h2mux.MuxerConfig{
 | 
			
		||||
		Timeout:            muxerTimeout,
 | 
			
		||||
		Handler:            h,
 | 
			
		||||
		IsClient:           true,
 | 
			
		||||
		HeartbeatInterval:  mc.HeartbeatInterval,
 | 
			
		||||
		MaxHeartbeats:      mc.MaxHeartbeats,
 | 
			
		||||
		Logger:             logger,
 | 
			
		||||
		Log:                log,
 | 
			
		||||
		CompressionQuality: mc.CompressionSetting,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +68,7 @@ func NewH2muxConnection(ctx context.Context,
 | 
			
		|||
 | 
			
		||||
	// Establish a muxed connection with the edge
 | 
			
		||||
	// Client mux handshake with agent server
 | 
			
		||||
	muxer, err := h2mux.Handshake(edgeConn, edgeConn, *muxerConfig.H2MuxerConfig(h, observer), h2mux.ActiveStreams)
 | 
			
		||||
	muxer, err := h2mux.Handshake(edgeConn, edgeConn, *muxerConfig.H2MuxerConfig(h, observer.log), h2mux.ActiveStreams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		recoverable := isHandshakeErrRecoverable(err, connIndex, observer)
 | 
			
		||||
		return nil, err, recoverable
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +88,7 @@ func (h *h2muxConnection) ServeNamedTunnel(ctx context.Context, namedTunnel *Nam
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		rpcClient := newRegistrationRPCClient(ctx, stream, h.observer)
 | 
			
		||||
		rpcClient := newRegistrationRPCClient(ctx, stream, h.observer.log)
 | 
			
		||||
		defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
		if err = rpcClient.RegisterConnection(serveCtx, namedTunnel, connOptions, h.connIndex, h.observer); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +123,7 @@ func (h *h2muxConnection) ServeClassicTunnel(ctx context.Context, classicTunnel
 | 
			
		|||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			// log errors and proceed to RegisterTunnel
 | 
			
		||||
			h.observer.Errorf("Couldn't reconnect connection %d. Reregistering it instead. Error was: %v", h.connIndex, err)
 | 
			
		||||
			h.observer.log.Error().Msgf("Couldn't reconnect connection %d. Reregistering it instead. Error was: %v", h.connIndex, err)
 | 
			
		||||
		}
 | 
			
		||||
		return h.registerTunnel(ctx, credentialManager, classicTunnel, registrationOptions)
 | 
			
		||||
	})
 | 
			
		||||
| 
						 | 
				
			
			@ -212,9 +213,9 @@ func (rp *h2muxRespWriter) WriteRespHeaders(resp *http.Response) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (rp *h2muxRespWriter) WriteErrorResponse() {
 | 
			
		||||
	rp.WriteHeaders([]h2mux.Header{
 | 
			
		||||
	_ = rp.WriteHeaders([]h2mux.Header{
 | 
			
		||||
		{Name: ":status", Value: "502"},
 | 
			
		||||
		{Name: ResponseMetaHeaderField, Value: responseMetaHeaderCfd},
 | 
			
		||||
	})
 | 
			
		||||
	rp.Write([]byte("502 Bad Gateway"))
 | 
			
		||||
	_, _ = rp.Write([]byte("502 Bad Gateway"))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ func newH2MuxConnection(ctx context.Context, t require.TestingT) (*h2muxConnecti
 | 
			
		|||
	edgeMuxChan := make(chan *h2mux.Muxer)
 | 
			
		||||
	go func() {
 | 
			
		||||
		edgeMuxConfig := h2mux.MuxerConfig{
 | 
			
		||||
			Logger: testObserver,
 | 
			
		||||
			Log: testObserver.log,
 | 
			
		||||
		}
 | 
			
		||||
		edgeMux, err := h2mux.Handshake(edgeConn, edgeConn, edgeMuxConfig, h2mux.ActiveStreams)
 | 
			
		||||
		require.NoError(t, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +85,7 @@ func TestServeStreamHTTP(t *testing.T) {
 | 
			
		|||
	wg.Add(2)
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer wg.Done()
 | 
			
		||||
		edgeMux.Serve(ctx)
 | 
			
		||||
		_ = edgeMux.Serve(ctx)
 | 
			
		||||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer wg.Done()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,9 +11,9 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/net/http2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ type http2Connection struct {
 | 
			
		|||
	connIndex    uint8
 | 
			
		||||
	wg           *sync.WaitGroup
 | 
			
		||||
	// newRPCClientFunc allows us to mock RPCs during testing
 | 
			
		||||
	newRPCClientFunc func(context.Context, io.ReadWriteCloser, logger.Service) NamedTunnelRPCClient
 | 
			
		||||
	newRPCClientFunc func(context.Context, io.ReadWriteCloser, *zerolog.Logger) NamedTunnelRPCClient
 | 
			
		||||
	connectedFuse    ConnectedFuse
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ func (c *http2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	}
 | 
			
		||||
	flusher, isFlusher := w.(http.Flusher)
 | 
			
		||||
	if !isFlusher {
 | 
			
		||||
		c.observer.Errorf("%T doesn't implement http.Flusher", w)
 | 
			
		||||
		c.observer.log.Error().Msgf("%T doesn't implement http.Flusher", w)
 | 
			
		||||
		respWriter.WriteErrorResponse()
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +112,7 @@ func (c *http2Connection) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func (c *http2Connection) serveControlStream(ctx context.Context, respWriter *http2RespWriter) error {
 | 
			
		||||
	rpcClient := c.newRPCClientFunc(ctx, respWriter, c.observer)
 | 
			
		||||
	rpcClient := c.newRPCClientFunc(ctx, respWriter, c.observer.log)
 | 
			
		||||
	defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
	if err := rpcClient.RegisterConnection(ctx, c.namedTunnel, c.connOptions, c.connIndex, c.observer); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,10 +12,11 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
 | 
			
		||||
	"github.com/gobwas/ws/wsutil"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"golang.org/x/net/http2"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +137,7 @@ type mockRPCClientFactory struct {
 | 
			
		|||
	unregistered chan struct{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mf *mockRPCClientFactory) newMockRPCClient(context.Context, io.ReadWriteCloser, logger.Service) NamedTunnelRPCClient {
 | 
			
		||||
func (mf *mockRPCClientFactory) newMockRPCClient(context.Context, io.ReadWriteCloser, *zerolog.Logger) NamedTunnelRPCClient {
 | 
			
		||||
	return mockNamedTunnelRPCClient{
 | 
			
		||||
		registered:   mf.registered,
 | 
			
		||||
		unregistered: mf.unregistered,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,20 +5,21 @@ import (
 | 
			
		|||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Observer struct {
 | 
			
		||||
	logger.Service
 | 
			
		||||
	log              *zerolog.Logger
 | 
			
		||||
	metrics          *tunnelMetrics
 | 
			
		||||
	tunnelEventChans []chan Event
 | 
			
		||||
	uiEnabled        bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewObserver(logger logger.Service, tunnelEventChans []chan Event, uiEnabled bool) *Observer {
 | 
			
		||||
func NewObserver(log *zerolog.Logger, tunnelEventChans []chan Event, uiEnabled bool) *Observer {
 | 
			
		||||
	return &Observer{
 | 
			
		||||
		logger,
 | 
			
		||||
		log,
 | 
			
		||||
		newTunnelMetrics(),
 | 
			
		||||
		tunnelEventChans,
 | 
			
		||||
		uiEnabled,
 | 
			
		||||
| 
						 | 
				
			
			@ -27,7 +28,7 @@ func NewObserver(logger logger.Service, tunnelEventChans []chan Event, uiEnabled
 | 
			
		|||
 | 
			
		||||
func (o *Observer) logServerInfo(connIndex uint8, location, msg string) {
 | 
			
		||||
	o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location})
 | 
			
		||||
	o.Infof(msg)
 | 
			
		||||
	o.log.Info().Msgf(msg)
 | 
			
		||||
	o.metrics.registerServerLocation(uint8ToString(connIndex), location)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -36,10 +37,10 @@ func (o *Observer) logTrialHostname(registration *tunnelpogs.TunnelRegistration)
 | 
			
		|||
	if !o.uiEnabled {
 | 
			
		||||
		if registrationURL, err := url.Parse(registration.Url); err == nil {
 | 
			
		||||
			for _, line := range asciiBox(trialZoneMsg(registrationURL.String()), 2) {
 | 
			
		||||
				o.Info(line)
 | 
			
		||||
				o.log.Info().Msg(line)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			o.Error("Failed to connect tunnel, please try again.")
 | 
			
		||||
			o.log.Error().Msg("Failed to connect tunnel, please try again.")
 | 
			
		||||
			return fmt.Errorf("empty URL in response from Cloudflare edge")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ type autoProtocolSelector struct {
 | 
			
		|||
	fetchFunc      PercentageFetcher
 | 
			
		||||
	refreshAfter   time.Time
 | 
			
		||||
	ttl            time.Duration
 | 
			
		||||
	logger         logger.Service
 | 
			
		||||
	log            *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newAutoProtocolSelector(
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +97,7 @@ func newAutoProtocolSelector(
 | 
			
		|||
	switchThrehold int32,
 | 
			
		||||
	fetchFunc PercentageFetcher,
 | 
			
		||||
	ttl time.Duration,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) *autoProtocolSelector {
 | 
			
		||||
	return &autoProtocolSelector{
 | 
			
		||||
		current:        current,
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ func newAutoProtocolSelector(
 | 
			
		|||
		fetchFunc:      fetchFunc,
 | 
			
		||||
		refreshAfter:   time.Now().Add(ttl),
 | 
			
		||||
		ttl:            ttl,
 | 
			
		||||
		logger:         logger,
 | 
			
		||||
		log:            log,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ func (s *autoProtocolSelector) Current() Protocol {
 | 
			
		|||
 | 
			
		||||
	percentage, err := s.fetchFunc()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to refresh protocol, err: %v", err)
 | 
			
		||||
		s.log.Error().Msgf("Failed to refresh protocol, err: %v", err)
 | 
			
		||||
		return s.current
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,13 @@ func (s *autoProtocolSelector) Fallback() (Protocol, bool) {
 | 
			
		|||
 | 
			
		||||
type PercentageFetcher func() (int32, error)
 | 
			
		||||
 | 
			
		||||
func NewProtocolSelector(protocolFlag string, namedTunnel *NamedTunnelConfig, fetchFunc PercentageFetcher, ttl time.Duration, logger logger.Service) (ProtocolSelector, error) {
 | 
			
		||||
func NewProtocolSelector(
 | 
			
		||||
	protocolFlag string,
 | 
			
		||||
	namedTunnel *NamedTunnelConfig,
 | 
			
		||||
	fetchFunc PercentageFetcher,
 | 
			
		||||
	ttl time.Duration,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) (ProtocolSelector, error) {
 | 
			
		||||
	if namedTunnel == nil {
 | 
			
		||||
		return &staticProtocolSelector{
 | 
			
		||||
			current: H2mux,
 | 
			
		||||
| 
						 | 
				
			
			@ -157,9 +163,9 @@ func NewProtocolSelector(protocolFlag string, namedTunnel *NamedTunnelConfig, fe
 | 
			
		|||
	}
 | 
			
		||||
	if protocolFlag == HTTP2.String() {
 | 
			
		||||
		if http2Percentage < 0 {
 | 
			
		||||
			return newAutoProtocolSelector(H2mux, explicitHTTP2FallbackThreshold, fetchFunc, ttl, logger), nil
 | 
			
		||||
			return newAutoProtocolSelector(H2mux, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
 | 
			
		||||
		}
 | 
			
		||||
		return newAutoProtocolSelector(HTTP2, explicitHTTP2FallbackThreshold, fetchFunc, ttl, logger), nil
 | 
			
		||||
		return newAutoProtocolSelector(HTTP2, explicitHTTP2FallbackThreshold, fetchFunc, ttl, log), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if protocolFlag != autoSelectFlag {
 | 
			
		||||
| 
						 | 
				
			
			@ -167,13 +173,13 @@ func NewProtocolSelector(protocolFlag string, namedTunnel *NamedTunnelConfig, fe
 | 
			
		|||
	}
 | 
			
		||||
	threshold := switchThreshold(namedTunnel.Credentials.AccountTag)
 | 
			
		||||
	if threshold < http2Percentage {
 | 
			
		||||
		return newAutoProtocolSelector(HTTP2, threshold, fetchFunc, ttl, logger), nil
 | 
			
		||||
		return newAutoProtocolSelector(HTTP2, threshold, fetchFunc, ttl, log), nil
 | 
			
		||||
	}
 | 
			
		||||
	return newAutoProtocolSelector(H2mux, threshold, fetchFunc, ttl, logger), nil
 | 
			
		||||
	return newAutoProtocolSelector(H2mux, threshold, fetchFunc, ttl, log), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func switchThreshold(accountTag string) int32 {
 | 
			
		||||
	h := fnv.New32a()
 | 
			
		||||
	h.Write([]byte(accountTag))
 | 
			
		||||
	_, _ = h.Write([]byte(accountTag))
 | 
			
		||||
	return int32(h.Sum32() % 100)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,6 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -130,9 +129,9 @@ func TestNewProtocolSelector(t *testing.T) {
 | 
			
		|||
			wantErr:           true,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	logger, _ := logger.New()
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		selector, err := NewProtocolSelector(test.protocol, test.namedTunnelConfig, test.fetchFunc, testNoTTL, logger)
 | 
			
		||||
		selector, err := NewProtocolSelector(test.protocol, test.namedTunnelConfig, test.fetchFunc, testNoTTL, &log)
 | 
			
		||||
		if test.wantErr {
 | 
			
		||||
			assert.Error(t, err, fmt.Sprintf("test %s failed", test.name))
 | 
			
		||||
		} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -148,9 +147,8 @@ func TestNewProtocolSelector(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestAutoProtocolSelectorRefresh(t *testing.T) {
 | 
			
		||||
	logger, _ := logger.New()
 | 
			
		||||
	fetcher := dynamicMockFetcher{}
 | 
			
		||||
	selector, err := NewProtocolSelector("auto", testNamedTunnelConfig, fetcher.fetch(), testNoTTL, logger)
 | 
			
		||||
	selector, err := NewProtocolSelector("auto", testNamedTunnelConfig, fetcher.fetch(), testNoTTL, &log)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, H2mux, selector.Current())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -178,9 +176,8 @@ func TestAutoProtocolSelectorRefresh(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestHTTP2ProtocolSelectorRefresh(t *testing.T) {
 | 
			
		||||
	logger, _ := logger.New()
 | 
			
		||||
	fetcher := dynamicMockFetcher{}
 | 
			
		||||
	selector, err := NewProtocolSelector("http2", testNamedTunnelConfig, fetcher.fetch(), testNoTTL, logger)
 | 
			
		||||
	selector, err := NewProtocolSelector("http2", testNamedTunnelConfig, fetcher.fetch(), testNoTTL, &log)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, HTTP2, selector.Current())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -208,9 +205,8 @@ func TestHTTP2ProtocolSelectorRefresh(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestProtocolSelectorRefreshTTL(t *testing.T) {
 | 
			
		||||
	logger, _ := logger.New()
 | 
			
		||||
	fetcher := dynamicMockFetcher{percentage: 100}
 | 
			
		||||
	selector, err := NewProtocolSelector("auto", testNamedTunnelConfig, fetcher.fetch(), time.Hour, logger)
 | 
			
		||||
	selector, err := NewProtocolSelector("auto", testNamedTunnelConfig, fetcher.fetch(), time.Hour, &log)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, HTTP2, selector.Current())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,10 @@ import (
 | 
			
		|||
	"io"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunnelrpc"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"zombiezen.com/go/capnproto2/rpc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -22,12 +23,12 @@ type tunnelServerClient struct {
 | 
			
		|||
func NewTunnelServerClient(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	stream io.ReadWriteCloser,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) *tunnelServerClient {
 | 
			
		||||
	transport := tunnelrpc.NewTransportLogger(logger, rpc.StreamTransport(stream))
 | 
			
		||||
	transport := tunnelrpc.NewTransportLogger(log, rpc.StreamTransport(stream))
 | 
			
		||||
	conn := rpc.NewConn(
 | 
			
		||||
		transport,
 | 
			
		||||
		tunnelrpc.ConnLog(logger),
 | 
			
		||||
		tunnelrpc.ConnLog(log),
 | 
			
		||||
	)
 | 
			
		||||
	registrationClient := tunnelpogs.RegistrationServer_PogsClient{Client: conn.Bootstrap(ctx), Conn: conn}
 | 
			
		||||
	return &tunnelServerClient{
 | 
			
		||||
| 
						 | 
				
			
			@ -46,8 +47,8 @@ func (tsc *tunnelServerClient) Authenticate(ctx context.Context, classicTunnel *
 | 
			
		|||
 | 
			
		||||
func (tsc *tunnelServerClient) Close() {
 | 
			
		||||
	// Closing the client will also close the connection
 | 
			
		||||
	tsc.client.Close()
 | 
			
		||||
	tsc.transport.Close()
 | 
			
		||||
	_ = tsc.client.Close()
 | 
			
		||||
	_ = tsc.transport.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NamedTunnelRPCClient interface {
 | 
			
		||||
| 
						 | 
				
			
			@ -70,12 +71,12 @@ type registrationServerClient struct {
 | 
			
		|||
func newRegistrationRPCClient(
 | 
			
		||||
	ctx context.Context,
 | 
			
		||||
	stream io.ReadWriteCloser,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) NamedTunnelRPCClient {
 | 
			
		||||
	transport := tunnelrpc.NewTransportLogger(logger, rpc.StreamTransport(stream))
 | 
			
		||||
	transport := tunnelrpc.NewTransportLogger(log, rpc.StreamTransport(stream))
 | 
			
		||||
	conn := rpc.NewConn(
 | 
			
		||||
		transport,
 | 
			
		||||
		tunnelrpc.ConnLog(logger),
 | 
			
		||||
		tunnelrpc.ConnLog(log),
 | 
			
		||||
	)
 | 
			
		||||
	return ®istrationServerClient{
 | 
			
		||||
		client:    tunnelpogs.RegistrationServer_PogsClient{Client: conn.Bootstrap(ctx), Conn: conn},
 | 
			
		||||
| 
						 | 
				
			
			@ -117,14 +118,14 @@ func (rsc *registrationServerClient) RegisterConnection(
 | 
			
		|||
func (rsc *registrationServerClient) GracefulShutdown(ctx context.Context, gracePeriod time.Duration) {
 | 
			
		||||
	ctx, cancel := context.WithTimeout(ctx, gracePeriod)
 | 
			
		||||
	defer cancel()
 | 
			
		||||
	rsc.client.UnregisterConnection(ctx)
 | 
			
		||||
	_ = rsc.client.UnregisterConnection(ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rsc *registrationServerClient) Close() {
 | 
			
		||||
	// Closing the client will also close the connection
 | 
			
		||||
	rsc.client.Close()
 | 
			
		||||
	_ = rsc.client.Close()
 | 
			
		||||
	// Closing the transport also closes the stream
 | 
			
		||||
	rsc.transport.Close()
 | 
			
		||||
	_ = rsc.transport.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rpcName string
 | 
			
		||||
| 
						 | 
				
			
			@ -143,10 +144,10 @@ func (h *h2muxConnection) registerTunnel(ctx context.Context, credentialSetter C
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	rpcClient := NewTunnelServerClient(ctx, stream, h.observer)
 | 
			
		||||
	rpcClient := NewTunnelServerClient(ctx, stream, h.observer.log)
 | 
			
		||||
	defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
	h.logServerInfo(ctx, rpcClient)
 | 
			
		||||
	_ = h.logServerInfo(ctx, rpcClient)
 | 
			
		||||
	registration := rpcClient.client.RegisterTunnel(
 | 
			
		||||
		ctx,
 | 
			
		||||
		classicTunnel.OriginCert,
 | 
			
		||||
| 
						 | 
				
			
			@ -178,12 +179,12 @@ func (h *h2muxConnection) processRegistrationSuccess(
 | 
			
		|||
	credentialManager CredentialManager, classicTunnel *ClassicTunnelConfig,
 | 
			
		||||
) error {
 | 
			
		||||
	for _, logLine := range registration.LogLines {
 | 
			
		||||
		h.observer.Info(logLine)
 | 
			
		||||
		h.observer.log.Info().Msg(logLine)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if registration.TunnelID != "" {
 | 
			
		||||
		h.observer.metrics.tunnelsHA.AddTunnelID(h.connIndex, registration.TunnelID)
 | 
			
		||||
		h.observer.Infof("Each HA connection's tunnel IDs: %v", h.observer.metrics.tunnelsHA.String())
 | 
			
		||||
		h.observer.log.Info().Msgf("Each HA connection's tunnel IDs: %v", h.observer.metrics.tunnelsHA.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Print out the user's trial zone URL in a nice box (if they requested and got one and UI flag is not set)
 | 
			
		||||
| 
						 | 
				
			
			@ -197,7 +198,7 @@ func (h *h2muxConnection) processRegistrationSuccess(
 | 
			
		|||
	credentialManager.SetConnDigest(h.connIndex, registration.ConnDigest)
 | 
			
		||||
	h.observer.metrics.userHostnamesCounts.WithLabelValues(registration.Url).Inc()
 | 
			
		||||
 | 
			
		||||
	h.observer.Infof("Route propagating, it may take up to 1 minute for your new route to become functional")
 | 
			
		||||
	h.observer.log.Info().Msgf("Route propagating, it may take up to 1 minute for your new route to become functional")
 | 
			
		||||
	h.observer.metrics.regSuccess.WithLabelValues(string(name)).Inc()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -228,15 +229,15 @@ func (h *h2muxConnection) reconnectTunnel(ctx context.Context, credentialManager
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	h.observer.Debug("initiating RPC stream to reconnect")
 | 
			
		||||
	h.observer.log.Debug().Msg("initiating RPC stream to reconnect")
 | 
			
		||||
	stream, err := h.newRPCStream(ctx, register)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	rpcClient := NewTunnelServerClient(ctx, stream, h.observer)
 | 
			
		||||
	rpcClient := NewTunnelServerClient(ctx, stream, h.observer.log)
 | 
			
		||||
	defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
	h.logServerInfo(ctx, rpcClient)
 | 
			
		||||
	_ = h.logServerInfo(ctx, rpcClient)
 | 
			
		||||
	registration := rpcClient.client.ReconnectTunnel(
 | 
			
		||||
		ctx,
 | 
			
		||||
		token,
 | 
			
		||||
| 
						 | 
				
			
			@ -259,15 +260,15 @@ func (h *h2muxConnection) logServerInfo(ctx context.Context, rpcClient *tunnelSe
 | 
			
		|||
	})
 | 
			
		||||
	serverInfoMessage, err := serverInfoPromise.Result().Struct()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		h.observer.Errorf("Failed to retrieve server information: %s", err)
 | 
			
		||||
		h.observer.log.Error().Msgf("Failed to retrieve server information: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	serverInfo, err := tunnelpogs.UnmarshalServerInfo(serverInfoMessage)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		h.observer.Errorf("Failed to retrieve server information: %s", err)
 | 
			
		||||
		h.observer.log.Error().Msgf("Failed to retrieve server information: %s", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, fmt.Sprintf("Connnection %d connected to %s", h.connIndex, serverInfo.LocationName))
 | 
			
		||||
	h.observer.logServerInfo(h.connIndex, serverInfo.LocationName, fmt.Sprintf("Connection %d connected to %s", h.connIndex, serverInfo.LocationName))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -281,15 +282,15 @@ func (h *h2muxConnection) unregister(isNamedTunnel bool) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	if isNamedTunnel {
 | 
			
		||||
		rpcClient := newRegistrationRPCClient(unregisterCtx, stream, h.observer)
 | 
			
		||||
		rpcClient := newRegistrationRPCClient(unregisterCtx, stream, h.observer.log)
 | 
			
		||||
		defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
		rpcClient.GracefulShutdown(unregisterCtx, h.config.GracePeriod)
 | 
			
		||||
	} else {
 | 
			
		||||
		rpcClient := NewTunnelServerClient(unregisterCtx, stream, h.observer)
 | 
			
		||||
		rpcClient := NewTunnelServerClient(unregisterCtx, stream, h.observer.log)
 | 
			
		||||
		defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
		// gracePeriod is encoded in int64 using capnproto
 | 
			
		||||
		rpcClient.client.UnregisterTunnel(unregisterCtx, h.config.GracePeriod.Nanoseconds())
 | 
			
		||||
		_ = rpcClient.client.UnregisterTunnel(unregisterCtx, h.config.GracePeriod.Nanoseconds())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,20 +8,21 @@ import (
 | 
			
		|||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/hello"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/validation"
 | 
			
		||||
	"github.com/gorilla/mux"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Proxy is an HTTP server that proxies requests to a Client.
 | 
			
		||||
type Proxy struct {
 | 
			
		||||
	client          Client
 | 
			
		||||
	accessValidator *validation.Access
 | 
			
		||||
	logger          logger.Service
 | 
			
		||||
	log             *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewInsecureProxy creates a Proxy that talks to a Client at an origin.
 | 
			
		||||
| 
						 | 
				
			
			@ -43,12 +44,9 @@ func NewInsecureProxy(ctx context.Context, origin string) (*Proxy, error) {
 | 
			
		|||
		return nil, errors.Wrap(err, "could not connect to the database")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger, err := logger.New() // TODO: Does not obey log configuration
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "error setting up logger")
 | 
			
		||||
	}
 | 
			
		||||
	log := zerolog.New(os.Stderr).With().Logger() // TODO: Does not obey log configuration
 | 
			
		||||
 | 
			
		||||
	return &Proxy{client, nil, logger}, nil
 | 
			
		||||
	return &Proxy{client, nil, &log}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSecureProxy creates a Proxy that talks to a Client at an origin.
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +94,7 @@ func (proxy *Proxy) IsAllowed(r *http.Request, verbose ...bool) bool {
 | 
			
		|||
	// of either a misconfiguration of the CLI or a massive failure of upstream systems.
 | 
			
		||||
	if len(verbose) > 0 {
 | 
			
		||||
		cfRay := proxy.getRayHeader(r)
 | 
			
		||||
		proxy.logger.Infof("dbproxy: Failed JWT authentication: cf-ray: %s %s", cfRay, err)
 | 
			
		||||
		proxy.log.Info().Msgf("dbproxy: Failed JWT authentication: cf-ray: %s %s", cfRay, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false
 | 
			
		||||
| 
						 | 
				
			
			@ -151,8 +149,8 @@ func (proxy *Proxy) httpListen(ctx context.Context, listener net.Listener) error
 | 
			
		|||
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-ctx.Done()
 | 
			
		||||
		httpServer.Close()
 | 
			
		||||
		listener.Close()
 | 
			
		||||
		_ = httpServer.Close()
 | 
			
		||||
		_ = listener.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return httpServer.Serve(listener)
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +239,7 @@ func (proxy *Proxy) httpRespondErr(w http.ResponseWriter, r *http.Request, defau
 | 
			
		|||
	proxy.httpRespond(w, r, status, err.Error())
 | 
			
		||||
	if len(err.Error()) > 0 {
 | 
			
		||||
		cfRay := proxy.getRayHeader(r)
 | 
			
		||||
		proxy.logger.Infof("dbproxy: Database proxy error: cf-ray: %s %s", cfRay, err)
 | 
			
		||||
		proxy.log.Info().Msgf("dbproxy: Database proxy error: cf-ray: %s %s", cfRay, err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ func NewSQLClient(ctx context.Context, originURL *url.URL) (Client, error) {
 | 
			
		|||
	// Closes the driver, will occur when the context finishes.
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-ctx.Done()
 | 
			
		||||
		driver.Close()
 | 
			
		||||
		_ = driver.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return &SQLClient{driver.DriverName(), driver}, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +260,7 @@ func sqlRows(rows *sql.Rows) ([]map[string]interface{}, error) {
 | 
			
		|||
		for i := range columns {
 | 
			
		||||
			pointers[i] = &values[i]
 | 
			
		||||
		}
 | 
			
		||||
		rows.Scan(pointers...)
 | 
			
		||||
		_ = rows.Scan(pointers...)
 | 
			
		||||
 | 
			
		||||
		// Convert a row, an array of values, into an object where
 | 
			
		||||
		// each key is the name of its respective column.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,8 @@ import (
 | 
			
		|||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -58,15 +58,15 @@ var friendlyDNSErrorLines = []string{
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// EdgeDiscovery implements HA service discovery lookup.
 | 
			
		||||
func edgeDiscovery(logger logger.Service) ([][]*net.TCPAddr, error) {
 | 
			
		||||
func edgeDiscovery(log *zerolog.Logger) ([][]*net.TCPAddr, error) {
 | 
			
		||||
	_, addrs, err := netLookupSRV(srvService, srvProto, srvName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		_, fallbackAddrs, fallbackErr := fallbackLookupSRV(srvService, srvProto, srvName)
 | 
			
		||||
		if fallbackErr != nil || len(fallbackAddrs) == 0 {
 | 
			
		||||
			// use the original DNS error `err` in messages, not `fallbackErr`
 | 
			
		||||
			logger.Errorf("Error looking up Cloudflare edge IPs: the DNS query failed: %s", err)
 | 
			
		||||
			log.Error().Msgf("Error looking up Cloudflare edge IPs: the DNS query failed: %s", err)
 | 
			
		||||
			for _, s := range friendlyDNSErrorLines {
 | 
			
		||||
				logger.Error(s)
 | 
			
		||||
				log.Error().Msg(s)
 | 
			
		||||
			}
 | 
			
		||||
			return nil, errors.Wrapf(err, "Could not lookup srv records on _%v._%v.%v", srvService, srvProto, srvName)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -122,11 +122,11 @@ func resolveSRVToTCP(srv *net.SRV) ([]*net.TCPAddr, error) {
 | 
			
		|||
 | 
			
		||||
// ResolveAddrs resolves TCP address given a list of addresses. Address can be a hostname, however, it will return at most one
 | 
			
		||||
// of the hostname's IP addresses.
 | 
			
		||||
func ResolveAddrs(addrs []string, logger logger.Service) (resolved []*net.TCPAddr) {
 | 
			
		||||
func ResolveAddrs(addrs []string, log *zerolog.Logger) (resolved []*net.TCPAddr) {
 | 
			
		||||
	for _, addr := range addrs {
 | 
			
		||||
		tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("Failed to resolve %s, err: %v", addr, err)
 | 
			
		||||
			log.Error().Msgf("Failed to resolve %s, err: %v", addr, err)
 | 
			
		||||
		} else {
 | 
			
		||||
			resolved = append(resolved, tcpAddr)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ package allregions
 | 
			
		|||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +19,8 @@ func TestEdgeDiscovery(t *testing.T) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	addrLists, err := edgeDiscovery(l)
 | 
			
		||||
	l := zerolog.Nop()
 | 
			
		||||
	addrLists, err := edgeDiscovery(&l)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	actualAddrSet := map[string]bool{}
 | 
			
		||||
	for _, addrs := range addrLists {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,7 +57,6 @@ func (r Region) GetUnusedIP(excluding *net.TCPAddr) *net.TCPAddr {
 | 
			
		|||
// Use the address, assigning it to a proxy connection.
 | 
			
		||||
func (r Region) Use(addr *net.TCPAddr, connID int) {
 | 
			
		||||
	if addr == nil {
 | 
			
		||||
		//logrus.Errorf("Attempted to use nil address for connection %d", connID)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	r.connFor[addr] = InUse(connID)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regions stores Cloudflare edge network IPs, partitioned into two regions.
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +19,8 @@ type Regions struct {
 | 
			
		|||
// ------------------------------------
 | 
			
		||||
 | 
			
		||||
// ResolveEdge resolves the Cloudflare edge, returning all regions discovered.
 | 
			
		||||
func ResolveEdge(logger logger.Service) (*Regions, error) {
 | 
			
		||||
	addrLists, err := edgeDiscovery(logger)
 | 
			
		||||
func ResolveEdge(log *zerolog.Logger) (*Regions, error) {
 | 
			
		||||
	addrLists, err := edgeDiscovery(log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -35,8 +35,8 @@ func ResolveEdge(logger logger.Service) (*Regions, error) {
 | 
			
		|||
 | 
			
		||||
// StaticEdge creates a list of edge addresses from the list of hostnames.
 | 
			
		||||
// Mainly used for testing connectivity.
 | 
			
		||||
func StaticEdge(hostnames []string, logger logger.Service) (*Regions, error) {
 | 
			
		||||
	resolved := ResolveAddrs(hostnames, logger)
 | 
			
		||||
func StaticEdge(hostnames []string, log *zerolog.Logger) (*Regions, error) {
 | 
			
		||||
	resolved := ResolveAddrs(hostnames, log)
 | 
			
		||||
	if len(resolved) == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("failed to resolve any edge address")
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/edgediscovery/allregions"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +19,7 @@ var errNoAddressesLeft = fmt.Errorf("There are no free edge addresses left")
 | 
			
		|||
type Edge struct {
 | 
			
		||||
	regions *allregions.Regions
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	logger logger.Service
 | 
			
		||||
	log *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ------------------------------------
 | 
			
		||||
| 
						 | 
				
			
			@ -28,34 +28,34 @@ type Edge struct {
 | 
			
		|||
 | 
			
		||||
// ResolveEdge runs the initial discovery of the Cloudflare edge, finding Addrs that can be allocated
 | 
			
		||||
// to connections.
 | 
			
		||||
func ResolveEdge(l logger.Service) (*Edge, error) {
 | 
			
		||||
	regions, err := allregions.ResolveEdge(l)
 | 
			
		||||
func ResolveEdge(log *zerolog.Logger) (*Edge, error) {
 | 
			
		||||
	regions, err := allregions.ResolveEdge(log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return new(Edge), err
 | 
			
		||||
	}
 | 
			
		||||
	return &Edge{
 | 
			
		||||
		logger:  l,
 | 
			
		||||
		log:     log,
 | 
			
		||||
		regions: regions,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StaticEdge creates a list of edge addresses from the list of hostnames. Mainly used for testing connectivity.
 | 
			
		||||
func StaticEdge(l logger.Service, hostnames []string) (*Edge, error) {
 | 
			
		||||
	regions, err := allregions.StaticEdge(hostnames, l)
 | 
			
		||||
func StaticEdge(log *zerolog.Logger, hostnames []string) (*Edge, error) {
 | 
			
		||||
	regions, err := allregions.StaticEdge(hostnames, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return new(Edge), err
 | 
			
		||||
	}
 | 
			
		||||
	return &Edge{
 | 
			
		||||
		logger:  l,
 | 
			
		||||
		log:     log,
 | 
			
		||||
		regions: regions,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MockEdge creates a Cloudflare Edge from arbitrary TCP addresses. Used for testing.
 | 
			
		||||
func MockEdge(l logger.Service, addrs []*net.TCPAddr) *Edge {
 | 
			
		||||
func MockEdge(log *zerolog.Logger, addrs []*net.TCPAddr) *Edge {
 | 
			
		||||
	regions := allregions.NewNoResolve(addrs)
 | 
			
		||||
	return &Edge{
 | 
			
		||||
		logger:  l,
 | 
			
		||||
		log:     log,
 | 
			
		||||
		regions: regions,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -82,17 +82,17 @@ func (ed *Edge) GetAddr(connID int) (*net.TCPAddr, error) {
 | 
			
		|||
 | 
			
		||||
	// If this connection has already used an edge addr, return it.
 | 
			
		||||
	if addr := ed.regions.AddrUsedBy(connID); addr != nil {
 | 
			
		||||
		ed.logger.Debugf("edgediscovery - GetAddr: Returning same address back to proxy connection: connID: %d", connID)
 | 
			
		||||
		ed.log.Debug().Msgf("edgediscovery - GetAddr: Returning same address back to proxy connection: connID: %d", connID)
 | 
			
		||||
		return addr, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Otherwise, give it an unused one
 | 
			
		||||
	addr := ed.regions.GetUnusedAddr(nil, connID)
 | 
			
		||||
	if addr == nil {
 | 
			
		||||
		ed.logger.Debugf("edgediscovery - GetAddr: No addresses left to give proxy connection: connID: %d", connID)
 | 
			
		||||
		ed.log.Debug().Msgf("edgediscovery - GetAddr: No addresses left to give proxy connection: connID: %d", connID)
 | 
			
		||||
		return nil, errNoAddressesLeft
 | 
			
		||||
	}
 | 
			
		||||
	ed.logger.Debugf("edgediscovery - GetAddr: Giving connection its new address %s: connID: %d", addr, connID)
 | 
			
		||||
	ed.log.Debug().Msgf("edgediscovery - GetAddr: Giving connection its new address %s: connID: %d", addr, connID)
 | 
			
		||||
	return addr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -107,11 +107,11 @@ func (ed *Edge) GetDifferentAddr(connID int) (*net.TCPAddr, error) {
 | 
			
		|||
	}
 | 
			
		||||
	addr := ed.regions.GetUnusedAddr(oldAddr, connID)
 | 
			
		||||
	if addr == nil {
 | 
			
		||||
		ed.logger.Debugf("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection: connID: %d", connID)
 | 
			
		||||
		ed.log.Debug().Msgf("edgediscovery - GetDifferentAddr: No addresses left to give proxy connection: connID: %d", connID)
 | 
			
		||||
		// note: if oldAddr were not nil, it will become available on the next iteration
 | 
			
		||||
		return nil, errNoAddressesLeft
 | 
			
		||||
	}
 | 
			
		||||
	ed.logger.Debugf("edgediscovery - GetDifferentAddr: Giving connection its new address %s: connID: %d", addr, connID)
 | 
			
		||||
	ed.log.Debug().Msgf("edgediscovery - GetDifferentAddr: Giving connection its new address %s: connID: %d", addr, connID)
 | 
			
		||||
	return addr, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,6 +127,6 @@ func (ed *Edge) AvailableAddrs() int {
 | 
			
		|||
func (ed *Edge) GiveBack(addr *net.TCPAddr) bool {
 | 
			
		||||
	ed.Lock()
 | 
			
		||||
	defer ed.Unlock()
 | 
			
		||||
	ed.logger.Debug("edgediscovery - GiveBack: Address now unused")
 | 
			
		||||
	ed.log.Debug().Msg("edgediscovery - GiveBack: Address now unused")
 | 
			
		||||
	return ed.regions.GiveBack(addr)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import (
 | 
			
		|||
	"net"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -29,11 +29,12 @@ var (
 | 
			
		|||
		Port: 8000,
 | 
			
		||||
		Zone: "",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log = zerolog.Nop()
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestGiveBack(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
 | 
			
		||||
	// Give this connection an address
 | 
			
		||||
	assert.Equal(t, 4, edge.AvailableAddrs())
 | 
			
		||||
| 
						 | 
				
			
			@ -49,10 +50,8 @@ func TestGiveBack(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	// Make an edge with a single IP
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0})
 | 
			
		||||
	tunnelConnID := 0
 | 
			
		||||
 | 
			
		||||
	// Use the IP for a tunnel
 | 
			
		||||
| 
						 | 
				
			
			@ -66,8 +65,7 @@ func TestRPCAndProxyShareSingleEdgeIP(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestGetAddrForRPC(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
 | 
			
		||||
	// Get a connection
 | 
			
		||||
	assert.Equal(t, 4, edge.AvailableAddrs())
 | 
			
		||||
| 
						 | 
				
			
			@ -84,10 +82,8 @@ func TestGetAddrForRPC(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestOnePerRegion(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	// Make an edge with only one address
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1})
 | 
			
		||||
 | 
			
		||||
	// Use the only address
 | 
			
		||||
	const connID = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -108,10 +104,8 @@ func TestOnePerRegion(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestOnlyOneAddrLeft(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	// Make an edge with only one address
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0})
 | 
			
		||||
 | 
			
		||||
	// Use the only address
 | 
			
		||||
	const connID = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -130,10 +124,8 @@ func TestOnlyOneAddrLeft(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestNoAddrsLeft(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	// Make an edge with no addresses
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{})
 | 
			
		||||
 | 
			
		||||
	_, err := edge.GetAddr(2)
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -142,8 +134,7 @@ func TestNoAddrsLeft(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestGetAddr(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
 | 
			
		||||
	// Give this connection an address
 | 
			
		||||
	const connID = 0
 | 
			
		||||
| 
						 | 
				
			
			@ -158,8 +149,7 @@ func TestGetAddr(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestGetDifferentAddr(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	edge := MockEdge(l, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
	edge := MockEdge(&log, []*net.TCPAddr{&addr0, &addr1, &addr2, &addr3})
 | 
			
		||||
 | 
			
		||||
	// Give this connection an address
 | 
			
		||||
	assert.Equal(t, 4, edge.AvailableAddrs())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							| 
						 | 
				
			
			@ -50,6 +50,7 @@ require (
 | 
			
		|||
	github.com/prometheus/client_golang v1.7.1
 | 
			
		||||
	github.com/prometheus/common v0.13.0 // indirect
 | 
			
		||||
	github.com/rivo/tview v0.0.0-20200712113419-c65badfc3d92
 | 
			
		||||
	github.com/rs/zerolog v1.20.0
 | 
			
		||||
	github.com/stretchr/testify v1.6.0
 | 
			
		||||
	github.com/urfave/cli/v2 v2.2.0
 | 
			
		||||
	github.com/xo/dburl v0.0.0-20191005012637-293c3298d6c0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										5
									
								
								go.sum
								
								
								
								
							| 
						 | 
				
			
			@ -143,6 +143,7 @@ github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 h1:7CNPV0LWRCa1FNmq
 | 
			
		|||
github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 | 
			
		||||
| 
						 | 
				
			
			@ -565,6 +566,9 @@ github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
 | 
			
		|||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 | 
			
		||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 | 
			
		||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
 | 
			
		||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
 | 
			
		||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4 h1:S9YlS71UNJIyS61OqGAmLXv3w5zclSidN+qwr80XxKs=
 | 
			
		||||
github.com/russross/blackfriday v0.0.0-20170610170232-067529f716f4/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
 | 
			
		||||
| 
						 | 
				
			
			@ -837,6 +841,7 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw
 | 
			
		|||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,11 +8,10 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/net/http2"
 | 
			
		||||
	"golang.org/x/net/http2/hpack"
 | 
			
		||||
	"golang.org/x/sync/errgroup"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +49,7 @@ type MuxerConfig struct {
 | 
			
		|||
	// The minimum number of heartbeats to send before terminating the connection.
 | 
			
		||||
	MaxHeartbeats uint64
 | 
			
		||||
	// Logger to use
 | 
			
		||||
	Logger             logger.Service
 | 
			
		||||
	Log                *zerolog.Logger
 | 
			
		||||
	CompressionQuality CompressionSetting
 | 
			
		||||
	// Initial size for HTTP2 flow control windows
 | 
			
		||||
	DefaultWindowSize uint32
 | 
			
		||||
| 
						 | 
				
			
			@ -138,10 +137,10 @@ func Handshake(
 | 
			
		|||
	handshakeSetting := http2.Setting{ID: SettingMuxerMagic, Val: MuxerMagicEdge}
 | 
			
		||||
	compressionSetting := http2.Setting{ID: SettingCompression, Val: config.CompressionQuality.toH2Setting()}
 | 
			
		||||
	if CompressionIsSupported() {
 | 
			
		||||
		config.Logger.Debug("muxer: Compression is supported")
 | 
			
		||||
		config.Log.Debug().Msg("muxer: Compression is supported")
 | 
			
		||||
		m.compressionQuality = config.CompressionQuality.getPreset()
 | 
			
		||||
	} else {
 | 
			
		||||
		config.Logger.Debug("muxer: Compression is not supported")
 | 
			
		||||
		config.Log.Debug().Msg("muxer: Compression is not supported")
 | 
			
		||||
		compressionSetting = http2.Setting{ID: SettingCompression, Val: 0}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -178,12 +177,12 @@ func Handshake(
 | 
			
		|||
	// Sanity check to enusre idelDuration is sane
 | 
			
		||||
	if idleDuration == 0 || idleDuration < defaultTimeout {
 | 
			
		||||
		idleDuration = defaultTimeout
 | 
			
		||||
		config.Logger.Infof("muxer: Minimum idle time has been adjusted to %d", defaultTimeout)
 | 
			
		||||
		config.Log.Info().Msgf("muxer: Minimum idle time has been adjusted to %d", defaultTimeout)
 | 
			
		||||
	}
 | 
			
		||||
	maxRetries := config.MaxHeartbeats
 | 
			
		||||
	if maxRetries == 0 {
 | 
			
		||||
		maxRetries = defaultRetries
 | 
			
		||||
		config.Logger.Infof("muxer: Minimum number of unacked heartbeats to send before closing the connection has been adjusted to %d", maxRetries)
 | 
			
		||||
		config.Log.Info().Msgf("muxer: Minimum number of unacked heartbeats to send before closing the connection has been adjusted to %d", maxRetries)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	compBytesBefore, compBytesAfter := NewAtomicCounter(0), NewAtomicCounter(0)
 | 
			
		||||
| 
						 | 
				
			
			@ -325,7 +324,7 @@ func (m *Muxer) Serve(ctx context.Context) error {
 | 
			
		|||
	errGroup.Go(func() error {
 | 
			
		||||
		ch := make(chan error)
 | 
			
		||||
		go func() {
 | 
			
		||||
			err := m.muxReader.run(m.config.Logger)
 | 
			
		||||
			err := m.muxReader.run(m.config.Log)
 | 
			
		||||
			m.explicitShutdown.Fuse(false)
 | 
			
		||||
			m.r.Close()
 | 
			
		||||
			m.abort()
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +345,7 @@ func (m *Muxer) Serve(ctx context.Context) error {
 | 
			
		|||
	errGroup.Go(func() error {
 | 
			
		||||
		ch := make(chan error)
 | 
			
		||||
		go func() {
 | 
			
		||||
			err := m.muxWriter.run(m.config.Logger)
 | 
			
		||||
			err := m.muxWriter.run(m.config.Log)
 | 
			
		||||
			m.explicitShutdown.Fuse(false)
 | 
			
		||||
			m.w.Close()
 | 
			
		||||
			m.abort()
 | 
			
		||||
| 
						 | 
				
			
			@ -367,7 +366,7 @@ func (m *Muxer) Serve(ctx context.Context) error {
 | 
			
		|||
	errGroup.Go(func() error {
 | 
			
		||||
		ch := make(chan error)
 | 
			
		||||
		go func() {
 | 
			
		||||
			err := m.muxMetricsUpdater.run(m.config.Logger)
 | 
			
		||||
			err := m.muxMetricsUpdater.run(m.config.Log)
 | 
			
		||||
			// don't block if parent goroutine quit early
 | 
			
		||||
			select {
 | 
			
		||||
			case ch <- err:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,10 +16,9 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"golang.org/x/sync/errgroup"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +26,8 @@ const (
 | 
			
		|||
	testHandshakeTimeout  = time.Millisecond * 1000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var log = zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
func TestMain(m *testing.M) {
 | 
			
		||||
	if os.Getenv("VERBOSE") == "1" {
 | 
			
		||||
		//TODO: set log level
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +53,7 @@ func NewDefaultMuxerPair(t assert.TestingT, testName string, f MuxedStreamFunc)
 | 
			
		|||
			Handler:                 f,
 | 
			
		||||
			IsClient:                true,
 | 
			
		||||
			Name:                    "origin",
 | 
			
		||||
			Logger:                  logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                     &log,
 | 
			
		||||
			DefaultWindowSize:       (1 << 8) - 1,
 | 
			
		||||
			MaxWindowSize:           (1 << 15) - 1,
 | 
			
		||||
			StreamWriteBufferMaxLen: 1024,
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +65,7 @@ func NewDefaultMuxerPair(t assert.TestingT, testName string, f MuxedStreamFunc)
 | 
			
		|||
			Timeout:                 testHandshakeTimeout,
 | 
			
		||||
			IsClient:                false,
 | 
			
		||||
			Name:                    "edge",
 | 
			
		||||
			Logger:                  logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                     &log,
 | 
			
		||||
			DefaultWindowSize:       (1 << 8) - 1,
 | 
			
		||||
			MaxWindowSize:           (1 << 15) - 1,
 | 
			
		||||
			StreamWriteBufferMaxLen: 1024,
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +88,7 @@ func NewCompressedMuxerPair(t assert.TestingT, testName string, quality Compress
 | 
			
		|||
			IsClient:           true,
 | 
			
		||||
			Name:               "origin",
 | 
			
		||||
			CompressionQuality: quality,
 | 
			
		||||
			Logger:             logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                &log,
 | 
			
		||||
			HeartbeatInterval:  defaultTimeout,
 | 
			
		||||
			MaxHeartbeats:      defaultRetries,
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,7 @@ func NewCompressedMuxerPair(t assert.TestingT, testName string, quality Compress
 | 
			
		|||
			IsClient:           false,
 | 
			
		||||
			Name:               "edge",
 | 
			
		||||
			CompressionQuality: quality,
 | 
			
		||||
			Logger:             logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                &log,
 | 
			
		||||
			HeartbeatInterval:  defaultTimeout,
 | 
			
		||||
			MaxHeartbeats:      defaultRetries,
 | 
			
		||||
		},
 | 
			
		||||
| 
						 | 
				
			
			@ -186,11 +187,11 @@ func TestSingleStream(t *testing.T) {
 | 
			
		|||
		if stream.Headers[0].Value != "headerValue" {
 | 
			
		||||
			t.Fatalf("expected header value %s, got %s", "headerValue", stream.Headers[0].Value)
 | 
			
		||||
		}
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		buf := []byte("Hello world")
 | 
			
		||||
		stream.Write(buf)
 | 
			
		||||
		_, _ = stream.Write(buf)
 | 
			
		||||
		n, err := io.ReadFull(stream, buf)
 | 
			
		||||
		if n > 0 {
 | 
			
		||||
			t.Fatalf("read %d bytes after EOF", n)
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +231,7 @@ func TestSingleStream(t *testing.T) {
 | 
			
		|||
	if string(responseBody) != "Hello world" {
 | 
			
		||||
		t.Fatalf("expected response body %s, got %s", "Hello world", responseBody)
 | 
			
		||||
	}
 | 
			
		||||
	stream.Close()
 | 
			
		||||
	_ = stream.Close()
 | 
			
		||||
	n, err = stream.Write([]byte("aaaaa"))
 | 
			
		||||
	if n > 0 {
 | 
			
		||||
		t.Fatalf("wrote %d bytes after EOF", n)
 | 
			
		||||
| 
						 | 
				
			
			@ -252,7 +253,7 @@ func TestSingleStreamLargeResponseBody(t *testing.T) {
 | 
			
		|||
		if stream.Headers[0].Value != "headerValue" {
 | 
			
		||||
			t.Fatalf("expected header value %s, got %s", "headerValue", stream.Headers[0].Value)
 | 
			
		||||
		}
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		payload := make([]byte, bodySize)
 | 
			
		||||
| 
						 | 
				
			
			@ -302,7 +303,6 @@ func TestSingleStreamLargeResponseBody(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestMultipleStreams(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	f := MuxedStreamFunc(func(stream *MuxedStream) error {
 | 
			
		||||
		if len(stream.Headers) != 1 {
 | 
			
		||||
			t.Fatalf("expected %d headers, got %d", 1, len(stream.Headers))
 | 
			
		||||
| 
						 | 
				
			
			@ -310,13 +310,13 @@ func TestMultipleStreams(t *testing.T) {
 | 
			
		|||
		if stream.Headers[0].Name != "client-token" {
 | 
			
		||||
			t.Fatalf("expected header name %s, got %s", "client-token", stream.Headers[0].Name)
 | 
			
		||||
		}
 | 
			
		||||
		l.Debugf("Got request for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		log.Debug().Msgf("Got request for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-token", Value: stream.Headers[0].Value},
 | 
			
		||||
		})
 | 
			
		||||
		l.Debugf("Wrote headers for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		stream.Write([]byte("OK"))
 | 
			
		||||
		l.Debugf("Wrote body for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		log.Debug().Msgf("Wrote headers for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		_, _ = stream.Write([]byte("OK"))
 | 
			
		||||
		log.Debug().Msgf("Wrote body for stream %s", stream.Headers[0].Value)
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	muxPair := NewDefaultMuxerPair(t, t.Name(), f)
 | 
			
		||||
| 
						 | 
				
			
			@ -334,7 +334,7 @@ func TestMultipleStreams(t *testing.T) {
 | 
			
		|||
				[]Header{{Name: "client-token", Value: tokenString}},
 | 
			
		||||
				nil,
 | 
			
		||||
			)
 | 
			
		||||
			l.Debugf("Got headers for stream %d", tokenId)
 | 
			
		||||
			log.Debug().Msgf("Got headers for stream %d", tokenId)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errorsC <- err
 | 
			
		||||
				return
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +372,7 @@ func TestMultipleStreams(t *testing.T) {
 | 
			
		|||
	testFail := false
 | 
			
		||||
	for err := range errorsC {
 | 
			
		||||
		testFail = true
 | 
			
		||||
		l.Errorf("%s", err)
 | 
			
		||||
		log.Error().Msgf("%s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if testFail {
 | 
			
		||||
		t.Fatalf("TestMultipleStreams failed")
 | 
			
		||||
| 
						 | 
				
			
			@ -396,7 +396,7 @@ func TestMultipleStreamsFlowControl(t *testing.T) {
 | 
			
		|||
		if stream.Headers[0].Value != "headerValue" {
 | 
			
		||||
			t.Fatalf("expected header value %s, got %s", "headerValue", stream.Headers[0].Value)
 | 
			
		||||
		}
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		payload := make([]byte, responseSizes[(stream.streamID-2)/2])
 | 
			
		||||
| 
						 | 
				
			
			@ -450,27 +450,25 @@ func TestMultipleStreamsFlowControl(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestGracefulShutdown(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	sendC := make(chan struct{})
 | 
			
		||||
	responseBuf := bytes.Repeat([]byte("Hello world"), 65536)
 | 
			
		||||
 | 
			
		||||
	f := MuxedStreamFunc(func(stream *MuxedStream) error {
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		<-sendC
 | 
			
		||||
		l.Debugf("Writing %d bytes", len(responseBuf))
 | 
			
		||||
		stream.Write(responseBuf)
 | 
			
		||||
		stream.CloseWrite()
 | 
			
		||||
		l.Debugf("Wrote %d bytes", len(responseBuf))
 | 
			
		||||
		log.Debug().Msgf("Writing %d bytes", len(responseBuf))
 | 
			
		||||
		_, _ = stream.Write(responseBuf)
 | 
			
		||||
		_ = stream.CloseWrite()
 | 
			
		||||
		log.Debug().Msgf("Wrote %d bytes", len(responseBuf))
 | 
			
		||||
		// Reading from the stream will block until the edge closes its end of the stream.
 | 
			
		||||
		// Otherwise, we'll close the whole connection before receiving the 'stream closed'
 | 
			
		||||
		// message from the edge.
 | 
			
		||||
		// Graceful shutdown works if you omit this, it just gives spurious errors for now -
 | 
			
		||||
		// TODO ignore errors when writing 'stream closed' and we're shutting down.
 | 
			
		||||
		stream.Read([]byte{0})
 | 
			
		||||
		l.Debugf("Handler ends")
 | 
			
		||||
		_, _ = stream.Read([]byte{0})
 | 
			
		||||
		log.Debug().Msgf("Handler ends")
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	muxPair := NewDefaultMuxerPair(t, t.Name(), f)
 | 
			
		||||
| 
						 | 
				
			
			@ -487,7 +485,7 @@ func TestGracefulShutdown(t *testing.T) {
 | 
			
		|||
	muxPair.EdgeMux.Shutdown()
 | 
			
		||||
	close(sendC)
 | 
			
		||||
	responseBody := make([]byte, len(responseBuf))
 | 
			
		||||
	l.Debugf("Waiting for %d bytes", len(responseBuf))
 | 
			
		||||
	log.Debug().Msgf("Waiting for %d bytes", len(responseBuf))
 | 
			
		||||
	n, err := io.ReadFull(stream, responseBody)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error from (*MuxedStream).Read with %d bytes read: %s", n, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -498,7 +496,7 @@ func TestGracefulShutdown(t *testing.T) {
 | 
			
		|||
	if !bytes.Equal(responseBuf, responseBody) {
 | 
			
		||||
		t.Fatalf("response body mismatch")
 | 
			
		||||
	}
 | 
			
		||||
	stream.Close()
 | 
			
		||||
	_ = stream.Close()
 | 
			
		||||
	muxPair.Wait(t)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -509,7 +507,7 @@ func TestUnexpectedShutdown(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	f := MuxedStreamFunc(func(stream *MuxedStream) error {
 | 
			
		||||
		defer close(handlerFinishC)
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		<-sendC
 | 
			
		||||
| 
						 | 
				
			
			@ -536,7 +534,7 @@ func TestUnexpectedShutdown(t *testing.T) {
 | 
			
		|||
		nil,
 | 
			
		||||
	)
 | 
			
		||||
	// Close the underlying connection before telling the origin to write.
 | 
			
		||||
	muxPair.EdgeConn.Close()
 | 
			
		||||
	_ = muxPair.EdgeConn.Close()
 | 
			
		||||
	close(sendC)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error in OpenStream: %s", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -559,18 +557,18 @@ func TestUnexpectedShutdown(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
func EchoHandler(stream *MuxedStream) error {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	fmt.Fprintf(&buf, "Hello, world!\n\n# REQUEST HEADERS:\n\n")
 | 
			
		||||
	_, _ = fmt.Fprintf(&buf, "Hello, world!\n\n# REQUEST HEADERS:\n\n")
 | 
			
		||||
	for _, header := range stream.Headers {
 | 
			
		||||
		fmt.Fprintf(&buf, "[%s] = %s\n", header.Name, header.Value)
 | 
			
		||||
		_, _ = fmt.Fprintf(&buf, "[%s] = %s\n", header.Name, header.Value)
 | 
			
		||||
	}
 | 
			
		||||
	stream.WriteHeaders([]Header{
 | 
			
		||||
	_ = stream.WriteHeaders([]Header{
 | 
			
		||||
		{Name: ":status", Value: "200"},
 | 
			
		||||
		{Name: "server", Value: "Echo-server/1.0"},
 | 
			
		||||
		{Name: "date", Value: time.Now().Format(time.RFC850)},
 | 
			
		||||
		{Name: "content-type", Value: "text/html; charset=utf-8"},
 | 
			
		||||
		{Name: "content-length", Value: strconv.Itoa(buf.Len())},
 | 
			
		||||
	})
 | 
			
		||||
	buf.WriteTo(stream)
 | 
			
		||||
	_, _ = buf.WriteTo(stream)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -582,14 +580,14 @@ func TestOpenAfterDisconnect(t *testing.T) {
 | 
			
		|||
		switch i {
 | 
			
		||||
		case 0:
 | 
			
		||||
			// Close both directions of the connection to cause EOF on both peers.
 | 
			
		||||
			muxPair.OriginConn.Close()
 | 
			
		||||
			muxPair.EdgeConn.Close()
 | 
			
		||||
			_ = muxPair.OriginConn.Close()
 | 
			
		||||
			_ = muxPair.EdgeConn.Close()
 | 
			
		||||
		case 1:
 | 
			
		||||
			// Close origin conn to cause EOF on origin first.
 | 
			
		||||
			muxPair.OriginConn.Close()
 | 
			
		||||
			_ = muxPair.OriginConn.Close()
 | 
			
		||||
		case 2:
 | 
			
		||||
			// Close edge conn to cause EOF on edge first.
 | 
			
		||||
			muxPair.EdgeConn.Close()
 | 
			
		||||
			_ = muxPair.EdgeConn.Close()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, err := muxPair.OpenEdgeMuxStream(
 | 
			
		||||
| 
						 | 
				
			
			@ -617,7 +615,7 @@ func TestHPACK(t *testing.T) {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		t.Fatalf("error in OpenStream: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	stream.Close()
 | 
			
		||||
	_ = stream.Close()
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < 3; i++ {
 | 
			
		||||
		stream, err := muxPair.OpenEdgeMuxStream(
 | 
			
		||||
| 
						 | 
				
			
			@ -654,8 +652,8 @@ func TestHPACK(t *testing.T) {
 | 
			
		|||
		if stream.Headers[0].Value != "200" {
 | 
			
		||||
			t.Fatalf("expected status 200, got %s", stream.Headers[0].Value)
 | 
			
		||||
		}
 | 
			
		||||
		ioutil.ReadAll(stream)
 | 
			
		||||
		stream.Close()
 | 
			
		||||
		_, _ = ioutil.ReadAll(stream)
 | 
			
		||||
		_ = stream.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -680,7 +678,7 @@ func AssertIfPipeReadable(t *testing.T, pipe io.ReadCloser) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestMultipleStreamsWithDictionaries(t *testing.T) {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	l := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	for q := CompressionNone; q <= CompressionMax; q++ {
 | 
			
		||||
		htmlBody := `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"` +
 | 
			
		||||
| 
						 | 
				
			
			@ -730,10 +728,10 @@ func TestMultipleStreamsWithDictionaries(t *testing.T) {
 | 
			
		|||
				contentType = "img/gif"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			stream.WriteHeaders([]Header{
 | 
			
		||||
				Header{Name: "content-type", Value: contentType},
 | 
			
		||||
			_ = stream.WriteHeaders([]Header{
 | 
			
		||||
				{Name: "content-type", Value: contentType},
 | 
			
		||||
			})
 | 
			
		||||
			stream.Write([]byte(strings.Replace(htmlBody, "paragraph", pathHeader.Value, 1) + stream.Headers[5].Value))
 | 
			
		||||
			_, _ = stream.Write([]byte(strings.Replace(htmlBody, "paragraph", pathHeader.Value, 1) + stream.Headers[5].Value))
 | 
			
		||||
 | 
			
		||||
			return nil
 | 
			
		||||
		})
 | 
			
		||||
| 
						 | 
				
			
			@ -817,7 +815,7 @@ func TestMultipleStreamsWithDictionaries(t *testing.T) {
 | 
			
		|||
		testFail := false
 | 
			
		||||
		for err := range errorsC {
 | 
			
		||||
			testFail = true
 | 
			
		||||
			l.Errorf("%s", err)
 | 
			
		||||
			l.Error().Msgf("%s", err)
 | 
			
		||||
		}
 | 
			
		||||
		if testFail {
 | 
			
		||||
			t.Fatalf("TestMultipleStreams failed")
 | 
			
		||||
| 
						 | 
				
			
			@ -831,8 +829,6 @@ func TestMultipleStreamsWithDictionaries(t *testing.T) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func sampleSiteHandler(files map[string][]byte) MuxedStreamFunc {
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
 | 
			
		||||
	return func(stream *MuxedStream) error {
 | 
			
		||||
		var contentType string
 | 
			
		||||
		var pathHeader Header
 | 
			
		||||
| 
						 | 
				
			
			@ -857,16 +853,16 @@ func sampleSiteHandler(files map[string][]byte) MuxedStreamFunc {
 | 
			
		|||
		} else {
 | 
			
		||||
			contentType = "img/gif"
 | 
			
		||||
		}
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
			Header{Name: "content-type", Value: contentType},
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "content-type", Value: contentType},
 | 
			
		||||
		})
 | 
			
		||||
		l.Debugf("Wrote headers for stream %s", pathHeader.Value)
 | 
			
		||||
		log.Debug().Msgf("Wrote headers for stream %s", pathHeader.Value)
 | 
			
		||||
		file, ok := files[pathHeader.Value]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return fmt.Errorf("%s content is not preloaded", pathHeader.Value)
 | 
			
		||||
		}
 | 
			
		||||
		stream.Write(file)
 | 
			
		||||
		l.Debugf("Wrote body for stream %s", pathHeader.Value)
 | 
			
		||||
		_, _ = stream.Write(file)
 | 
			
		||||
		log.Debug().Msgf("Wrote body for stream %s", pathHeader.Value)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1008,7 +1004,7 @@ func BenchmarkOpenStream(b *testing.B) {
 | 
			
		|||
			if stream.Headers[0].Value != "headerValue" {
 | 
			
		||||
				b.Fatalf("expected header value %s, got %s", "headerValue", stream.Headers[0].Value)
 | 
			
		||||
			}
 | 
			
		||||
			stream.WriteHeaders([]Header{
 | 
			
		||||
			_ = stream.WriteHeaders([]Header{
 | 
			
		||||
				{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
			})
 | 
			
		||||
			return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -1058,7 +1054,7 @@ func BenchmarkSingleStreamLargeResponseBody(b *testing.B) {
 | 
			
		|||
		if stream.Headers[0].Value != "headerValue" {
 | 
			
		||||
			b.Fatalf("expected header value %s, got %s", "headerValue", stream.Headers[0].Value)
 | 
			
		||||
		}
 | 
			
		||||
		stream.WriteHeaders([]Header{
 | 
			
		||||
		_ = stream.WriteHeaders([]Header{
 | 
			
		||||
			{Name: "response-header", Value: "responseValue"},
 | 
			
		||||
		})
 | 
			
		||||
		for i := 0; i < writeN; i++ {
 | 
			
		||||
| 
						 | 
				
			
			@ -1083,7 +1079,7 @@ func BenchmarkSingleStreamLargeResponseBody(b *testing.B) {
 | 
			
		|||
			Handler:                 f,
 | 
			
		||||
			IsClient:                true,
 | 
			
		||||
			Name:                    "origin",
 | 
			
		||||
			Logger:                  logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                     &log,
 | 
			
		||||
			DefaultWindowSize:       defaultWindowSize,
 | 
			
		||||
			MaxWindowSize:           maxWindowSize,
 | 
			
		||||
			StreamWriteBufferMaxLen: defaultWriteBufferMaxLen,
 | 
			
		||||
| 
						 | 
				
			
			@ -1095,7 +1091,7 @@ func BenchmarkSingleStreamLargeResponseBody(b *testing.B) {
 | 
			
		|||
			Timeout:                 testHandshakeTimeout,
 | 
			
		||||
			IsClient:                false,
 | 
			
		||||
			Name:                    "edge",
 | 
			
		||||
			Logger:                  logger.NewOutputWriter(logger.NewMockWriteManager()),
 | 
			
		||||
			Log:                     &log,
 | 
			
		||||
			DefaultWindowSize:       defaultWindowSize,
 | 
			
		||||
			MaxWindowSize:           maxWindowSize,
 | 
			
		||||
			StreamWriteBufferMaxLen: defaultWriteBufferMaxLen,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,8 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/golang-collections/collections/queue"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// data points used to compute average receive window and send window size
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ type muxMetricsUpdater interface {
 | 
			
		|||
	// metrics returns the latest metrics
 | 
			
		||||
	metrics() *MuxerMetrics
 | 
			
		||||
	// run is a blocking call to start the event loop
 | 
			
		||||
	run(logger logger.Service) error
 | 
			
		||||
	run(log *zerolog.Logger) error
 | 
			
		||||
	// updateRTTChan is called by muxReader to report new RTT measurements
 | 
			
		||||
	updateRTT(rtt *roundTripMeasurement)
 | 
			
		||||
	//updateReceiveWindowChan is called by muxReader and muxWriter when receiveWindow size is updated
 | 
			
		||||
| 
						 | 
				
			
			@ -137,30 +137,30 @@ func (updater *muxMetricsUpdaterImpl) metrics() *MuxerMetrics {
 | 
			
		|||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (updater *muxMetricsUpdaterImpl) run(logger logger.Service) error {
 | 
			
		||||
	defer logger.Debug("mux - metrics: event loop finished")
 | 
			
		||||
func (updater *muxMetricsUpdaterImpl) run(log *zerolog.Logger) error {
 | 
			
		||||
	defer log.Debug().Msg("mux - metrics: event loop finished")
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-updater.abortChan:
 | 
			
		||||
			logger.Infof("mux - metrics: Stopping mux metrics updater")
 | 
			
		||||
			log.Info().Msgf("mux - metrics: Stopping mux metrics updater")
 | 
			
		||||
			return nil
 | 
			
		||||
		case roundTripMeasurement := <-updater.updateRTTChan:
 | 
			
		||||
			go updater.rttData.update(roundTripMeasurement)
 | 
			
		||||
			logger.Debug("mux - metrics: Update rtt")
 | 
			
		||||
			log.Debug().Msg("mux - metrics: Update rtt")
 | 
			
		||||
		case receiveWindow := <-updater.updateReceiveWindowChan:
 | 
			
		||||
			go updater.receiveWindowData.update(receiveWindow)
 | 
			
		||||
			logger.Debug("mux - metrics: Update receive window")
 | 
			
		||||
			log.Debug().Msg("mux - metrics: Update receive window")
 | 
			
		||||
		case sendWindow := <-updater.updateSendWindowChan:
 | 
			
		||||
			go updater.sendWindowData.update(sendWindow)
 | 
			
		||||
			logger.Debug("mux - metrics: Update send window")
 | 
			
		||||
			log.Debug().Msg("mux - metrics: Update send window")
 | 
			
		||||
		case inBoundBytes := <-updater.updateInBoundBytesChan:
 | 
			
		||||
			// inBoundBytes is bytes/sec because the update interval is 1 sec
 | 
			
		||||
			go updater.inBoundRate.update(inBoundBytes)
 | 
			
		||||
			logger.Debugf("mux - metrics: Inbound bytes %d", inBoundBytes)
 | 
			
		||||
			log.Debug().Msgf("mux - metrics: Inbound bytes %d", inBoundBytes)
 | 
			
		||||
		case outBoundBytes := <-updater.updateOutBoundBytesChan:
 | 
			
		||||
			// outBoundBytes is bytes/sec because the update interval is 1 sec
 | 
			
		||||
			go updater.outBoundRate.update(outBoundBytes)
 | 
			
		||||
			logger.Debugf("mux - metrics: Outbound bytes %d", outBoundBytes)
 | 
			
		||||
			log.Debug().Msgf("mux - metrics: Outbound bytes %d", outBoundBytes)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import (
 | 
			
		|||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -91,10 +91,10 @@ func TestMuxMetricsUpdater(t *testing.T) {
 | 
			
		|||
	abortChan := make(chan struct{})
 | 
			
		||||
	compBefore, compAfter := NewAtomicCounter(0), NewAtomicCounter(0)
 | 
			
		||||
	m := newMuxMetricsUpdater(abortChan, compBefore, compAfter)
 | 
			
		||||
	logger := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		errChan <- m.run(logger)
 | 
			
		||||
		errChan <- m.run(&log)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ import (
 | 
			
		|||
	"net/url"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/net/http2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -68,8 +68,8 @@ func (r *MuxReader) Shutdown() <-chan struct{} {
 | 
			
		|||
	return done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *MuxReader) run(logger logger.Service) error {
 | 
			
		||||
	defer logger.Debug("mux - read: event loop finished")
 | 
			
		||||
func (r *MuxReader) run(log *zerolog.Logger) error {
 | 
			
		||||
	defer log.Debug().Msg("mux - read: event loop finished")
 | 
			
		||||
 | 
			
		||||
	// routine to periodically update bytesRead
 | 
			
		||||
	go func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ func (r *MuxReader) run(logger logger.Service) error {
 | 
			
		|||
			}
 | 
			
		||||
			switch e := err.(type) {
 | 
			
		||||
			case http2.StreamError:
 | 
			
		||||
				logger.Infof("%s: stream error", errorString)
 | 
			
		||||
				log.Info().Msgf("%s: stream error", errorString)
 | 
			
		||||
				// Ideally we wouldn't return here, since that aborts the muxer.
 | 
			
		||||
				// We should communicate the error to the relevant MuxedStream
 | 
			
		||||
				// data structure, so that callers of MuxedStream.Read() and
 | 
			
		||||
| 
						 | 
				
			
			@ -101,28 +101,28 @@ func (r *MuxReader) run(logger logger.Service) error {
 | 
			
		|||
				// and keep the muxer going.
 | 
			
		||||
				return r.streamError(e.StreamID, e.Code)
 | 
			
		||||
			case http2.ConnectionError:
 | 
			
		||||
				logger.Infof("%s: stream error", errorString)
 | 
			
		||||
				log.Info().Msgf("%s: stream error", errorString)
 | 
			
		||||
				return r.connectionError(err)
 | 
			
		||||
			default:
 | 
			
		||||
				if isConnectionClosedError(err) {
 | 
			
		||||
					if r.streams.Len() == 0 {
 | 
			
		||||
						// don't log the error here -- that would just be extra noise
 | 
			
		||||
						logger.Debug("mux - read: shutting down")
 | 
			
		||||
						log.Debug().Msg("mux - read: shutting down")
 | 
			
		||||
						return nil
 | 
			
		||||
					}
 | 
			
		||||
					logger.Infof("%s: connection closed unexpectedly", errorString)
 | 
			
		||||
					log.Info().Msgf("%s: connection closed unexpectedly", errorString)
 | 
			
		||||
					return err
 | 
			
		||||
				} else {
 | 
			
		||||
					logger.Infof("%s: frame read error", errorString)
 | 
			
		||||
					log.Info().Msgf("%s: frame read error", errorString)
 | 
			
		||||
					return r.connectionError(err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		r.connActive.Signal()
 | 
			
		||||
		logger.Debugf("mux - read: read frame: data %v", frame)
 | 
			
		||||
		log.Debug().Msgf("mux - read: read frame: data %v", frame)
 | 
			
		||||
		switch f := frame.(type) {
 | 
			
		||||
		case *http2.DataFrame:
 | 
			
		||||
			err = r.receiveFrameData(f, logger)
 | 
			
		||||
			err = r.receiveFrameData(f, log)
 | 
			
		||||
		case *http2.MetaHeadersFrame:
 | 
			
		||||
			err = r.receiveHeaderData(f)
 | 
			
		||||
		case *http2.RSTStreamFrame:
 | 
			
		||||
| 
						 | 
				
			
			@ -155,7 +155,7 @@ func (r *MuxReader) run(logger logger.Service) error {
 | 
			
		|||
			err = ErrUnexpectedFrameType
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Debugf("mux - read: read error: data %v", frame)
 | 
			
		||||
			log.Debug().Msgf("mux - read: read error: data %v", frame)
 | 
			
		||||
			return r.connectionError(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -276,7 +276,7 @@ func (r *MuxReader) handleStream(stream *MuxedStream) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Receives a data frame from a stream. A non-nil error is a connection error.
 | 
			
		||||
func (r *MuxReader) receiveFrameData(frame *http2.DataFrame, logger logger.Service) error {
 | 
			
		||||
func (r *MuxReader) receiveFrameData(frame *http2.DataFrame, log *zerolog.Logger) error {
 | 
			
		||||
	stream, err := r.getStreamForFrame(frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return r.defaultStreamErrorHandler(err, frame.Header())
 | 
			
		||||
| 
						 | 
				
			
			@ -292,9 +292,9 @@ func (r *MuxReader) receiveFrameData(frame *http2.DataFrame, logger logger.Servi
 | 
			
		|||
	if frame.Header().Flags.Has(http2.FlagDataEndStream) {
 | 
			
		||||
		if stream.receiveEOF() {
 | 
			
		||||
			r.streams.Delete(stream.streamID)
 | 
			
		||||
			logger.Debugf("mux - read: stream closed: streamID: %d", frame.Header().StreamID)
 | 
			
		||||
			log.Debug().Msgf("mux - read: stream closed: streamID: %d", frame.Header().StreamID)
 | 
			
		||||
		} else {
 | 
			
		||||
			logger.Debugf("mux - read: shutdown receive side: streamID: %d", frame.Header().StreamID)
 | 
			
		||||
			log.Debug().Msgf("mux - read: shutdown receive side: streamID: %d", frame.Header().StreamID)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,10 +3,10 @@ package h2mux
 | 
			
		|||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"io"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"golang.org/x/net/http2"
 | 
			
		||||
	"golang.org/x/net/http2/hpack"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -72,8 +72,8 @@ func tsToPingData(ts int64) [8]byte {
 | 
			
		|||
	return pingData
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		||||
	defer logger.Debug("mux - write: event loop finished")
 | 
			
		||||
func (w *MuxWriter) run(log *zerolog.Logger) error {
 | 
			
		||||
	defer log.Debug().Msg("mux - write: event loop finished")
 | 
			
		||||
 | 
			
		||||
	// routine to periodically communicate bytesWrote
 | 
			
		||||
	go func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -91,17 +91,17 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-w.abortChan:
 | 
			
		||||
			logger.Debug("mux - write: aborting writer thread")
 | 
			
		||||
			log.Debug().Msg("mux - write: aborting writer thread")
 | 
			
		||||
			return nil
 | 
			
		||||
		case errCode := <-w.goAwayChan:
 | 
			
		||||
			logger.Debugf("mux - write: sending GOAWAY code %v", errCode)
 | 
			
		||||
			log.Debug().Msgf("mux - write: sending GOAWAY code %v", errCode)
 | 
			
		||||
			err := w.f.WriteGoAway(w.streams.LastPeerStreamID(), errCode, []byte{})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			w.idleTimer.MarkActive()
 | 
			
		||||
		case <-w.pingTimestamp.GetUpdateChan():
 | 
			
		||||
			logger.Debug("mux - write: sending PING ACK")
 | 
			
		||||
			log.Debug().Msg("mux - write: sending PING ACK")
 | 
			
		||||
			err := w.f.WritePing(true, tsToPingData(w.pingTimestamp.Get()))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
| 
						 | 
				
			
			@ -111,7 +111,7 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
			if !w.idleTimer.Retry() {
 | 
			
		||||
				return ErrConnectionDropped
 | 
			
		||||
			}
 | 
			
		||||
			logger.Debug("mux - write: sending PING")
 | 
			
		||||
			log.Debug().Msg("mux - write: sending PING")
 | 
			
		||||
			err := w.f.WritePing(false, tsToPingData(time.Now().UnixNano()))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +121,7 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
			w.idleTimer.MarkActive()
 | 
			
		||||
		case <-w.streamErrors.GetSignalChan():
 | 
			
		||||
			for streamID, errCode := range w.streamErrors.GetErrors() {
 | 
			
		||||
				logger.Debugf("mux - write: resetting stream with code: %v streamID: %d", errCode, streamID)
 | 
			
		||||
				log.Debug().Msgf("mux - write: resetting stream with code: %v streamID: %d", errCode, streamID)
 | 
			
		||||
				err := w.f.WriteRSTStream(streamID, errCode)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +141,7 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
			if streamRequest.body != nil {
 | 
			
		||||
				go streamRequest.flushBody()
 | 
			
		||||
			}
 | 
			
		||||
			err := w.writeStreamData(streamRequest.stream, logger)
 | 
			
		||||
			err := w.writeStreamData(streamRequest.stream, log)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -151,7 +151,7 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
			if !ok {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			err := w.writeStreamData(stream, logger)
 | 
			
		||||
			err := w.writeStreamData(stream, log)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +159,7 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
		case useDict := <-w.useDictChan:
 | 
			
		||||
			err := w.writeUseDictionary(useDict)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logger.Errorf("mux - write: error writing use dictionary: %s", err)
 | 
			
		||||
				log.Error().Msgf("mux - write: error writing use dictionary: %s", err)
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			w.idleTimer.MarkActive()
 | 
			
		||||
| 
						 | 
				
			
			@ -167,18 +167,18 @@ func (w *MuxWriter) run(logger logger.Service) error {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *MuxWriter) writeStreamData(stream *MuxedStream, logger logger.Service) error {
 | 
			
		||||
	logger.Debugf("mux - write: writable: streamID: %d", stream.streamID)
 | 
			
		||||
func (w *MuxWriter) writeStreamData(stream *MuxedStream, log *zerolog.Logger) error {
 | 
			
		||||
	log.Debug().Msgf("mux - write: writable: streamID: %d", stream.streamID)
 | 
			
		||||
	chunk := stream.getChunk()
 | 
			
		||||
	w.metricsUpdater.updateReceiveWindow(stream.getReceiveWindow())
 | 
			
		||||
	w.metricsUpdater.updateSendWindow(stream.getSendWindow())
 | 
			
		||||
	if chunk.sendHeadersFrame() {
 | 
			
		||||
		err := w.writeHeaders(chunk.streamID, chunk.headers)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("mux - write: error writing headers: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			log.Error().Msgf("mux - write: error writing headers: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		logger.Debugf("mux - write: output headers: streamID: %d", stream.streamID)
 | 
			
		||||
		log.Debug().Msgf("mux - write: output headers: streamID: %d", stream.streamID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if chunk.sendWindowUpdateFrame() {
 | 
			
		||||
| 
						 | 
				
			
			@ -189,22 +189,22 @@ func (w *MuxWriter) writeStreamData(stream *MuxedStream, logger logger.Service)
 | 
			
		|||
		// window, unless the receiver treats this as a connection error"
 | 
			
		||||
		err := w.f.WriteWindowUpdate(chunk.streamID, chunk.windowUpdate)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("mux - write: error writing window update: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			log.Error().Msgf("mux - write: error writing window update: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		logger.Debugf("mux - write: increment receive window by %d streamID: %d", chunk.windowUpdate, stream.streamID)
 | 
			
		||||
		log.Debug().Msgf("mux - write: increment receive window by %d streamID: %d", chunk.windowUpdate, stream.streamID)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for chunk.sendDataFrame() {
 | 
			
		||||
		payload, sentEOF := chunk.nextDataFrame(int(w.maxFrameSize))
 | 
			
		||||
		err := w.f.WriteData(chunk.streamID, sentEOF, payload)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("mux - write: error writing data: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			log.Error().Msgf("mux - write: error writing data: %s: streamID: %d", err, stream.streamID)
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		// update the amount of data wrote
 | 
			
		||||
		w.bytesWrote.IncrementBy(uint64(len(payload)))
 | 
			
		||||
		logger.Debugf("mux - write: output data: %d: streamID: %d", len(payload), stream.streamID)
 | 
			
		||||
		log.Debug().Msgf("mux - write: output data: %d: streamID: %d", len(payload), stream.streamID)
 | 
			
		||||
 | 
			
		||||
		if sentEOF {
 | 
			
		||||
			if stream.readBuffer.Closed() {
 | 
			
		||||
| 
						 | 
				
			
			@ -212,15 +212,15 @@ func (w *MuxWriter) writeStreamData(stream *MuxedStream, logger logger.Service)
 | 
			
		|||
				if !stream.gotReceiveEOF() {
 | 
			
		||||
					// the peer may send data that we no longer want to receive. Force them into the
 | 
			
		||||
					// closed state.
 | 
			
		||||
					logger.Debugf("mux - write: resetting stream: streamID: %d", stream.streamID)
 | 
			
		||||
					log.Debug().Msgf("mux - write: resetting stream: streamID: %d", stream.streamID)
 | 
			
		||||
					w.f.WriteRSTStream(chunk.streamID, http2.ErrCodeNo)
 | 
			
		||||
				} else {
 | 
			
		||||
					// Half-open stream transitioned into closed
 | 
			
		||||
					logger.Debugf("mux - write: closing stream: streamID: %d", stream.streamID)
 | 
			
		||||
					log.Debug().Msgf("mux - write: closing stream: streamID: %d", stream.streamID)
 | 
			
		||||
				}
 | 
			
		||||
				w.streams.Delete(chunk.streamID)
 | 
			
		||||
			} else {
 | 
			
		||||
				logger.Debugf("mux - write: closing stream write side: streamID: %d", stream.streamID)
 | 
			
		||||
				log.Debug().Msgf("mux - write: closing stream write side: streamID: %d", stream.streamID)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,8 +12,8 @@ import (
 | 
			
		|||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tlsconfig"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -99,8 +99,8 @@ const indexTemplate = `
 | 
			
		|||
</html>
 | 
			
		||||
`
 | 
			
		||||
 | 
			
		||||
func StartHelloWorldServer(logger logger.Service, listener net.Listener, shutdownC <-chan struct{}) error {
 | 
			
		||||
	logger.Infof("Starting Hello World server at %s", listener.Addr())
 | 
			
		||||
func StartHelloWorldServer(log *zerolog.Logger, listener net.Listener, shutdownC <-chan struct{}) error {
 | 
			
		||||
	log.Info().Msgf("Starting Hello World server at %s", listener.Addr())
 | 
			
		||||
	serverName := defaultServerName
 | 
			
		||||
	if hostname, err := os.Hostname(); err == nil {
 | 
			
		||||
		serverName = hostname
 | 
			
		||||
| 
						 | 
				
			
			@ -113,14 +113,14 @@ func StartHelloWorldServer(logger logger.Service, listener net.Listener, shutdow
 | 
			
		|||
 | 
			
		||||
	muxer := http.NewServeMux()
 | 
			
		||||
	muxer.HandleFunc(UptimeRoute, uptimeHandler(time.Now()))
 | 
			
		||||
	muxer.HandleFunc(WSRoute, websocketHandler(logger, upgrader))
 | 
			
		||||
	muxer.HandleFunc(SSERoute, sseHandler(logger))
 | 
			
		||||
	muxer.HandleFunc(WSRoute, websocketHandler(log, upgrader))
 | 
			
		||||
	muxer.HandleFunc(SSERoute, sseHandler(log))
 | 
			
		||||
	muxer.HandleFunc(HealthRoute, healthHandler())
 | 
			
		||||
	muxer.HandleFunc("/", rootHandler(serverName))
 | 
			
		||||
	httpServer := &http.Server{Addr: listener.Addr().String(), Handler: muxer}
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-shutdownC
 | 
			
		||||
		httpServer.Close()
 | 
			
		||||
		_ = httpServer.Close()
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err := httpServer.Serve(listener)
 | 
			
		||||
| 
						 | 
				
			
			@ -152,13 +152,13 @@ func uptimeHandler(startTime time.Time) http.HandlerFunc {
 | 
			
		|||
			w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
		} else {
 | 
			
		||||
			w.Header().Set("Content-Type", "application/json")
 | 
			
		||||
			w.Write(respJson)
 | 
			
		||||
			_, _ = w.Write(respJson)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This handler will echo message
 | 
			
		||||
func websocketHandler(logger logger.Service, upgrader websocket.Upgrader) http.HandlerFunc {
 | 
			
		||||
func websocketHandler(log *zerolog.Logger, upgrader websocket.Upgrader) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		// This addresses the issue of r.Host includes port but origin header doesn't
 | 
			
		||||
		host, _, err := net.SplitHostPort(r.Host)
 | 
			
		||||
| 
						 | 
				
			
			@ -168,32 +168,32 @@ func websocketHandler(logger logger.Service, upgrader websocket.Upgrader) http.H
 | 
			
		|||
 | 
			
		||||
		conn, err := upgrader.Upgrade(w, r, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logger.Errorf("failed to upgrade to websocket connection, error: %s", err)
 | 
			
		||||
			log.Error().Msgf("failed to upgrade to websocket connection, error: %s", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		defer conn.Close()
 | 
			
		||||
		for {
 | 
			
		||||
			mt, message, err := conn.ReadMessage()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				logger.Errorf("websocket read message error: %s", err)
 | 
			
		||||
				log.Error().Msgf("websocket read message error: %s", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if err := conn.WriteMessage(mt, message); err != nil {
 | 
			
		||||
				logger.Errorf("websocket write message error: %s", err)
 | 
			
		||||
				log.Error().Msgf("websocket write message error: %s", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sseHandler(logger logger.Service) http.HandlerFunc {
 | 
			
		||||
func sseHandler(log *zerolog.Logger) http.HandlerFunc {
 | 
			
		||||
	return func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Header().Set("Content-Type", "text/event-stream; charset=utf-8")
 | 
			
		||||
		flusher, ok := w.(http.Flusher)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
			logger.Errorf("Can't support SSE. ResponseWriter %T doesn't implement http.Flusher interface", w)
 | 
			
		||||
			log.Error().Msgf("Can't support SSE. ResponseWriter %T doesn't implement http.Flusher interface", w)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +204,7 @@ func sseHandler(logger logger.Service) http.HandlerFunc {
 | 
			
		|||
				freq = parsedFreq
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		logger.Infof("Server Sent Events every %s", freq)
 | 
			
		||||
		log.Info().Msgf("Server Sent Events every %s", freq)
 | 
			
		||||
		ticker := time.NewTicker(freq)
 | 
			
		||||
		counter := 0
 | 
			
		||||
		for {
 | 
			
		||||
| 
						 | 
				
			
			@ -247,9 +247,9 @@ func rootHandler(serverName string) http.HandlerFunc {
 | 
			
		|||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			w.WriteHeader(http.StatusInternalServerError)
 | 
			
		||||
			fmt.Fprintf(w, "error: %v", err)
 | 
			
		||||
			_, _ = fmt.Fprintf(w, "error: %v", err)
 | 
			
		||||
		} else {
 | 
			
		||||
			buffer.WriteTo(w)
 | 
			
		||||
			_, _ = buffer.WriteTo(w)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,11 +9,11 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
| 
						 | 
				
			
			@ -63,7 +63,7 @@ type Ingress struct {
 | 
			
		|||
 | 
			
		||||
// NewSingleOrigin constructs an Ingress set with only one rule, constructed from
 | 
			
		||||
// legacy CLI parameters like --url or --no-chunked-encoding.
 | 
			
		||||
func NewSingleOrigin(c *cli.Context, allowURLFromArgs bool, logger logger.Service) (Ingress, error) {
 | 
			
		||||
func NewSingleOrigin(c *cli.Context, allowURLFromArgs bool) (Ingress, error) {
 | 
			
		||||
 | 
			
		||||
	service, err := parseSingleOriginService(c, allowURLFromArgs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,10 +113,10 @@ func (ing Ingress) IsEmpty() bool {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// StartOrigins will start any origin services managed by cloudflared, e.g. proxy servers or Hello World.
 | 
			
		||||
func (ing Ingress) StartOrigins(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error) {
 | 
			
		||||
func (ing Ingress) StartOrigins(wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error) {
 | 
			
		||||
	for _, rule := range ing.Rules {
 | 
			
		||||
		if err := rule.Service.start(wg, log, shutdownC, errC, rule.Config); err != nil {
 | 
			
		||||
			log.Errorf("Error starting local service %s: %s", rule.Service, err)
 | 
			
		||||
			log.Error().Msgf("Error starting local service %s: %s", rule.Service, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,6 @@ import (
 | 
			
		|||
	"gopkg.in/yaml.v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tlsconfig"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -329,9 +328,8 @@ func TestSingleOriginSetsConfig(t *testing.T) {
 | 
			
		|||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	allowURLFromArgs := false
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	ingress, err := NewSingleOrigin(cliCtx, allowURLFromArgs, logger)
 | 
			
		||||
	ingress, err := NewSingleOrigin(cliCtx, allowURLFromArgs)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, time.Second, ingress.Rules[0].Config.ConnectTimeout)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,12 +13,12 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/hello"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/socks"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tlsconfig"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/websocket"
 | 
			
		||||
	gws "github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OriginService is something a tunnel can proxy traffic to.
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ type OriginService interface {
 | 
			
		|||
	// Start the origin service if it's managed by cloudflared, e.g. proxy servers or Hello World.
 | 
			
		||||
	// If it's not managed by cloudflared, this is a no-op because the user is responsible for
 | 
			
		||||
	// starting the origin service.
 | 
			
		||||
	start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error
 | 
			
		||||
	start(wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// unixSocketPath is an OriginService representing a unix socket (which accepts HTTP)
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ func (o *unixSocketPath) String() string {
 | 
			
		|||
	return "unix socket: " + o.path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *unixSocketPath) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (o *unixSocketPath) start(wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
	transport, err := newHTTPTransport(o, cfg, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +84,7 @@ func (o *localService) Dial(reqURL *url.URL, headers http.Header) (*gws.Conn, *h
 | 
			
		|||
	return d.Dial(reqURL.String(), headers)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *localService) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (o *localService) start(wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
	transport, err := newHTTPTransport(o, cfg, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -101,13 +101,13 @@ func (o *localService) start(wg *sync.WaitGroup, log logger.Service, shutdownC <
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *localService) startProxy(staticHost string, wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (o *localService) startProxy(staticHost string, wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
 | 
			
		||||
	// Start a listener for the proxy
 | 
			
		||||
	proxyAddress := net.JoinHostPort(cfg.ProxyAddress, strconv.Itoa(int(cfg.ProxyPort)))
 | 
			
		||||
	listener, err := net.Listen("tcp", proxyAddress)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Errorf("Cannot start Websocket Proxy Server: %s", err)
 | 
			
		||||
		log.Error().Msgf("Cannot start Websocket Proxy Server: %s", err)
 | 
			
		||||
		return errors.Wrap(err, "Cannot start Websocket Proxy Server")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -119,18 +119,18 @@ func (o *localService) startProxy(staticHost string, wg *sync.WaitGroup, log log
 | 
			
		|||
		// This origin's config specifies what type of proxy to start.
 | 
			
		||||
		switch cfg.ProxyType {
 | 
			
		||||
		case socksProxy:
 | 
			
		||||
			log.Info("SOCKS5 server started")
 | 
			
		||||
			log.Info().Msg("SOCKS5 server started")
 | 
			
		||||
			streamHandler = func(wsConn *websocket.Conn, remoteConn net.Conn, _ http.Header) {
 | 
			
		||||
				dialer := socks.NewConnDialer(remoteConn)
 | 
			
		||||
				requestHandler := socks.NewRequestHandler(dialer)
 | 
			
		||||
				socksServer := socks.NewConnectionHandler(requestHandler)
 | 
			
		||||
 | 
			
		||||
				socksServer.Serve(wsConn)
 | 
			
		||||
				_ = socksServer.Serve(wsConn)
 | 
			
		||||
			}
 | 
			
		||||
		case "":
 | 
			
		||||
			log.Debug("Not starting any websocket proxy")
 | 
			
		||||
			log.Debug().Msg("Not starting any websocket proxy")
 | 
			
		||||
		default:
 | 
			
		||||
			log.Errorf("%s isn't a valid proxy (valid options are {%s})", cfg.ProxyType, socksProxy)
 | 
			
		||||
			log.Error().Msgf("%s isn't a valid proxy (valid options are {%s})", cfg.ProxyType, socksProxy)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		errC <- websocket.StartProxyServer(log, listener, staticHost, shutdownC, streamHandler)
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +203,13 @@ func (o *helloWorld) String() string {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Start starts a HelloWorld server and stores its address in the Service receiver.
 | 
			
		||||
func (o *helloWorld) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (o *helloWorld) start(
 | 
			
		||||
	wg *sync.WaitGroup,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
	shutdownC <-chan struct{},
 | 
			
		||||
	errC chan error,
 | 
			
		||||
	cfg OriginRequestConfig,
 | 
			
		||||
) error {
 | 
			
		||||
	transport, err := newHTTPTransport(o, cfg, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +267,13 @@ func (o *statusCode) String() string {
 | 
			
		|||
	return fmt.Sprintf("HTTP %d", o.resp.StatusCode)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (o *statusCode) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (o *statusCode) start(
 | 
			
		||||
	wg *sync.WaitGroup,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
	shutdownC <-chan struct{},
 | 
			
		||||
	errC chan error,
 | 
			
		||||
	cfg OriginRequestConfig,
 | 
			
		||||
) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -280,7 +292,7 @@ func (nrc *NopReadCloser) Close() error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log logger.Service) (*http.Transport, error) {
 | 
			
		||||
func newHTTPTransport(service OriginService, cfg OriginRequestConfig, log *zerolog.Logger) (*http.Transport, error) {
 | 
			
		||||
	originCertPool, err := tlsconfig.LoadOriginCA(cfg.CAPool, log)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "Error loading cert pool")
 | 
			
		||||
| 
						 | 
				
			
			@ -338,6 +350,6 @@ func (mos MockOriginService) String() string {
 | 
			
		|||
	return "MockOriginService"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mos MockOriginService) start(wg *sync.WaitGroup, log logger.Service, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
func (mos MockOriginService) start(wg *sync.WaitGroup, log *zerolog.Logger, shutdownC <-chan struct{}, errC chan error, cfg OriginRequestConfig) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ type RollingConfig struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func createDefaultConfig() Config {
 | 
			
		||||
	const minLevel = "fatal"
 | 
			
		||||
	const minLevel = "info"
 | 
			
		||||
 | 
			
		||||
	const RollingMaxSize = 1    // Mb
 | 
			
		||||
	const RollingMaxBackups = 5 // files
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,7 @@ func createDefaultConfig() Config {
 | 
			
		|||
func CreateConfig(
 | 
			
		||||
	minLevel string,
 | 
			
		||||
	disableTerminal bool,
 | 
			
		||||
	rollingLogPath, nonRollingLogFilePath string,
 | 
			
		||||
	rollingLogPath, rollingLogFilename, nonRollingLogFilePath string,
 | 
			
		||||
) *Config {
 | 
			
		||||
	var console *ConsoleConfig
 | 
			
		||||
	if !disableTerminal {
 | 
			
		||||
| 
						 | 
				
			
			@ -71,7 +71,7 @@ func CreateConfig(
 | 
			
		|||
 | 
			
		||||
	var rolling *RollingConfig
 | 
			
		||||
	if rollingLogPath != "" {
 | 
			
		||||
		rolling = createRollingConfig(rollingLogPath)
 | 
			
		||||
		rolling = createRollingConfig(rollingLogPath, rollingLogFilename)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if minLevel == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -103,14 +103,14 @@ func createFileConfig(filepath string) *FileConfig {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createRollingConfig(directory string) *RollingConfig {
 | 
			
		||||
func createRollingConfig(directory, filename string) *RollingConfig {
 | 
			
		||||
	if directory == "" {
 | 
			
		||||
		directory = defaultConfig.RollingConfig.Directory
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &RollingConfig{
 | 
			
		||||
		Directory:  directory,
 | 
			
		||||
		Filename:   defaultConfig.RollingConfig.Filename,
 | 
			
		||||
		Filename:   filename,
 | 
			
		||||
		maxSize:    defaultConfig.RollingConfig.maxSize,
 | 
			
		||||
		maxBackups: defaultConfig.RollingConfig.maxBackups,
 | 
			
		||||
		maxAge:     defaultConfig.RollingConfig.maxAge,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										203
									
								
								logger/create.go
								
								
								
								
							
							
						
						
									
										203
									
								
								logger/create.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,13 +1,11 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/alecthomas/units"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	fallbacklog "github.com/rs/zerolog/log"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,162 +22,40 @@ const (
 | 
			
		|||
	LogSSHLevelFlag     = "log-level"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Option is to encaspulate actions that will be called by Parse and run later to build an Options struct
 | 
			
		||||
type Option func(*Options) error
 | 
			
		||||
func newZerolog(loggerConfig *Config) *zerolog.Logger {
 | 
			
		||||
	var writers []io.Writer
 | 
			
		||||
 | 
			
		||||
// Options is use to set logging configuration data
 | 
			
		||||
type Options struct {
 | 
			
		||||
	logFileDirectory        string
 | 
			
		||||
	maxFileSize             units.Base2Bytes
 | 
			
		||||
	maxFileCount            uint
 | 
			
		||||
	terminalOutputDisabled  bool
 | 
			
		||||
	supportedFileLevels     []Level
 | 
			
		||||
	supportedTerminalLevels []Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DisableTerminal stops terminal output for the logger
 | 
			
		||||
func DisableTerminal(disable bool) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		c.terminalOutputDisabled = disable
 | 
			
		||||
		return nil
 | 
			
		||||
	if loggerConfig.ConsoleConfig != nil {
 | 
			
		||||
		writers = append(writers, zerolog.ConsoleWriter{
 | 
			
		||||
			Out:     os.Stderr,
 | 
			
		||||
			NoColor: loggerConfig.ConsoleConfig.noColor,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// File sets a custom file to log events
 | 
			
		||||
func File(path string, size units.Base2Bytes, count uint) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		c.logFileDirectory = path
 | 
			
		||||
		c.maxFileSize = size
 | 
			
		||||
		c.maxFileCount = count
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
	// TODO TUN-3472: Support file writer and log rotation
 | 
			
		||||
 | 
			
		||||
// DefaultFile configures the log options will the defaults
 | 
			
		||||
func DefaultFile(directoryPath string) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		size, err := units.ParseBase2Bytes("1MB")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.logFileDirectory = directoryPath
 | 
			
		||||
		c.maxFileSize = size
 | 
			
		||||
		c.maxFileCount = 5
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SupportedFileLevels sets the supported logging levels for the log file
 | 
			
		||||
func SupportedFileLevels(supported []Level) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		c.supportedFileLevels = supported
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SupportedTerminalevels sets the supported logging levels for the terminal output
 | 
			
		||||
func SupportedTerminalevels(supported []Level) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		c.supportedTerminalLevels = supported
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LogLevelString sets the supported logging levels from a command line flag
 | 
			
		||||
func LogLevelString(level string) Option {
 | 
			
		||||
	return func(c *Options) error {
 | 
			
		||||
		supported, err := ParseLevelString(level)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		c.supportedFileLevels = supported
 | 
			
		||||
		c.supportedTerminalLevels = supported
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse builds the Options struct so the caller knows what actions should be run
 | 
			
		||||
func Parse(opts ...Option) (*Options, error) {
 | 
			
		||||
	options := &Options{}
 | 
			
		||||
	for _, opt := range opts {
 | 
			
		||||
		if err := opt(options); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return options, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New setups a new logger based on the options.
 | 
			
		||||
// The default behavior is to write to standard out
 | 
			
		||||
func New(opts ...Option) (*OutputWriter, error) {
 | 
			
		||||
	options, err := Parse(opts...)
 | 
			
		||||
	multi := zerolog.MultiLevelWriter(writers...)
 | 
			
		||||
 | 
			
		||||
	level, err := zerolog.ParseLevel(loggerConfig.MinLevel)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		failLog := fallbacklog.With().Logger()
 | 
			
		||||
		fallbacklog.Error().Msgf("Falling back to a default logger due to logger setup failure: %s", err)
 | 
			
		||||
		return &failLog
 | 
			
		||||
	}
 | 
			
		||||
	log := zerolog.New(multi).With().Timestamp().Logger().Level(level)
 | 
			
		||||
 | 
			
		||||
	l := NewOutputWriter(SharedWriteManager)
 | 
			
		||||
	if options.logFileDirectory != "" {
 | 
			
		||||
		l.Add(NewFileRollingWriter(SanitizeLogPath(options.logFileDirectory),
 | 
			
		||||
			"cloudflared",
 | 
			
		||||
			int64(options.maxFileSize),
 | 
			
		||||
			options.maxFileCount),
 | 
			
		||||
			NewDefaultFormatter(time.RFC3339Nano), options.supportedFileLevels...)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !options.terminalOutputDisabled {
 | 
			
		||||
		terminalFormatter := NewTerminalFormatter(time.RFC3339)
 | 
			
		||||
 | 
			
		||||
		if len(options.supportedTerminalLevels) == 0 {
 | 
			
		||||
			l.Add(os.Stderr, terminalFormatter, InfoLevel, ErrorLevel, FatalLevel)
 | 
			
		||||
		} else {
 | 
			
		||||
			l.Add(os.Stderr, terminalFormatter, options.supportedTerminalLevels...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return l, nil
 | 
			
		||||
	return &log
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewInHouse(loggerConfig *Config) (*OutputWriter, error) {
 | 
			
		||||
	var loggerOpts []Option
 | 
			
		||||
 | 
			
		||||
	var logPath string
 | 
			
		||||
	if loggerConfig.FileConfig != nil {
 | 
			
		||||
		logPath = loggerConfig.FileConfig.Filepath
 | 
			
		||||
	}
 | 
			
		||||
	if logPath == "" && loggerConfig.RollingConfig != nil {
 | 
			
		||||
		logPath = loggerConfig.RollingConfig.Directory
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if logPath != "" {
 | 
			
		||||
		loggerOpts = append(loggerOpts, DefaultFile(logPath))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	loggerOpts = append(loggerOpts, LogLevelString(loggerConfig.MinLevel))
 | 
			
		||||
 | 
			
		||||
	if loggerConfig.ConsoleConfig == nil {
 | 
			
		||||
		disableOption := DisableTerminal(true)
 | 
			
		||||
		loggerOpts = append(loggerOpts, disableOption)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	l, err := New(loggerOpts...)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return l, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateTransportLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) {
 | 
			
		||||
func CreateTransportLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
 | 
			
		||||
	return createFromContext(c, LogTransportLevelFlag, LogDirectoryFlag, disableTerminal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) {
 | 
			
		||||
func CreateLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
 | 
			
		||||
	return createFromContext(c, LogLevelFlag, LogDirectoryFlag, disableTerminal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func CreateSSHLoggerFromContext(c *cli.Context, disableTerminal bool) (*OutputWriter, error) {
 | 
			
		||||
func CreateSSHLoggerFromContext(c *cli.Context, disableTerminal bool) *zerolog.Logger {
 | 
			
		||||
	return createFromContext(c, LogSSHLevelFlag, LogSSHDirectoryFlag, disableTerminal)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -188,37 +64,26 @@ func createFromContext(
 | 
			
		|||
	logLevelFlagName,
 | 
			
		||||
	logDirectoryFlagName string,
 | 
			
		||||
	disableTerminal bool,
 | 
			
		||||
) (*OutputWriter, error) {
 | 
			
		||||
) *zerolog.Logger {
 | 
			
		||||
	logLevel := c.String(logLevelFlagName)
 | 
			
		||||
	logFile := c.String(LogFileFlag)
 | 
			
		||||
	logDirectory := c.String(logDirectoryFlagName)
 | 
			
		||||
 | 
			
		||||
	loggerConfig := CreateConfig(logLevel, disableTerminal, logDirectory, logFile)
 | 
			
		||||
	loggerConfig := CreateConfig(
 | 
			
		||||
		logLevel,
 | 
			
		||||
		disableTerminal,
 | 
			
		||||
		logDirectory,
 | 
			
		||||
		defaultConfig.RollingConfig.Filename,
 | 
			
		||||
		logFile,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	return NewInHouse(loggerConfig)
 | 
			
		||||
	return newZerolog(loggerConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParseLevelString returns the expected log levels based on the cmd flag
 | 
			
		||||
func ParseLevelString(lvl string) ([]Level, error) {
 | 
			
		||||
	switch strings.ToLower(lvl) {
 | 
			
		||||
	case "fatal":
 | 
			
		||||
		return []Level{FatalLevel}, nil
 | 
			
		||||
	case "error":
 | 
			
		||||
		return []Level{FatalLevel, ErrorLevel}, nil
 | 
			
		||||
	case "info", "warn":
 | 
			
		||||
		return []Level{FatalLevel, ErrorLevel, InfoLevel}, nil
 | 
			
		||||
	case "debug":
 | 
			
		||||
		return []Level{FatalLevel, ErrorLevel, InfoLevel, DebugLevel}, nil
 | 
			
		||||
func Create(loggerConfig *Config) *zerolog.Logger {
 | 
			
		||||
	if loggerConfig == nil {
 | 
			
		||||
		loggerConfig = &defaultConfig
 | 
			
		||||
	}
 | 
			
		||||
	return []Level{}, fmt.Errorf("not a valid log level: %q", lvl)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SanitizeLogPath checks that the logger log path
 | 
			
		||||
func SanitizeLogPath(path string) string {
 | 
			
		||||
	newPath := strings.TrimSpace(path)
 | 
			
		||||
	// make sure it has a log file extension and is not a directory
 | 
			
		||||
	if filepath.Ext(newPath) != ".log" && !(isDirectory(newPath) || strings.HasSuffix(newPath, "/")) {
 | 
			
		||||
		newPath = newPath + ".log"
 | 
			
		||||
	}
 | 
			
		||||
	return newPath
 | 
			
		||||
	return newZerolog(loggerConfig)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,46 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLogLevelParse(t *testing.T) {
 | 
			
		||||
	lvls, err := ParseLevelString("fatal")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	lvls, err = ParseLevelString("error")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel, ErrorLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	lvls, err = ParseLevelString("info")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel, ErrorLevel, InfoLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	lvls, err = ParseLevelString("info")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel, ErrorLevel, InfoLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	lvls, err = ParseLevelString("warn")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel, ErrorLevel, InfoLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	lvls, err = ParseLevelString("debug")
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, []Level{FatalLevel, ErrorLevel, InfoLevel, DebugLevel}, lvls)
 | 
			
		||||
 | 
			
		||||
	_, err = ParseLevelString("blah")
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
 | 
			
		||||
	_, err = ParseLevelString("")
 | 
			
		||||
	assert.Error(t, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPathSanitizer(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, "somebad/path/log.bat.log", SanitizeLogPath("\t somebad/path/log.bat\n\n"))
 | 
			
		||||
	assert.Equal(t, "proper/path/cloudflared.log", SanitizeLogPath("proper/path/cloudflared.log"))
 | 
			
		||||
	assert.Equal(t, "proper/path/", SanitizeLogPath("proper/path/"))
 | 
			
		||||
	assert.Equal(t, "proper/path/cloudflared.log", SanitizeLogPath("\tproper/path/cloudflared\n\n"))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,125 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FileRollingWriter maintains a set of log files numbered in order
 | 
			
		||||
// to keep a subset of log data to ensure it doesn't grow pass defined limits
 | 
			
		||||
type FileRollingWriter struct {
 | 
			
		||||
	baseFileName string
 | 
			
		||||
	directory    string
 | 
			
		||||
	maxFileSize  int64
 | 
			
		||||
	maxFileCount uint
 | 
			
		||||
	fileHandle   *os.File
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFileRollingWriter creates a new rolling file writer.
 | 
			
		||||
// directory is the working directory for the files
 | 
			
		||||
// baseFileName is the log file name. This writer appends .log to the name for the file name
 | 
			
		||||
// maxFileSize is the size in bytes of how large each file can be. Not a hard limit, general limit based after each write
 | 
			
		||||
// maxFileCount is the number of rolled files to keep.
 | 
			
		||||
func NewFileRollingWriter(directory, baseFileName string, maxFileSize int64, maxFileCount uint) *FileRollingWriter {
 | 
			
		||||
	return &FileRollingWriter{
 | 
			
		||||
		directory:    directory,
 | 
			
		||||
		baseFileName: baseFileName,
 | 
			
		||||
		maxFileSize:  maxFileSize,
 | 
			
		||||
		maxFileCount: maxFileCount,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write is an implementation of io.writer the rolls the file once it reaches its max size
 | 
			
		||||
// It is expected the caller to Write is doing so in a thread safe manner (as WriteManager does).
 | 
			
		||||
func (w *FileRollingWriter) Write(p []byte) (n int, err error) {
 | 
			
		||||
	logFile, isSingleFile := buildPath(w.directory, w.baseFileName)
 | 
			
		||||
	if w.fileHandle == nil {
 | 
			
		||||
		h, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		w.fileHandle = h
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// get size for rolling check
 | 
			
		||||
	info, err := w.fileHandle.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// failed to stat the file. Close the file handle and attempt to open a new handle on the next write
 | 
			
		||||
		w.Close()
 | 
			
		||||
		w.fileHandle = nil
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// write to the file
 | 
			
		||||
	written, err := w.fileHandle.Write(p)
 | 
			
		||||
 | 
			
		||||
	// check if the file needs to be rolled
 | 
			
		||||
	if err == nil && info.Size()+int64(written) > w.maxFileSize && !isSingleFile {
 | 
			
		||||
		// close the file handle than do the renaming. A new one will be opened on the next write
 | 
			
		||||
		w.Close()
 | 
			
		||||
		w.rename(logFile, 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return written, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the file handle if it is open
 | 
			
		||||
func (w *FileRollingWriter) Close() {
 | 
			
		||||
	if w.fileHandle != nil {
 | 
			
		||||
		w.fileHandle.Close()
 | 
			
		||||
		w.fileHandle = nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rename is how the files are rolled. It works recursively to move the base log file to the rolled ones
 | 
			
		||||
// e.g. cloudflared.log -> cloudflared-1.log,
 | 
			
		||||
// but if cloudflared-1.log already exists, it is renamed to cloudflared-2.log,
 | 
			
		||||
// then the other files move in to their postion
 | 
			
		||||
func (w *FileRollingWriter) rename(sourcePath string, index uint) {
 | 
			
		||||
	destinationPath, isSingleFile := buildPath(w.directory, fmt.Sprintf("%s-%d", w.baseFileName, index))
 | 
			
		||||
	if isSingleFile {
 | 
			
		||||
		return //don't need to rename anything, it is a single file
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// rolled to the max amount of files allowed on disk
 | 
			
		||||
	if index >= w.maxFileCount {
 | 
			
		||||
		os.Remove(destinationPath)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if the rolled path already exist, rename it to cloudflared-2.log, then do this one.
 | 
			
		||||
	// recursive call since the oldest one needs to be renamed, before the newer ones can be moved
 | 
			
		||||
	if exists(destinationPath) {
 | 
			
		||||
		w.rename(destinationPath, index+1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	os.Rename(sourcePath, destinationPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// return the path to the log file and if it is a single file or not.
 | 
			
		||||
// true means a single file. false means a rolled file
 | 
			
		||||
func buildPath(directory, fileName string) (string, bool) {
 | 
			
		||||
	if !isDirectory(directory) { // not a directory, so try and treat it as a single file for backwards compatibility sake
 | 
			
		||||
		return directory, true
 | 
			
		||||
	}
 | 
			
		||||
	return filepath.Join(directory, fileName+".log"), false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exists(filePath string) bool {
 | 
			
		||||
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isDirectory(path string) bool {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fileInfo, err := os.Stat(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return fileInfo.IsDir()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,108 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestFileWrite(t *testing.T) {
 | 
			
		||||
	fileName := "test_file"
 | 
			
		||||
	fileLog := fileName + ".log"
 | 
			
		||||
	testData := []byte(string("hello Dalton, how are you doing?"))
 | 
			
		||||
	defer func() {
 | 
			
		||||
		os.Remove(fileLog)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	w := NewFileRollingWriter("", fileName, 1000, 2)
 | 
			
		||||
	defer w.Close()
 | 
			
		||||
 | 
			
		||||
	l, err := w.Write(testData)
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, l, len(testData), "expected write length and data length to match")
 | 
			
		||||
 | 
			
		||||
	d, err := ioutil.ReadFile(fileLog)
 | 
			
		||||
	assert.FileExists(t, fileLog, "file doesn't exist at expected path")
 | 
			
		||||
	assert.Equal(t, d, testData, "expected data in file to match test data")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRolling(t *testing.T) {
 | 
			
		||||
	dirName := "testdir"
 | 
			
		||||
	err := os.Mkdir(dirName, 0755)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	fileName := "test_file"
 | 
			
		||||
	firstFile := filepath.Join(dirName, fileName+".log")
 | 
			
		||||
	secondFile := filepath.Join(dirName, fileName+"-1.log")
 | 
			
		||||
	thirdFile := filepath.Join(dirName, fileName+"-2.log")
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
		os.RemoveAll(dirName)
 | 
			
		||||
		os.Remove(firstFile)
 | 
			
		||||
		os.Remove(secondFile)
 | 
			
		||||
		os.Remove(thirdFile)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	w := NewFileRollingWriter(dirName, fileName, 1000, 2)
 | 
			
		||||
	defer w.Close()
 | 
			
		||||
 | 
			
		||||
	for i := 99; i >= 1; i-- {
 | 
			
		||||
		testData := []byte(fmt.Sprintf("%d bottles of beer on the wall...", i))
 | 
			
		||||
		w.Write(testData)
 | 
			
		||||
	}
 | 
			
		||||
	assert.FileExists(t, firstFile, "first file doesn't exist as expected")
 | 
			
		||||
	assert.FileExists(t, secondFile, "second file doesn't exist as expected")
 | 
			
		||||
	assert.FileExists(t, thirdFile, "third file doesn't exist as expected")
 | 
			
		||||
	assert.False(t, exists(filepath.Join(dirName, fileName+"-3.log")), "limited to two files and there is more")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSingleFile(t *testing.T) {
 | 
			
		||||
	fileName := "test_file"
 | 
			
		||||
	testData := []byte(string("hello Dalton, how are you doing?"))
 | 
			
		||||
	defer func() {
 | 
			
		||||
		os.Remove(fileName)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	w := NewFileRollingWriter(fileName, fileName, 1000, 2)
 | 
			
		||||
	defer w.Close()
 | 
			
		||||
 | 
			
		||||
	l, err := w.Write(testData)
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, l, len(testData), "expected write length and data length to match")
 | 
			
		||||
 | 
			
		||||
	d, err := ioutil.ReadFile(fileName)
 | 
			
		||||
	assert.FileExists(t, fileName, "file doesn't exist at expected path")
 | 
			
		||||
	assert.Equal(t, d, testData, "expected data in file to match test data")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSingleFileInDirectory(t *testing.T) {
 | 
			
		||||
	dirName := "testdir"
 | 
			
		||||
	err := os.Mkdir(dirName, 0755)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	fileName := "test_file"
 | 
			
		||||
	fullPath := filepath.Join(dirName, fileName+".log")
 | 
			
		||||
	testData := []byte(string("hello Dalton, how are you doing?"))
 | 
			
		||||
	defer func() {
 | 
			
		||||
		os.Remove(fullPath)
 | 
			
		||||
		os.RemoveAll(dirName)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	w := NewFileRollingWriter(fullPath, fileName, 1000, 2)
 | 
			
		||||
	defer w.Close()
 | 
			
		||||
 | 
			
		||||
	l, err := w.Write(testData)
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	assert.Equal(t, l, len(testData), "expected write length and data length to match")
 | 
			
		||||
 | 
			
		||||
	d, err := ioutil.ReadFile(fullPath)
 | 
			
		||||
	assert.FileExists(t, fullPath, "file doesn't exist at expected path")
 | 
			
		||||
	assert.Equal(t, d, testData, "expected data in file to match test data")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,138 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/acmacalister/skittles"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Level of logging, lower number means more verbose logging, higher more terse
 | 
			
		||||
type Level int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// DebugLevel is for messages that are intended for purposes debugging only
 | 
			
		||||
	DebugLevel Level = iota
 | 
			
		||||
 | 
			
		||||
	// InfoLevel is for standard log messages
 | 
			
		||||
	InfoLevel
 | 
			
		||||
 | 
			
		||||
	// ErrorLevel is for error message to indicate something has gone wrong
 | 
			
		||||
	ErrorLevel
 | 
			
		||||
 | 
			
		||||
	// FatalLevel is for error message that log and kill the program with an os.exit(1)
 | 
			
		||||
	FatalLevel
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Formatter is the base interface for formatting logging messages before writing them out
 | 
			
		||||
type Formatter interface {
 | 
			
		||||
	Timestamp(Level, time.Time) string // format the timestamp string
 | 
			
		||||
	Content(Level, string) string      // format content string (color for terminal, etc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DefaultFormatter writes a simple structure timestamp and the message per log line
 | 
			
		||||
type DefaultFormatter struct {
 | 
			
		||||
	format string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewDefaultFormatter creates the standard log formatter
 | 
			
		||||
// format is the time format to use for timestamp formatting
 | 
			
		||||
func NewDefaultFormatter(format string) Formatter {
 | 
			
		||||
	return &DefaultFormatter{
 | 
			
		||||
		format: format,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Timestamp formats a log line timestamp with a brackets around them
 | 
			
		||||
func (f *DefaultFormatter) Timestamp(l Level, d time.Time) string {
 | 
			
		||||
	if f.format == "" {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("[%s]: ", d.Format(f.format))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Content just writes the log line straight to the sources
 | 
			
		||||
func (f *DefaultFormatter) Content(l Level, c string) string {
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TerminalFormatter is setup for colored output
 | 
			
		||||
type TerminalFormatter struct {
 | 
			
		||||
	format        string
 | 
			
		||||
	supportsColor bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UIFormatter is used for streaming logs to UI
 | 
			
		||||
type UIFormatter struct {
 | 
			
		||||
	format        string
 | 
			
		||||
	supportsColor bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewTerminalFormatter creates a Terminal formatter for colored output
 | 
			
		||||
// format is the time format to use for timestamp formatting
 | 
			
		||||
func NewTerminalFormatter(format string) Formatter {
 | 
			
		||||
	supportsColor := (runtime.GOOS != "windows")
 | 
			
		||||
	return &TerminalFormatter{
 | 
			
		||||
		format:        format,
 | 
			
		||||
		supportsColor: supportsColor,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewUIFormatter(format string) Formatter {
 | 
			
		||||
	supportsColor := (runtime.GOOS != "windows")
 | 
			
		||||
	return &UIFormatter{
 | 
			
		||||
		format:        format,
 | 
			
		||||
		supportsColor: supportsColor,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Timestamp uses formatting that is tview-specific for UI
 | 
			
		||||
func (f *UIFormatter) Timestamp(l Level, d time.Time) string {
 | 
			
		||||
	t := ""
 | 
			
		||||
	dateStr := "[" + d.Format(f.format) + "] "
 | 
			
		||||
	switch l {
 | 
			
		||||
	case InfoLevel:
 | 
			
		||||
		t = "[#00ffff]INFO[white]"
 | 
			
		||||
	case ErrorLevel:
 | 
			
		||||
		t = "[red]ERROR[white]"
 | 
			
		||||
	case DebugLevel:
 | 
			
		||||
		t = "[yellow]DEBUG[white]"
 | 
			
		||||
	case FatalLevel:
 | 
			
		||||
		t = "[red]FATAL[white]"
 | 
			
		||||
	}
 | 
			
		||||
	return t + dateStr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *UIFormatter) Content(l Level, c string) string {
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Timestamp returns the log level with a matching color to the log type
 | 
			
		||||
func (f *TerminalFormatter) Timestamp(l Level, d time.Time) string {
 | 
			
		||||
	t := ""
 | 
			
		||||
	dateStr := "[" + d.Format(f.format) + "] "
 | 
			
		||||
	switch l {
 | 
			
		||||
	case InfoLevel:
 | 
			
		||||
		t = f.output("INFO", skittles.Cyan)
 | 
			
		||||
	case ErrorLevel:
 | 
			
		||||
		t = f.output("ERROR", skittles.Red)
 | 
			
		||||
	case DebugLevel:
 | 
			
		||||
		t = f.output("DEBUG", skittles.Yellow)
 | 
			
		||||
	case FatalLevel:
 | 
			
		||||
		t = f.output("FATAL", skittles.Red)
 | 
			
		||||
	}
 | 
			
		||||
	return t + dateStr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Content just writes the log line straight to the sources
 | 
			
		||||
func (f *TerminalFormatter) Content(l Level, c string) string {
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *TerminalFormatter) output(msg string, colorFunc func(interface{}) string) string {
 | 
			
		||||
	if f.supportsColor {
 | 
			
		||||
		return colorFunc(msg)
 | 
			
		||||
	}
 | 
			
		||||
	return msg
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,59 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import "sync"
 | 
			
		||||
 | 
			
		||||
// SharedWriteManager is a package level variable to allows multiple loggers to use the same write manager.
 | 
			
		||||
// This is useful when multiple loggers will write to the same file to ensure they don't clobber each other.
 | 
			
		||||
var SharedWriteManager = NewWriteManager()
 | 
			
		||||
 | 
			
		||||
type writeData struct {
 | 
			
		||||
	target LogOutput
 | 
			
		||||
	data   []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteManager is a logging service that handles managing multiple writing streams
 | 
			
		||||
type WriteManager struct {
 | 
			
		||||
	shutdown  chan struct{}
 | 
			
		||||
	writeChan chan writeData
 | 
			
		||||
	writers   map[string]Service
 | 
			
		||||
	wg        sync.WaitGroup
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWriteManager creates a write manager that implements OutputManager
 | 
			
		||||
func NewWriteManager() OutputManager {
 | 
			
		||||
	m := &WriteManager{
 | 
			
		||||
		shutdown:  make(chan struct{}),
 | 
			
		||||
		writeChan: make(chan writeData, 1000),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go m.run()
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append adds a message to the writer runloop
 | 
			
		||||
func (m *WriteManager) Append(data []byte, target LogOutput) {
 | 
			
		||||
	m.wg.Add(1)
 | 
			
		||||
	m.writeChan <- writeData{data: data, target: target}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shutdown stops the sync manager service
 | 
			
		||||
func (m *WriteManager) Shutdown() {
 | 
			
		||||
	m.wg.Wait()
 | 
			
		||||
	close(m.shutdown)
 | 
			
		||||
	close(m.writeChan)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// run is the main runloop that schedules log messages
 | 
			
		||||
func (m *WriteManager) run() {
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case event, ok := <-m.writeChan:
 | 
			
		||||
			if ok {
 | 
			
		||||
				event.target.WriteLogLine(event.data)
 | 
			
		||||
				m.wg.Done()
 | 
			
		||||
			}
 | 
			
		||||
		case <-m.shutdown:
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,24 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type outputFunc func(b []byte)
 | 
			
		||||
 | 
			
		||||
func (f outputFunc) WriteLogLine(data []byte) {
 | 
			
		||||
	f(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWriteManger(t *testing.T) {
 | 
			
		||||
	testData := []byte(string("hello Austin, how are you doing?"))
 | 
			
		||||
	waitChan := make(chan []byte)
 | 
			
		||||
	m := NewWriteManager()
 | 
			
		||||
	m.Append(testData, outputFunc(func(b []byte) {
 | 
			
		||||
		waitChan <- b
 | 
			
		||||
	}))
 | 
			
		||||
	resp := <-waitChan
 | 
			
		||||
	assert.Equal(t, testData, resp)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,18 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
// MockWriteManager does nothing and is provided for testing purposes
 | 
			
		||||
type MockWriteManager struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewMockWriteManager creates an OutputManager that does nothing for testing purposes
 | 
			
		||||
func NewMockWriteManager() OutputManager {
 | 
			
		||||
	return &MockWriteManager{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Append is a mock stub
 | 
			
		||||
func (m *MockWriteManager) Append(data []byte, target LogOutput) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Shutdown is a mock stub
 | 
			
		||||
func (m *MockWriteManager) Shutdown() {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										157
									
								
								logger/output.go
								
								
								
								
							
							
						
						
									
										157
									
								
								logger/output.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,157 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// provided for testing
 | 
			
		||||
var osExit = os.Exit
 | 
			
		||||
 | 
			
		||||
type LogOutput interface {
 | 
			
		||||
	WriteLogLine([]byte)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OutputManager is used to sync data of Output
 | 
			
		||||
type OutputManager interface {
 | 
			
		||||
	Append([]byte, LogOutput)
 | 
			
		||||
	Shutdown()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Service is the logging service that is either a group or single log writer
 | 
			
		||||
type Service interface {
 | 
			
		||||
	Error(message string)
 | 
			
		||||
	Info(message string)
 | 
			
		||||
	Debug(message string)
 | 
			
		||||
	Fatal(message string)
 | 
			
		||||
 | 
			
		||||
	Errorf(format string, args ...interface{})
 | 
			
		||||
	Infof(format string, args ...interface{})
 | 
			
		||||
	Debugf(format string, args ...interface{})
 | 
			
		||||
	Fatalf(format string, args ...interface{})
 | 
			
		||||
 | 
			
		||||
	Add(writer io.Writer, formatter Formatter, levels ...Level)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type sourceGroup struct {
 | 
			
		||||
	writer          io.Writer
 | 
			
		||||
	formatter       Formatter
 | 
			
		||||
	levelsSupported []Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *sourceGroup) WriteLogLine(data []byte) {
 | 
			
		||||
	_, _ = s.writer.Write(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *sourceGroup) supportsLevel(l Level) bool {
 | 
			
		||||
	for _, level := range s.levelsSupported {
 | 
			
		||||
		if l == level {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OutputWriter is the standard logging implementation
 | 
			
		||||
type OutputWriter struct {
 | 
			
		||||
	groups     []*sourceGroup
 | 
			
		||||
	syncWriter OutputManager
 | 
			
		||||
	minLevel   Level
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewOutputWriter creates a new logger
 | 
			
		||||
func NewOutputWriter(syncWriter OutputManager) *OutputWriter {
 | 
			
		||||
	return &OutputWriter{
 | 
			
		||||
		syncWriter: syncWriter,
 | 
			
		||||
		groups:     nil,
 | 
			
		||||
		minLevel:   FatalLevel,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add a writer and formatter to output to
 | 
			
		||||
func (s *OutputWriter) Add(writer io.Writer, formatter Formatter, levels ...Level) {
 | 
			
		||||
	s.groups = append(s.groups, &sourceGroup{writer: writer, formatter: formatter, levelsSupported: levels})
 | 
			
		||||
 | 
			
		||||
	// track most verbose (lowest) level we need to output
 | 
			
		||||
	for _, level := range levels {
 | 
			
		||||
		if level < s.minLevel {
 | 
			
		||||
			s.minLevel = level
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Error writes an error to the logging sources
 | 
			
		||||
func (s *OutputWriter) Error(message string) {
 | 
			
		||||
	if s.minLevel <= ErrorLevel {
 | 
			
		||||
		s.output(ErrorLevel, message)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info writes an info string to the logging sources
 | 
			
		||||
func (s *OutputWriter) Info(message string) {
 | 
			
		||||
	if s.minLevel <= InfoLevel {
 | 
			
		||||
		s.output(InfoLevel, message)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debug writes a debug string to the logging sources
 | 
			
		||||
func (s *OutputWriter) Debug(message string) {
 | 
			
		||||
	if s.minLevel <= DebugLevel {
 | 
			
		||||
		s.output(DebugLevel, message)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatal writes a error string to the logging sources and runs does an os.exit()
 | 
			
		||||
func (s *OutputWriter) Fatal(message string) {
 | 
			
		||||
	s.output(FatalLevel, message)
 | 
			
		||||
	s.syncWriter.Shutdown() // waits for the pending logging to finish
 | 
			
		||||
	osExit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Errorf writes a formatted error to the logging sources
 | 
			
		||||
func (s *OutputWriter) Errorf(format string, args ...interface{}) {
 | 
			
		||||
	if s.minLevel <= ErrorLevel {
 | 
			
		||||
		s.output(ErrorLevel, fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Infof writes a formatted info statement to the logging sources
 | 
			
		||||
func (s *OutputWriter) Infof(format string, args ...interface{}) {
 | 
			
		||||
	if s.minLevel <= InfoLevel {
 | 
			
		||||
		s.output(InfoLevel, fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugf writes a formatted debug statement to the logging sources
 | 
			
		||||
func (s *OutputWriter) Debugf(format string, args ...interface{}) {
 | 
			
		||||
	if s.minLevel <= DebugLevel {
 | 
			
		||||
		s.output(DebugLevel, fmt.Sprintf(format, args...))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Fatalf writes a  writes a formatted error statement and runs does an os.exit()
 | 
			
		||||
func (s *OutputWriter) Fatalf(format string, args ...interface{}) {
 | 
			
		||||
	s.output(FatalLevel, fmt.Sprintf(format, args...))
 | 
			
		||||
	s.syncWriter.Shutdown() // waits for the pending logging to finish
 | 
			
		||||
	osExit(1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// output does the actual write to the sync manager
 | 
			
		||||
func (s *OutputWriter) output(l Level, content string) {
 | 
			
		||||
	now := time.Now()
 | 
			
		||||
	for _, group := range s.groups {
 | 
			
		||||
		if group.supportsLevel(l) {
 | 
			
		||||
			logLine := fmt.Sprintf("%s%s\n", group.formatter.Timestamp(l, now),
 | 
			
		||||
				group.formatter.Content(l, content))
 | 
			
		||||
			s.syncWriter.Append([]byte(logLine), group)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Write implements io.Writer to support SetOutput of the log package
 | 
			
		||||
func (s *OutputWriter) Write(p []byte) (n int, err error) {
 | 
			
		||||
	s.Info(string(p))
 | 
			
		||||
	return len(p), nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,106 +0,0 @@
 | 
			
		|||
package logger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestLogLevel(t *testing.T) {
 | 
			
		||||
	timeFormat := "2006-01-02"
 | 
			
		||||
	f := NewDefaultFormatter(timeFormat)
 | 
			
		||||
	m := NewWriteManager()
 | 
			
		||||
 | 
			
		||||
	var testBuffer bytes.Buffer
 | 
			
		||||
	logger := NewOutputWriter(m)
 | 
			
		||||
	logger.Add(&testBuffer, f, InfoLevel, DebugLevel)
 | 
			
		||||
 | 
			
		||||
	testTime := f.Timestamp(InfoLevel, time.Now())
 | 
			
		||||
 | 
			
		||||
	testInfo := "hello Dalton, how are you doing?"
 | 
			
		||||
	logger.Info(testInfo)
 | 
			
		||||
 | 
			
		||||
	tesErr := "hello Austin, how did it break today?"
 | 
			
		||||
	logger.Error(tesErr)
 | 
			
		||||
 | 
			
		||||
	testDebug := "hello Bill, who are you?"
 | 
			
		||||
	logger.Debug(testDebug)
 | 
			
		||||
 | 
			
		||||
	m.Shutdown()
 | 
			
		||||
 | 
			
		||||
	lines := strings.Split(testBuffer.String(), "\n")
 | 
			
		||||
	assert.Len(t, lines, 3, "only expected two strings in the buffer")
 | 
			
		||||
 | 
			
		||||
	infoLine := lines[0]
 | 
			
		||||
	debugLine := lines[1]
 | 
			
		||||
 | 
			
		||||
	compareInfo := fmt.Sprintf("%s%s", testTime, testInfo)
 | 
			
		||||
	assert.Equal(t, compareInfo, infoLine, "expect the strings to match")
 | 
			
		||||
 | 
			
		||||
	compareDebug := fmt.Sprintf("%s%s", testTime, testDebug)
 | 
			
		||||
	assert.Equal(t, compareDebug, debugLine, "expect the strings to match")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestOutputWrite(t *testing.T) {
 | 
			
		||||
	timeFormat := "2006-01-02"
 | 
			
		||||
	f := NewDefaultFormatter(timeFormat)
 | 
			
		||||
	m := NewWriteManager()
 | 
			
		||||
 | 
			
		||||
	var testBuffer bytes.Buffer
 | 
			
		||||
	logger := NewOutputWriter(m)
 | 
			
		||||
	logger.Add(&testBuffer, f, InfoLevel)
 | 
			
		||||
 | 
			
		||||
	logger.Debugf("debug message not logged here")
 | 
			
		||||
 | 
			
		||||
	testData := "hello Bob Bork, how are you doing?"
 | 
			
		||||
	logger.Info(testData)
 | 
			
		||||
	testTime := f.Timestamp(InfoLevel, time.Now())
 | 
			
		||||
 | 
			
		||||
	m.Shutdown()
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(&testBuffer)
 | 
			
		||||
	scanner.Scan()
 | 
			
		||||
	line := scanner.Text()
 | 
			
		||||
	assert.NoError(t, scanner.Err())
 | 
			
		||||
 | 
			
		||||
	compareLine := fmt.Sprintf("%s%s", testTime, testData)
 | 
			
		||||
	assert.Equal(t, compareLine, line, "expect the strings to match")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestFatalWrite(t *testing.T) {
 | 
			
		||||
	timeFormat := "2006-01-02"
 | 
			
		||||
	f := NewDefaultFormatter(timeFormat)
 | 
			
		||||
	m := NewWriteManager()
 | 
			
		||||
 | 
			
		||||
	var testBuffer bytes.Buffer
 | 
			
		||||
	logger := NewOutputWriter(m)
 | 
			
		||||
	logger.Add(&testBuffer, f, FatalLevel)
 | 
			
		||||
 | 
			
		||||
	oldOsExit := osExit
 | 
			
		||||
	defer func() { osExit = oldOsExit }()
 | 
			
		||||
 | 
			
		||||
	var got int
 | 
			
		||||
	myExit := func(code int) {
 | 
			
		||||
		got = code
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	osExit = myExit
 | 
			
		||||
 | 
			
		||||
	testData := "so long y'all"
 | 
			
		||||
	logger.Fatal(testData)
 | 
			
		||||
	testTime := f.Timestamp(FatalLevel, time.Now())
 | 
			
		||||
 | 
			
		||||
	scanner := bufio.NewScanner(&testBuffer)
 | 
			
		||||
	scanner.Scan()
 | 
			
		||||
	line := scanner.Text()
 | 
			
		||||
	assert.NoError(t, scanner.Err())
 | 
			
		||||
 | 
			
		||||
	compareLine := fmt.Sprintf("%s%s", testTime, testData)
 | 
			
		||||
	assert.Equal(t, compareLine, line, "expect the strings to match")
 | 
			
		||||
	assert.Equal(t, got, 1, "exit code should be one for a fatal log")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -10,12 +10,12 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/trace"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus/promhttp"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/net/trace"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -23,12 +23,12 @@ const (
 | 
			
		|||
	startupTime     = time.Millisecond * 500
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newMetricsHandler(connectionEvents <-chan connection.Event, log logger.Service) *http.ServeMux {
 | 
			
		||||
func newMetricsHandler(connectionEvents <-chan connection.Event, log *zerolog.Logger) *http.ServeMux {
 | 
			
		||||
	readyServer := NewReadyServer(connectionEvents, log)
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux.Handle("/metrics", promhttp.Handler())
 | 
			
		||||
	mux.HandleFunc("/healthcheck", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		fmt.Fprintf(w, "OK\n")
 | 
			
		||||
		_, _ = fmt.Fprintf(w, "OK\n")
 | 
			
		||||
	})
 | 
			
		||||
	mux.Handle("/ready", readyServer)
 | 
			
		||||
	return mux
 | 
			
		||||
| 
						 | 
				
			
			@ -38,14 +38,14 @@ func ServeMetrics(
 | 
			
		|||
	l net.Listener,
 | 
			
		||||
	shutdownC <-chan struct{},
 | 
			
		||||
	connectionEvents <-chan connection.Event,
 | 
			
		||||
	logger logger.Service,
 | 
			
		||||
	log *zerolog.Logger,
 | 
			
		||||
) (err error) {
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	// Metrics port is privileged, so no need for further access control
 | 
			
		||||
	trace.AuthRequest = func(*http.Request) (bool, bool) { return true, true }
 | 
			
		||||
	// TODO: parameterize ReadTimeout and WriteTimeout. The maximum time we can
 | 
			
		||||
	// profile CPU usage depends on WriteTimeout
 | 
			
		||||
	h := newMetricsHandler(connectionEvents, logger)
 | 
			
		||||
	h := newMetricsHandler(connectionEvents, log)
 | 
			
		||||
	server := &http.Server{
 | 
			
		||||
		ReadTimeout:  10 * time.Second,
 | 
			
		||||
		WriteTimeout: 10 * time.Second,
 | 
			
		||||
| 
						 | 
				
			
			@ -57,22 +57,22 @@ func ServeMetrics(
 | 
			
		|||
		defer wg.Done()
 | 
			
		||||
		err = server.Serve(l)
 | 
			
		||||
	}()
 | 
			
		||||
	logger.Infof("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr()))
 | 
			
		||||
	log.Info().Msgf("Starting metrics server on %s", fmt.Sprintf("%v/metrics", l.Addr()))
 | 
			
		||||
	// server.Serve will hang if server.Shutdown is called before the server is
 | 
			
		||||
	// fully started up. So add artificial delay.
 | 
			
		||||
	time.Sleep(startupTime)
 | 
			
		||||
 | 
			
		||||
	<-shutdownC
 | 
			
		||||
	ctx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
 | 
			
		||||
	server.Shutdown(ctx)
 | 
			
		||||
	_ = server.Shutdown(ctx)
 | 
			
		||||
	cancel()
 | 
			
		||||
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if err == http.ErrServerClosed {
 | 
			
		||||
		logger.Info("Metrics server stopped")
 | 
			
		||||
		log.Info().Msg("Metrics server stopped")
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	logger.Errorf("Metrics server quit with error: %s", err)
 | 
			
		||||
	log.Error().Msgf("Metrics server quit with error: %s", err)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,18 +7,19 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
 | 
			
		||||
	conn "github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ReadyServer serves HTTP 200 if the tunnel can serve traffic. Intended for k8s readiness checks.
 | 
			
		||||
type ReadyServer struct {
 | 
			
		||||
	sync.RWMutex
 | 
			
		||||
	isConnected map[int]bool
 | 
			
		||||
	log         logger.Service
 | 
			
		||||
	log         *zerolog.Logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewReadyServer initializes a ReadyServer and starts listening for dis/connection events.
 | 
			
		||||
func NewReadyServer(connectionEvents <-chan conn.Event, log logger.Service) *ReadyServer {
 | 
			
		||||
func NewReadyServer(connectionEvents <-chan conn.Event, log *zerolog.Logger) *ReadyServer {
 | 
			
		||||
	rs := ReadyServer{
 | 
			
		||||
		isConnected: make(map[int]bool, 0),
 | 
			
		||||
		log:         log,
 | 
			
		||||
| 
						 | 
				
			
			@ -37,7 +38,7 @@ func NewReadyServer(connectionEvents <-chan conn.Event, log logger.Service) *Rea
 | 
			
		|||
			case conn.SetURL:
 | 
			
		||||
				continue
 | 
			
		||||
			default:
 | 
			
		||||
				rs.log.Errorf("Unknown connection event case %v", c)
 | 
			
		||||
				rs.log.Error().Msgf("Unknown connection event case %v", c)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
| 
						 | 
				
			
			@ -59,9 +60,9 @@ func (rs *ReadyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		|||
	}
 | 
			
		||||
	msg, err := json.Marshal(body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(w, `{"error": "%s"}`, err)
 | 
			
		||||
		_, _ = fmt.Fprintf(w, `{"error": "%s"}`, err)
 | 
			
		||||
	}
 | 
			
		||||
	w.Write(msg)
 | 
			
		||||
	_, _ = w.Write(msg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This is the bulk of the logic for ServeHTTP, broken into its own pure function
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,11 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/buffer"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/ingress"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/websocket"
 | 
			
		||||
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -26,15 +27,15 @@ const (
 | 
			
		|||
type client struct {
 | 
			
		||||
	ingressRules ingress.Ingress
 | 
			
		||||
	tags         []tunnelpogs.Tag
 | 
			
		||||
	logger       logger.Service
 | 
			
		||||
	log          *zerolog.Logger
 | 
			
		||||
	bufferPool   *buffer.Pool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewClient(ingressRules ingress.Ingress, tags []tunnelpogs.Tag, logger logger.Service) connection.OriginClient {
 | 
			
		||||
func NewClient(ingressRules ingress.Ingress, tags []tunnelpogs.Tag, log *zerolog.Logger) connection.OriginClient {
 | 
			
		||||
	return &client{
 | 
			
		||||
		ingressRules: ingressRules,
 | 
			
		||||
		tags:         tags,
 | 
			
		||||
		logger:       logger,
 | 
			
		||||
		log:          log,
 | 
			
		||||
		bufferPool:   buffer.NewPool(512 * 1024),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -97,14 +98,14 @@ func (c *client) proxyHTTP(w connection.ResponseWriter, req *http.Request, rule
 | 
			
		|||
		return nil, errors.Wrap(err, "Error writing response header")
 | 
			
		||||
	}
 | 
			
		||||
	if connection.IsServerSentEvent(resp.Header) {
 | 
			
		||||
		c.logger.Debug("Detected Server-Side Events from Origin")
 | 
			
		||||
		c.log.Debug().Msg("Detected Server-Side Events from Origin")
 | 
			
		||||
		c.writeEventStream(w, resp.Body)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Use CopyBuffer, because Copy only allocates a 32KiB buffer, and cross-stream
 | 
			
		||||
		// compression generates dictionary on first write
 | 
			
		||||
		buf := c.bufferPool.Get()
 | 
			
		||||
		defer c.bufferPool.Put(buf)
 | 
			
		||||
		io.CopyBuffer(w, resp.Body, buf)
 | 
			
		||||
		_, _ = io.CopyBuffer(w, resp.Body, buf)
 | 
			
		||||
	}
 | 
			
		||||
	return resp, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +130,7 @@ func (c *client) proxyWebsocket(w connection.ResponseWriter, req *http.Request,
 | 
			
		|||
	go func() {
 | 
			
		||||
		// serveCtx is done if req is cancelled, or streamWebsocket returns
 | 
			
		||||
		<-serveCtx.Done()
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		_ = conn.Close()
 | 
			
		||||
		close(connClosedChan)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +160,7 @@ func (c *client) writeEventStream(w connection.ResponseWriter, respBody io.ReadC
 | 
			
		|||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		w.Write(line)
 | 
			
		||||
		_, _ = w.Write(line)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -171,46 +172,46 @@ func (c *client) appendTagHeaders(r *http.Request) {
 | 
			
		|||
 | 
			
		||||
func (c *client) logRequest(r *http.Request, cfRay string, lbProbe bool, ruleNum int) {
 | 
			
		||||
	if cfRay != "" {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s %s %s %s", cfRay, r.Method, r.URL, r.Proto)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s %s %s %s", cfRay, r.Method, r.URL, r.Proto)
 | 
			
		||||
	} else if lbProbe {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Load Balancer health check %s %s %s", cfRay, r.Method, r.URL, r.Proto)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s Load Balancer health check %s %s %s", cfRay, r.Method, r.URL, r.Proto)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.logger.Debugf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", r.Method, r.URL, r.Proto)
 | 
			
		||||
		c.log.Debug().Msgf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", r.Method, r.URL, r.Proto)
 | 
			
		||||
	}
 | 
			
		||||
	c.logger.Debugf("CF-RAY: %s Request Headers %+v", cfRay, r.Header)
 | 
			
		||||
	c.logger.Debugf("CF-RAY: %s Serving with ingress rule %d", cfRay, ruleNum)
 | 
			
		||||
	c.log.Debug().Msgf("CF-RAY: %s Request Headers %+v", cfRay, r.Header)
 | 
			
		||||
	c.log.Debug().Msgf("CF-RAY: %s Serving with ingress rule %d", cfRay, ruleNum)
 | 
			
		||||
 | 
			
		||||
	if contentLen := r.ContentLength; contentLen == -1 {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Request Content length unknown", cfRay)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s Request Content length unknown", cfRay)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Request content length %d", cfRay, contentLen)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s Request content length %d", cfRay, contentLen)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) logOriginResponse(r *http.Response, cfRay string, lbProbe bool, ruleNum int) {
 | 
			
		||||
	responseByCode.WithLabelValues(strconv.Itoa(r.StatusCode)).Inc()
 | 
			
		||||
	if cfRay != "" {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Status: %s served by ingress %d", cfRay, r.Status, ruleNum)
 | 
			
		||||
		c.log.Info().Msgf("CF-RAY: %s Status: %s served by ingress %d", cfRay, r.Status, ruleNum)
 | 
			
		||||
	} else if lbProbe {
 | 
			
		||||
		c.logger.Debugf("Response to Load Balancer health check %s", r.Status)
 | 
			
		||||
		c.log.Debug().Msgf("Response to Load Balancer health check %s", r.Status)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.logger.Debugf("Status: %s served by ingress %d", r.Status, ruleNum)
 | 
			
		||||
		c.log.Debug().Msgf("Status: %s served by ingress %d", r.Status, ruleNum)
 | 
			
		||||
	}
 | 
			
		||||
	c.logger.Debugf("CF-RAY: %s Response Headers %+v", cfRay, r.Header)
 | 
			
		||||
	c.log.Debug().Msgf("CF-RAY: %s Response Headers %+v", cfRay, r.Header)
 | 
			
		||||
 | 
			
		||||
	if contentLen := r.ContentLength; contentLen == -1 {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Response content length unknown", cfRay)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s Response content length unknown", cfRay)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.logger.Debugf("CF-RAY: %s Response content length %d", cfRay, contentLen)
 | 
			
		||||
		c.log.Debug().Msgf("CF-RAY: %s Response content length %d", cfRay, contentLen)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *client) logRequestError(err error, cfRay string, ruleNum int) {
 | 
			
		||||
	requestErrors.Inc()
 | 
			
		||||
	if cfRay != "" {
 | 
			
		||||
		c.logger.Errorf("CF-RAY: %s Proxying to ingress %d error: %v", cfRay, ruleNum, err)
 | 
			
		||||
		c.log.Error().Msgf("CF-RAY: %s Proxying to ingress %d error: %v", cfRay, ruleNum, err)
 | 
			
		||||
	} else {
 | 
			
		||||
		c.logger.Errorf("Proxying to ingress %d error: %v", ruleNum, err)
 | 
			
		||||
		c.log.Error().Msgf("Proxying to ingress %d error: %v", ruleNum, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,11 +16,11 @@ import (
 | 
			
		|||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/hello"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/ingress"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
	"github.com/urfave/cli/v2"
 | 
			
		||||
 | 
			
		||||
	"github.com/gobwas/ws/wsutil"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ func (w *mockHTTPRespWriter) WriteRespHeaders(resp *http.Response) error {
 | 
			
		|||
 | 
			
		||||
func (w *mockHTTPRespWriter) WriteErrorResponse() {
 | 
			
		||||
	w.WriteHeader(http.StatusBadGateway)
 | 
			
		||||
	w.Write([]byte("http response error"))
 | 
			
		||||
	_, _ = w.Write([]byte("http response error"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *mockHTTPRespWriter) Read(data []byte) (int, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -106,8 +106,7 @@ func (w *mockSSERespWriter) ReadBytes() []byte {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
func TestProxySingleOrigin(t *testing.T) {
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -115,18 +114,18 @@ func TestProxySingleOrigin(t *testing.T) {
 | 
			
		|||
	flagSet.Bool("hello-world", true, "")
 | 
			
		||||
 | 
			
		||||
	cliCtx := cli.NewContext(cli.NewApp(), flagSet, nil)
 | 
			
		||||
	err = cliCtx.Set("hello-world", "true")
 | 
			
		||||
	err := cliCtx.Set("hello-world", "true")
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	allowURLFromArgs := false
 | 
			
		||||
	ingressRule, err := ingress.NewSingleOrigin(cliCtx, allowURLFromArgs, logger)
 | 
			
		||||
	ingressRule, err := ingress.NewSingleOrigin(cliCtx, allowURLFromArgs)
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	errC := make(chan error)
 | 
			
		||||
	ingressRule.StartOrigins(&wg, logger, ctx.Done(), errC)
 | 
			
		||||
	ingressRule.StartOrigins(&wg, &log, ctx.Done(), errC)
 | 
			
		||||
 | 
			
		||||
	client := NewClient(ingressRule, testTags, logger)
 | 
			
		||||
	client := NewClient(ingressRule, testTags, &log)
 | 
			
		||||
	t.Run("testProxyHTTP", testProxyHTTP(t, client))
 | 
			
		||||
	t.Run("testProxyWebsocket", testProxyWebsocket(t, client))
 | 
			
		||||
	t.Run("testProxySSE", testProxySSE(t, client))
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +190,7 @@ func testProxySSE(t *testing.T, client connection.OriginClient) func(t *testing.
 | 
			
		|||
	return func(t *testing.T) {
 | 
			
		||||
		var (
 | 
			
		||||
			pushCount = 50
 | 
			
		||||
			pushFreq  = time.Duration(time.Millisecond * 10)
 | 
			
		||||
			pushFreq  = time.Millisecond * 10
 | 
			
		||||
		)
 | 
			
		||||
		respWriter := newMockSSERespWriter()
 | 
			
		||||
		ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
| 
						 | 
				
			
			@ -252,15 +251,14 @@ func TestProxyMultipleOrigins(t *testing.T) {
 | 
			
		|||
	})
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
	errC := make(chan error)
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	ingress.StartOrigins(&wg, logger, ctx.Done(), errC)
 | 
			
		||||
	ingress.StartOrigins(&wg, &log, ctx.Done(), errC)
 | 
			
		||||
 | 
			
		||||
	client := NewClient(ingress, testTags, logger)
 | 
			
		||||
	client := NewClient(ingress, testTags, &log)
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		url            string
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +312,7 @@ type mockAPI struct{}
 | 
			
		|||
 | 
			
		||||
func (ma mockAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	w.WriteHeader(http.StatusCreated)
 | 
			
		||||
	w.Write([]byte("Created"))
 | 
			
		||||
	_, _ = w.Write([]byte("Created"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type errorOriginTransport struct{}
 | 
			
		||||
| 
						 | 
				
			
			@ -336,10 +334,9 @@ func TestProxyError(t *testing.T) {
 | 
			
		|||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	require.NoError(t, err)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
 | 
			
		||||
	client := NewClient(ingress, testTags, logger)
 | 
			
		||||
	client := NewClient(ingress, testTags, &log)
 | 
			
		||||
 | 
			
		||||
	respWriter := newMockHTTPRespWriter()
 | 
			
		||||
	req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1", nil)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,14 +6,14 @@ import (
 | 
			
		|||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/edgediscovery"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/signal"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ type Supervisor struct {
 | 
			
		|||
	nextConnectedIndex  int
 | 
			
		||||
	nextConnectedSignal chan struct{}
 | 
			
		||||
 | 
			
		||||
	logger logger.Service
 | 
			
		||||
	log *zerolog.Logger
 | 
			
		||||
 | 
			
		||||
	reconnectCredentialManager *reconnectCredentialManager
 | 
			
		||||
	useReconnectToken          bool
 | 
			
		||||
| 
						 | 
				
			
			@ -68,9 +68,9 @@ func NewSupervisor(config *TunnelConfig, cloudflaredUUID uuid.UUID) (*Supervisor
 | 
			
		|||
		err     error
 | 
			
		||||
	)
 | 
			
		||||
	if len(config.EdgeAddrs) > 0 {
 | 
			
		||||
		edgeIPs, err = edgediscovery.StaticEdge(config.Logger, config.EdgeAddrs)
 | 
			
		||||
		edgeIPs, err = edgediscovery.StaticEdge(config.Log, config.EdgeAddrs)
 | 
			
		||||
	} else {
 | 
			
		||||
		edgeIPs, err = edgediscovery.ResolveEdge(config.Logger)
 | 
			
		||||
		edgeIPs, err = edgediscovery.ResolveEdge(config.Log)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -87,7 +87,7 @@ func NewSupervisor(config *TunnelConfig, cloudflaredUUID uuid.UUID) (*Supervisor
 | 
			
		|||
		edgeIPs:                    edgeIPs,
 | 
			
		||||
		tunnelErrors:               make(chan tunnelError),
 | 
			
		||||
		tunnelsConnecting:          map[int]chan struct{}{},
 | 
			
		||||
		logger:                     config.Logger,
 | 
			
		||||
		log:                        config.Log,
 | 
			
		||||
		reconnectCredentialManager: newReconnectCredentialManager(connection.MetricsNamespace, connection.TunnelSubsystem, config.HAConnections),
 | 
			
		||||
		useReconnectToken:          useReconnectToken,
 | 
			
		||||
	}, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re
 | 
			
		|||
		if timer, err := s.reconnectCredentialManager.RefreshAuth(ctx, refreshAuthBackoff, s.authenticate); err == nil {
 | 
			
		||||
			refreshAuthBackoffTimer = timer
 | 
			
		||||
		} else {
 | 
			
		||||
			s.logger.Errorf("supervisor: initial refreshAuth failed, retrying in %v: %s", refreshAuthRetryDuration, err)
 | 
			
		||||
			s.log.Error().Msgf("supervisor: initial refreshAuth failed, retrying in %v: %s", refreshAuthRetryDuration, err)
 | 
			
		||||
			refreshAuthBackoffTimer = time.After(refreshAuthRetryDuration)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -129,7 +129,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re
 | 
			
		|||
		case tunnelError := <-s.tunnelErrors:
 | 
			
		||||
			tunnelsActive--
 | 
			
		||||
			if tunnelError.err != nil {
 | 
			
		||||
				s.logger.Infof("supervisor: Tunnel disconnected due to error: %s", tunnelError.err)
 | 
			
		||||
				s.log.Info().Msgf("supervisor: Tunnel disconnected due to error: %s", tunnelError.err)
 | 
			
		||||
				tunnelsWaiting = append(tunnelsWaiting, tunnelError.index)
 | 
			
		||||
				s.waitForNextTunnel(tunnelError.index)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -152,7 +152,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re
 | 
			
		|||
		case <-refreshAuthBackoffTimer:
 | 
			
		||||
			newTimer, err := s.reconnectCredentialManager.RefreshAuth(ctx, refreshAuthBackoff, s.authenticate)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				s.logger.Errorf("supervisor: Authentication failed: %s", err)
 | 
			
		||||
				s.log.Error().Msgf("supervisor: Authentication failed: %s", err)
 | 
			
		||||
				// Permanent failure. Leave the `select` without setting the
 | 
			
		||||
				// channel to be non-null, so we'll never hit this case of the `select` again.
 | 
			
		||||
				continue
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +172,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal *signal.Signal, re
 | 
			
		|||
func (s *Supervisor) initialize(ctx context.Context, connectedSignal *signal.Signal, reconnectCh chan ReconnectSignal) error {
 | 
			
		||||
	availableAddrs := int(s.edgeIPs.AvailableAddrs())
 | 
			
		||||
	if s.config.HAConnections > availableAddrs {
 | 
			
		||||
		s.logger.Infof("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, availableAddrs)
 | 
			
		||||
		s.log.Info().Msgf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, availableAddrs)
 | 
			
		||||
		s.config.HAConnections = availableAddrs
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -295,7 +295,7 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int)
 | 
			
		|||
		// This callback is invoked by h2mux when the edge initiates a stream.
 | 
			
		||||
		return nil // noop
 | 
			
		||||
	})
 | 
			
		||||
	muxerConfig := s.config.MuxerConfig.H2MuxerConfig(handler, s.logger)
 | 
			
		||||
	muxerConfig := s.config.MuxerConfig.H2MuxerConfig(handler, s.log)
 | 
			
		||||
	muxer, err := h2mux.Handshake(edgeConn, edgeConn, *muxerConfig, h2mux.ActiveStreams)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			@ -311,7 +311,7 @@ func (s *Supervisor) authenticate(ctx context.Context, numPreviousAttempts int)
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rpcClient := connection.NewTunnelServerClient(ctx, stream, s.logger)
 | 
			
		||||
	rpcClient := connection.NewTunnelServerClient(ctx, stream, s.log)
 | 
			
		||||
	defer rpcClient.Close()
 | 
			
		||||
 | 
			
		||||
	const arbitraryConnectionID = uint8(0)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,13 +13,13 @@ import (
 | 
			
		|||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	"github.com/prometheus/client_golang/prometheus"
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"golang.org/x/sync/errgroup"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/cmd/cloudflared/buildinfo"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/edgediscovery"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/h2mux"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/signal"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/tunnelrpc"
 | 
			
		||||
	tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
 | 
			
		||||
| 
						 | 
				
			
			@ -55,7 +55,7 @@ type TunnelConfig struct {
 | 
			
		|||
	IsFreeTunnel     bool
 | 
			
		||||
	LBPool           string
 | 
			
		||||
	Tags             []tunnelpogs.Tag
 | 
			
		||||
	Logger           logger.Service
 | 
			
		||||
	Log              *zerolog.Logger
 | 
			
		||||
	Observer         *connection.Observer
 | 
			
		||||
	ReportedVersion  string
 | 
			
		||||
	Retries          uint
 | 
			
		||||
| 
						 | 
				
			
			@ -235,7 +235,7 @@ func waitForBackoff(
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	config.Observer.SendReconnect(connIndex)
 | 
			
		||||
	config.Logger.Infof("Retrying connection %d in %s seconds, error %v", connIndex, duration, err)
 | 
			
		||||
	config.Log.Info().Msgf("Retrying connection %d in %s seconds, error %v", connIndex, duration, err)
 | 
			
		||||
	protobackoff.Backoff(ctx)
 | 
			
		||||
 | 
			
		||||
	if protobackoff.ReachedMaxRetries() {
 | 
			
		||||
| 
						 | 
				
			
			@ -247,13 +247,13 @@ func waitForBackoff(
 | 
			
		|||
		if protobackoff.protocol == fallback {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		config.Logger.Infof("Fallback to use %s", fallback)
 | 
			
		||||
		config.Log.Info().Msgf("Fallback to use %s", fallback)
 | 
			
		||||
		protobackoff.fallback(fallback)
 | 
			
		||||
	} else if !protobackoff.inFallback {
 | 
			
		||||
		current := config.ProtocolSelector.Current()
 | 
			
		||||
		if protobackoff.protocol != current {
 | 
			
		||||
			protobackoff.protocol = current
 | 
			
		||||
			config.Logger.Infof("Change protocol to %s", current)
 | 
			
		||||
			config.Log.Info().Msgf("Change protocol to %s", current)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
| 
						 | 
				
			
			@ -311,9 +311,16 @@ func ServeH2mux(
 | 
			
		|||
	cloudflaredUUID uuid.UUID,
 | 
			
		||||
	reconnectCh chan ReconnectSignal,
 | 
			
		||||
) (err error, recoverable bool) {
 | 
			
		||||
	config.Logger.Debugf("Connecting via h2mux")
 | 
			
		||||
	config.Log.Debug().Msgf("Connecting via h2mux")
 | 
			
		||||
	// Returns error from parsing the origin URL or handshake errors
 | 
			
		||||
	handler, err, recoverable := connection.NewH2muxConnection(ctx, config.ConnectionConfig, config.MuxerConfig, edgeConn, connectionIndex, config.Observer)
 | 
			
		||||
	handler, err, recoverable := connection.NewH2muxConnection(
 | 
			
		||||
		ctx,
 | 
			
		||||
		config.ConnectionConfig,
 | 
			
		||||
		config.MuxerConfig,
 | 
			
		||||
		edgeConn,
 | 
			
		||||
		connectionIndex,
 | 
			
		||||
		config.Observer,
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err, recoverable
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -338,29 +345,29 @@ func ServeH2mux(
 | 
			
		|||
			// don't retry this connection anymore, let supervisor pick new a address
 | 
			
		||||
			return err, false
 | 
			
		||||
		case *serverRegisterTunnelError:
 | 
			
		||||
			config.Logger.Errorf("Register tunnel error from server side: %s", err.cause)
 | 
			
		||||
			config.Log.Error().Msgf("Register tunnel error from server side: %s", err.cause)
 | 
			
		||||
			// Don't send registration error return from server to Sentry. They are
 | 
			
		||||
			// logged on server side
 | 
			
		||||
			if incidents := config.IncidentLookup.ActiveIncidents(); len(incidents) > 0 {
 | 
			
		||||
				config.Logger.Error(activeIncidentsMsg(incidents))
 | 
			
		||||
				config.Log.Error().Msg(activeIncidentsMsg(incidents))
 | 
			
		||||
			}
 | 
			
		||||
			return err.cause, !err.permanent
 | 
			
		||||
		case *clientRegisterTunnelError:
 | 
			
		||||
			config.Logger.Errorf("Register tunnel error on client side: %s", err.cause)
 | 
			
		||||
			config.Log.Error().Msgf("Register tunnel error on client side: %s", err.cause)
 | 
			
		||||
			return err, true
 | 
			
		||||
		case *muxerShutdownError:
 | 
			
		||||
			config.Logger.Info("Muxer shutdown")
 | 
			
		||||
			config.Log.Info().Msg("Muxer shutdown")
 | 
			
		||||
			return err, true
 | 
			
		||||
		case *ReconnectSignal:
 | 
			
		||||
			config.Logger.Infof("Restarting connection %d due to reconnect signal in %s", connectionIndex, err.Delay)
 | 
			
		||||
			config.Log.Info().Msgf("Restarting connection %d due to reconnect signal in %s", connectionIndex, err.Delay)
 | 
			
		||||
			err.DelayBeforeReconnect()
 | 
			
		||||
			return err, true
 | 
			
		||||
		default:
 | 
			
		||||
			if err == context.Canceled {
 | 
			
		||||
				config.Logger.Debugf("Serve tunnel error: %s", err)
 | 
			
		||||
				config.Log.Debug().Msgf("Serve tunnel error: %s", err)
 | 
			
		||||
				return err, false
 | 
			
		||||
			}
 | 
			
		||||
			config.Logger.Errorf("Serve tunnel error: %s", err)
 | 
			
		||||
			config.Log.Error().Msgf("Serve tunnel error: %s", err)
 | 
			
		||||
			return err, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -376,8 +383,16 @@ func ServeHTTP2(
 | 
			
		|||
	connectedFuse connection.ConnectedFuse,
 | 
			
		||||
	reconnectCh chan ReconnectSignal,
 | 
			
		||||
) (err error, recoverable bool) {
 | 
			
		||||
	config.Logger.Debugf("Connecting via http2")
 | 
			
		||||
	server := connection.NewHTTP2Connection(tlsServerConn, config.ConnectionConfig, config.NamedTunnel, connOptions, config.Observer, connIndex, connectedFuse)
 | 
			
		||||
	config.Log.Debug().Msgf("Connecting via http2")
 | 
			
		||||
	server := connection.NewHTTP2Connection(
 | 
			
		||||
		tlsServerConn,
 | 
			
		||||
		config.ConnectionConfig,
 | 
			
		||||
		config.NamedTunnel,
 | 
			
		||||
		connOptions,
 | 
			
		||||
		config.Observer,
 | 
			
		||||
		connIndex,
 | 
			
		||||
		connectedFuse,
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	errGroup, serveCtx := errgroup.WithContext(ctx)
 | 
			
		||||
	errGroup.Go(func() error {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,8 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/connection"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
 | 
			
		||||
	"github.com/rs/zerolog"
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -31,8 +32,7 @@ func TestWaitForBackoffFallback(t *testing.T) {
 | 
			
		|||
		BaseTime:   time.Millisecond * 10,
 | 
			
		||||
	}
 | 
			
		||||
	ctx := context.Background()
 | 
			
		||||
	logger, err := logger.New()
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	log := zerolog.Nop()
 | 
			
		||||
	resolveTTL := time.Duration(0)
 | 
			
		||||
	namedTunnel := &connection.NamedTunnelConfig{
 | 
			
		||||
		Credentials: connection.Credentials{
 | 
			
		||||
| 
						 | 
				
			
			@ -42,10 +42,16 @@ func TestWaitForBackoffFallback(t *testing.T) {
 | 
			
		|||
	mockFetcher := dynamicMockFetcher{
 | 
			
		||||
		percentage: 0,
 | 
			
		||||
	}
 | 
			
		||||
	protocolSelector, err := connection.NewProtocolSelector(connection.HTTP2.String(), namedTunnel, mockFetcher.fetch(), resolveTTL, logger)
 | 
			
		||||
	protocolSelector, err := connection.NewProtocolSelector(
 | 
			
		||||
		connection.HTTP2.String(),
 | 
			
		||||
		namedTunnel,
 | 
			
		||||
		mockFetcher.fetch(),
 | 
			
		||||
		resolveTTL,
 | 
			
		||||
		&log,
 | 
			
		||||
	)
 | 
			
		||||
	assert.NoError(t, err)
 | 
			
		||||
	config := &TunnelConfig{
 | 
			
		||||
		Logger:           logger,
 | 
			
		||||
		Log:              &log,
 | 
			
		||||
		ProtocolSelector: protocolSelector,
 | 
			
		||||
		Observer:         connection.NewObserver(nil, nil, false),
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
//empty manager implements the Manager but does nothing (for testing and to disable logging unless the logs are set)
 | 
			
		||||
type emptyManager struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type emptyWriteCloser struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewEmptyManager creates a new instance of a log empty log manager that does nothing
 | 
			
		||||
func NewEmptyManager() Manager {
 | 
			
		||||
	return &emptyManager{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *emptyManager) NewLogger(name string, logger logger.Service) (io.WriteCloser, error) {
 | 
			
		||||
	return &emptyWriteCloser{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *emptyManager) NewSessionLogger(name string, logger logger.Service) (io.WriteCloser, error) {
 | 
			
		||||
	return &emptyWriteCloser{}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// emptyWriteCloser
 | 
			
		||||
 | 
			
		||||
func (w *emptyWriteCloser) Write(p []byte) (n int, err error) {
 | 
			
		||||
	return len(p), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (w *emptyWriteCloser) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +0,0 @@
 | 
			
		|||
# Generate go.capnp.out with:
 | 
			
		||||
# capnp compile -o- go.capnp > go.capnp.out
 | 
			
		||||
# Must run inside this directory to preserve paths.
 | 
			
		||||
 | 
			
		||||
@0xd12a1c51fedd6c88;
 | 
			
		||||
 | 
			
		||||
annotation package(file) :Text;
 | 
			
		||||
annotation import(file) :Text;
 | 
			
		||||
annotation doc(struct, field, enum) :Text;
 | 
			
		||||
annotation tag(enumerant) :Text;
 | 
			
		||||
annotation notag(enumerant) :Void;
 | 
			
		||||
annotation customtype(field) :Text;
 | 
			
		||||
annotation name(struct, field, union, enum, enumerant, interface, method, param, annotation, const, group) :Text;
 | 
			
		||||
 | 
			
		||||
$package("capnp");
 | 
			
		||||
							
								
								
									
										167
									
								
								sshlog/logger.go
								
								
								
								
							
							
						
						
									
										167
									
								
								sshlog/logger.go
								
								
								
								
							| 
						 | 
				
			
			@ -1,167 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	logTimeFormat        = "2006-01-02T15-04-05.000"
 | 
			
		||||
	megabyte             = 1024 * 1024
 | 
			
		||||
	defaultFileSizeLimit = 100 * megabyte
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Logger will buffer and write events to disk
 | 
			
		||||
type Logger struct {
 | 
			
		||||
	sync.Mutex
 | 
			
		||||
	filename      string
 | 
			
		||||
	file          *os.File
 | 
			
		||||
	writeBuffer   *bufio.Writer
 | 
			
		||||
	logger        logger.Service
 | 
			
		||||
	flushInterval time.Duration
 | 
			
		||||
	maxFileSize   int64
 | 
			
		||||
	done          chan struct{}
 | 
			
		||||
	once          sync.Once
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewLogger creates a Logger instance. A buffer is created that needs to be
 | 
			
		||||
// drained and closed when the caller is finished, so instances should call
 | 
			
		||||
// Close when finished with this Logger instance. Writes will be flushed to disk
 | 
			
		||||
// every second (fsync). filename is the name of the logfile to be created. The
 | 
			
		||||
// logger variable is a logger service that will log all i/o, filesystem error etc, that
 | 
			
		||||
// that shouldn't end execution of the logger, but are useful to report to the
 | 
			
		||||
// caller.
 | 
			
		||||
func NewLogger(filename string, logger logger.Service, flushInterval time.Duration, maxFileSize int64) (*Logger, error) {
 | 
			
		||||
	if logger == nil {
 | 
			
		||||
		return nil, errors.New("logger can't be nil")
 | 
			
		||||
	}
 | 
			
		||||
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	l := &Logger{filename: filename,
 | 
			
		||||
		file:          f,
 | 
			
		||||
		writeBuffer:   bufio.NewWriter(f),
 | 
			
		||||
		logger:        logger,
 | 
			
		||||
		flushInterval: flushInterval,
 | 
			
		||||
		maxFileSize:   maxFileSize,
 | 
			
		||||
		done:          make(chan struct{}),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go l.writer()
 | 
			
		||||
	return l, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Writes to a log buffer. Implements the io.Writer interface.
 | 
			
		||||
func (l *Logger) Write(p []byte) (n int, err error) {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
	return l.writeBuffer.Write(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close drains anything left in the buffer and cleans up any resources still
 | 
			
		||||
// in use.
 | 
			
		||||
func (l *Logger) Close() error {
 | 
			
		||||
	l.once.Do(func() {
 | 
			
		||||
		close(l.done)
 | 
			
		||||
	})
 | 
			
		||||
	if err := l.write(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return l.file.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writer is the run loop that handles draining the write buffer and syncing
 | 
			
		||||
// data to disk.
 | 
			
		||||
func (l *Logger) writer() {
 | 
			
		||||
	ticker := time.NewTicker(l.flushInterval)
 | 
			
		||||
	defer ticker.Stop()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-ticker.C:
 | 
			
		||||
			if err := l.write(); err != nil {
 | 
			
		||||
				l.logger.Errorf("%s", err)
 | 
			
		||||
			}
 | 
			
		||||
		case <-l.done:
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// write does the actual system write calls to disk and does a rotation if the
 | 
			
		||||
// file size limit has been reached. Since the rotation happens at the end,
 | 
			
		||||
// the rotation is a soft limit (aka the file can be bigger than the max limit
 | 
			
		||||
// because of the final buffer flush)
 | 
			
		||||
func (l *Logger) write() error {
 | 
			
		||||
	l.Lock()
 | 
			
		||||
	defer l.Unlock()
 | 
			
		||||
 | 
			
		||||
	if l.writeBuffer.Buffered() <= 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := l.writeBuffer.Flush(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := l.file.Sync(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if l.shouldRotate() {
 | 
			
		||||
		return l.rotate()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// shouldRotate checks to see if the current file should be rotated to a new
 | 
			
		||||
// logfile.
 | 
			
		||||
func (l *Logger) shouldRotate() bool {
 | 
			
		||||
	info, err := l.file.Stat()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return info.Size() >= l.maxFileSize
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rotate creates a new logfile with the existing filename and renames the
 | 
			
		||||
// existing file with a current timestamp.
 | 
			
		||||
func (l *Logger) rotate() error {
 | 
			
		||||
	if err := l.file.Close(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// move the existing file
 | 
			
		||||
	newname := rotationName(l.filename)
 | 
			
		||||
	if err := os.Rename(l.filename, newname); err != nil {
 | 
			
		||||
		return fmt.Errorf("can't rename log file: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.OpenFile(l.filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(0600))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("failed to open new logfile %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	l.file = f
 | 
			
		||||
	l.writeBuffer = bufio.NewWriter(f)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rotationName creates a new filename from the given name, inserting a timestamp
 | 
			
		||||
// between the filename and the extension.
 | 
			
		||||
func rotationName(name string) string {
 | 
			
		||||
	dir := filepath.Dir(name)
 | 
			
		||||
	filename := filepath.Base(name)
 | 
			
		||||
	ext := filepath.Ext(filename)
 | 
			
		||||
	prefix := filename[:len(filename)-len(ext)]
 | 
			
		||||
	t := time.Now()
 | 
			
		||||
	timestamp := t.Format(logTimeFormat)
 | 
			
		||||
	return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,90 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const logFileName = "test-logger.log"
 | 
			
		||||
 | 
			
		||||
func createLogger(t *testing.T) *Logger {
 | 
			
		||||
	os.Remove(logFileName)
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	logger, err := NewLogger(logFileName, l, time.Millisecond, 1024)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't create the logger!", err)
 | 
			
		||||
	}
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AUTH-2115 TODO: fix this test
 | 
			
		||||
//func TestWrite(t *testing.T) {
 | 
			
		||||
//	testStr := "hi"
 | 
			
		||||
//	logger := createLogger(t)
 | 
			
		||||
//	defer func() {
 | 
			
		||||
//		logger.Close()
 | 
			
		||||
//		os.Remove(logFileName)
 | 
			
		||||
//	}()
 | 
			
		||||
//
 | 
			
		||||
//	logger.Write([]byte(testStr))
 | 
			
		||||
//	time.DelayBeforeReconnect(2 * time.Millisecond)
 | 
			
		||||
//	data, err := ioutil.ReadFile(logFileName)
 | 
			
		||||
//	if err != nil {
 | 
			
		||||
//		t.Fatal("couldn't read the log file!", err)
 | 
			
		||||
//	}
 | 
			
		||||
//	checkStr := string(data)
 | 
			
		||||
//	if checkStr != testStr {
 | 
			
		||||
//		t.Fatal("file data doesn't match!")
 | 
			
		||||
//	}
 | 
			
		||||
//}
 | 
			
		||||
 | 
			
		||||
func TestFilenameRotation(t *testing.T) {
 | 
			
		||||
	newName := rotationName("dir/bob/acoolloggername.log")
 | 
			
		||||
 | 
			
		||||
	dir := filepath.Dir(newName)
 | 
			
		||||
	if dir != "dir/bob" {
 | 
			
		||||
		t.Fatal("rotation name doesn't respect the directory filepath:", newName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	filename := filepath.Base(newName)
 | 
			
		||||
	if !strings.HasPrefix(filename, "acoolloggername") {
 | 
			
		||||
		t.Fatal("rotation filename is wrong:", filename)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ext := filepath.Ext(newName)
 | 
			
		||||
	if ext != ".log" {
 | 
			
		||||
		t.Fatal("rotation file extension is wrong:", ext)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRotation(t *testing.T) {
 | 
			
		||||
	logger := createLogger(t)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < 2000; i++ {
 | 
			
		||||
		logger.Write([]byte("a string for testing rotation\n"))
 | 
			
		||||
	}
 | 
			
		||||
	logger.Close()
 | 
			
		||||
 | 
			
		||||
	count := 0
 | 
			
		||||
	filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
 | 
			
		||||
		if err != nil || info.IsDir() {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		if strings.HasPrefix(info.Name(), "test-logger") {
 | 
			
		||||
			log.Println("deleting: ", path)
 | 
			
		||||
			os.Remove(path)
 | 
			
		||||
			count++
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
	if count < 2 {
 | 
			
		||||
		t.Fatal("rotation didn't roll files:", count)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Manager be managing logs bruh
 | 
			
		||||
type Manager interface {
 | 
			
		||||
	NewLogger(string, logger.Service) (io.WriteCloser, error)
 | 
			
		||||
	NewSessionLogger(string, logger.Service) (io.WriteCloser, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type manager struct {
 | 
			
		||||
	baseDirectory string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new instance of a log manager
 | 
			
		||||
func New(baseDirectory string) Manager {
 | 
			
		||||
	return &manager{
 | 
			
		||||
		baseDirectory: baseDirectory,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) NewLogger(name string, logger logger.Service) (io.WriteCloser, error) {
 | 
			
		||||
	return NewLogger(filepath.Join(m.baseDirectory, name), logger, time.Second, defaultFileSizeLimit)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *manager) NewSessionLogger(name string, logger logger.Service) (io.WriteCloser, error) {
 | 
			
		||||
	return NewSessionLogger(filepath.Join(m.baseDirectory, name), logger, time.Second, defaultFileSizeLimit)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
using Go = import "go.capnp";
 | 
			
		||||
@0x8f43375162194466;
 | 
			
		||||
$Go.package("sshlog");
 | 
			
		||||
$Go.import("github.com/cloudflare/cloudflared/sshlog");
 | 
			
		||||
 | 
			
		||||
struct SessionLog {
 | 
			
		||||
    timestamp @0 :Text;
 | 
			
		||||
    content @1 :Data;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,110 +0,0 @@
 | 
			
		|||
// Code generated by capnpc-go. DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	capnp "zombiezen.com/go/capnproto2"
 | 
			
		||||
	text "zombiezen.com/go/capnproto2/encoding/text"
 | 
			
		||||
	schemas "zombiezen.com/go/capnproto2/schemas"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SessionLog struct{ capnp.Struct }
 | 
			
		||||
 | 
			
		||||
// SessionLog_TypeID is the unique identifier for the type SessionLog.
 | 
			
		||||
const SessionLog_TypeID = 0xa13a07c504a5ab64
 | 
			
		||||
 | 
			
		||||
func NewSessionLog(s *capnp.Segment) (SessionLog, error) {
 | 
			
		||||
	st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
 | 
			
		||||
	return SessionLog{st}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewRootSessionLog(s *capnp.Segment) (SessionLog, error) {
 | 
			
		||||
	st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2})
 | 
			
		||||
	return SessionLog{st}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ReadRootSessionLog(msg *capnp.Message) (SessionLog, error) {
 | 
			
		||||
	root, err := msg.RootPtr()
 | 
			
		||||
	return SessionLog{root.Struct()}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) String() string {
 | 
			
		||||
	str, _ := text.Marshal(0xa13a07c504a5ab64, s.Struct)
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) Timestamp() (string, error) {
 | 
			
		||||
	p, err := s.Struct.Ptr(0)
 | 
			
		||||
	return p.Text(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) HasTimestamp() bool {
 | 
			
		||||
	p, err := s.Struct.Ptr(0)
 | 
			
		||||
	return p.IsValid() || err != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) TimestampBytes() ([]byte, error) {
 | 
			
		||||
	p, err := s.Struct.Ptr(0)
 | 
			
		||||
	return p.TextBytes(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) SetTimestamp(v string) error {
 | 
			
		||||
	return s.Struct.SetText(0, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) Content() ([]byte, error) {
 | 
			
		||||
	p, err := s.Struct.Ptr(1)
 | 
			
		||||
	return []byte(p.Data()), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) HasContent() bool {
 | 
			
		||||
	p, err := s.Struct.Ptr(1)
 | 
			
		||||
	return p.IsValid() || err != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog) SetContent(v []byte) error {
 | 
			
		||||
	return s.Struct.SetData(1, v)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SessionLog_List is a list of SessionLog.
 | 
			
		||||
type SessionLog_List struct{ capnp.List }
 | 
			
		||||
 | 
			
		||||
// NewSessionLog creates a new list of SessionLog.
 | 
			
		||||
func NewSessionLog_List(s *capnp.Segment, sz int32) (SessionLog_List, error) {
 | 
			
		||||
	l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 2}, sz)
 | 
			
		||||
	return SessionLog_List{l}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s SessionLog_List) At(i int) SessionLog { return SessionLog{s.List.Struct(i)} }
 | 
			
		||||
 | 
			
		||||
func (s SessionLog_List) Set(i int, v SessionLog) error { return s.List.SetStruct(i, v.Struct) }
 | 
			
		||||
 | 
			
		||||
func (s SessionLog_List) String() string {
 | 
			
		||||
	str, _ := text.MarshalList(0xa13a07c504a5ab64, s.List)
 | 
			
		||||
	return str
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SessionLog_Promise is a wrapper for a SessionLog promised by a client call.
 | 
			
		||||
type SessionLog_Promise struct{ *capnp.Pipeline }
 | 
			
		||||
 | 
			
		||||
func (p SessionLog_Promise) Struct() (SessionLog, error) {
 | 
			
		||||
	s, err := p.Pipeline.Struct()
 | 
			
		||||
	return SessionLog{s}, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const schema_8f43375162194466 = "x\xda\x120q`\x12d\x8dg`\x08dae\xfb" +
 | 
			
		||||
	"\x9f\xb2z)\xcbQv\xab\x85\x0c\x82B\x8c\xff\xd3\\" +
 | 
			
		||||
	"$\x93\x02\xcd\x9d\xfb\x19X\x99\xd8\x19\x18\x04E_\x09" +
 | 
			
		||||
	"*\x82h\xd9r\x06\xc6\xff\xc5\xa9\xc5\xc5\x99\xf9y\xf1" +
 | 
			
		||||
	"L9\xf9\xe9z\xc9\x89\x05y\x05V\xc1`!\xfe<" +
 | 
			
		||||
	"\x9f\xfc\xf4\x00F\xc6@\x0ef\x16\x06\x06\x16F\x06\x06" +
 | 
			
		||||
	"A\xcd \x06\x86@\x0df\xc6@\x13&FAFF" +
 | 
			
		||||
	"\x11F\x90\xa0\xa1\x13\x03C\xa0\x0e3c\xa0\x05\x13\xe3" +
 | 
			
		||||
	"\xff\x92\xcc\xdc\xd4\xe2\x92\xc4\\\x06\xc6\x02F\x1e\x06&" +
 | 
			
		||||
	"F\x1e\x06\xc6\xfa\xe4\xfc\xbc\x92\xd4\xbc\x12F^\x06&" +
 | 
			
		||||
	"F^\x06F@\x00\x00\x00\xff\xff\xdaK$\x1a"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	schemas.Register(schema_8f43375162194466,
 | 
			
		||||
		0xa13a07c504a5ab64)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,71 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	capnp "zombiezen.com/go/capnproto2"
 | 
			
		||||
	"zombiezen.com/go/capnproto2/pogs"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SessionLogger will buffer and write events to disk using capnp proto for session replay
 | 
			
		||||
type SessionLogger struct {
 | 
			
		||||
	logger  *Logger
 | 
			
		||||
	encoder *capnp.Encoder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type sessionLogData struct {
 | 
			
		||||
	Timestamp string // The UTC timestamp of when the log occurred
 | 
			
		||||
	Content   []byte // The shell output
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewSessionLogger creates a new session logger by encapsulating a Logger object and writing capnp encoded messages to it
 | 
			
		||||
func NewSessionLogger(filename string, logger logger.Service, flushInterval time.Duration, maxFileSize int64) (*SessionLogger, error) {
 | 
			
		||||
	l, err := NewLogger(filename, logger, flushInterval, maxFileSize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	sessionLogger := &SessionLogger{
 | 
			
		||||
		logger:  l,
 | 
			
		||||
		encoder: capnp.NewEncoder(l),
 | 
			
		||||
	}
 | 
			
		||||
	return sessionLogger, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Writes to a log buffer. Implements the io.Writer interface.
 | 
			
		||||
func (l *SessionLogger) Write(p []byte) (n int, err error) {
 | 
			
		||||
	return l.writeSessionLog(&sessionLogData{
 | 
			
		||||
		Timestamp: time.Now().UTC().Format(time.RFC3339),
 | 
			
		||||
		Content:   p,
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close drains anything left in the buffer and cleans up any resources still
 | 
			
		||||
// in use.
 | 
			
		||||
func (l *SessionLogger) Close() error {
 | 
			
		||||
	return l.logger.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (l *SessionLogger) writeSessionLog(p *sessionLogData) (int, error) {
 | 
			
		||||
	msg, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	log, err := NewRootSessionLog(seg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	log.SetTimestamp(p.Timestamp)
 | 
			
		||||
	log.SetContent(p.Content)
 | 
			
		||||
 | 
			
		||||
	if err := l.encoder.Encode(msg); err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return len(p.Content), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func unmarshalSessionLog(s SessionLog) (*sessionLogData, error) {
 | 
			
		||||
	p := new(sessionLogData)
 | 
			
		||||
	err := pogs.Extract(p, SessionLog_TypeID, s.Struct)
 | 
			
		||||
	return p, err
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,69 +0,0 @@
 | 
			
		|||
package sshlog
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	capnp "zombiezen.com/go/capnproto2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const sessionLogFileName = "test-session-logger.log"
 | 
			
		||||
 | 
			
		||||
func createSessionLogger(t *testing.T) *SessionLogger {
 | 
			
		||||
	os.Remove(sessionLogFileName)
 | 
			
		||||
	l := logger.NewOutputWriter(logger.NewMockWriteManager())
 | 
			
		||||
	logger, err := NewSessionLogger(sessionLogFileName, l, time.Millisecond, 1024)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't create the logger!", err)
 | 
			
		||||
	}
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestSessionLogWrite(t *testing.T) {
 | 
			
		||||
	testStr := "hi"
 | 
			
		||||
	logger := createSessionLogger(t)
 | 
			
		||||
	defer func() {
 | 
			
		||||
		os.Remove(sessionLogFileName)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	logger.Write([]byte(testStr))
 | 
			
		||||
	logger.Close()
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(sessionLogFileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't read the log file!", err)
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	msg, err := capnp.NewDecoder(f).Decode()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't read the capnp msg file!", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sessionLog, err := ReadRootSessionLog(msg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't read the session log from the msg!", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	timeStr, err := sessionLog.Timestamp()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't read the Timestamp field!", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, terr := time.Parse(time.RFC3339, timeStr)
 | 
			
		||||
	if terr != nil {
 | 
			
		||||
		t.Fatal("couldn't parse the Timestamp into the expected RFC3339 format", terr)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	data, err := sessionLog.Content()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal("couldn't read the Content field!", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	checkStr := string(data)
 | 
			
		||||
	if checkStr != testStr {
 | 
			
		||||
		t.Fatal("file data doesn't match!")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,114 +0,0 @@
 | 
			
		|||
//+build !windows
 | 
			
		||||
 | 
			
		||||
package sshserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"crypto/rsa"
 | 
			
		||||
	"crypto/x509"
 | 
			
		||||
	"encoding/pem"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/gliderlabs/ssh"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rsaFilename   = "ssh_host_rsa_key"
 | 
			
		||||
	ecdsaFilename = "ssh_host_ecdsa_key"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var defaultHostKeyDir = filepath.Join(".cloudflared", "host_keys")
 | 
			
		||||
 | 
			
		||||
func (s *SSHProxy) configureHostKeys(hostKeyDir string) error {
 | 
			
		||||
	if hostKeyDir == "" {
 | 
			
		||||
		homeDir, err := os.UserHomeDir()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		hostKeyDir = filepath.Join(homeDir, defaultHostKeyDir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(hostKeyDir); os.IsNotExist(err) {
 | 
			
		||||
		if err := os.MkdirAll(hostKeyDir, 0755); err != nil {
 | 
			
		||||
			return errors.Wrap(err, fmt.Sprintf("Error creating %s directory", hostKeyDir))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.configureECDSAKey(hostKeyDir); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := s.configureRSAKey(hostKeyDir); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SSHProxy) configureRSAKey(basePath string) error {
 | 
			
		||||
	keyPath := filepath.Join(basePath, rsaFilename)
 | 
			
		||||
	if _, err := os.Stat(keyPath); os.IsNotExist(err) {
 | 
			
		||||
		key, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "Error generating RSA host key")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		privateKey := &pem.Block{
 | 
			
		||||
			Type:  "RSA PRIVATE KEY",
 | 
			
		||||
			Bytes: x509.MarshalPKCS1PrivateKey(key),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = writePrivateKey(keyPath, privateKey); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.logger.Debugf("Created new RSA SSH host key: %s", keyPath)
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Could not set SSH RSA host key")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SSHProxy) configureECDSAKey(basePath string) error {
 | 
			
		||||
	keyPath := filepath.Join(basePath, ecdsaFilename)
 | 
			
		||||
	if _, err := os.Stat(keyPath); os.IsNotExist(err) {
 | 
			
		||||
		key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "Error generating ECDSA host key")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		keyBytes, err := x509.MarshalECPrivateKey(key)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return errors.Wrap(err, "Error marshalling ECDSA key")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		privateKey := &pem.Block{
 | 
			
		||||
			Type:  "EC PRIVATE KEY",
 | 
			
		||||
			Bytes: keyBytes,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err = writePrivateKey(keyPath, privateKey); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s.logger.Debugf("Created new ECDSA SSH host key: %s", keyPath)
 | 
			
		||||
	}
 | 
			
		||||
	if err := s.SetOption(ssh.HostKeyFile(keyPath)); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Could not set SSH ECDSA host key")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writePrivateKey(keyPath string, privateKey *pem.Block) error {
 | 
			
		||||
	if err := ioutil.WriteFile(keyPath, pem.EncodeToMemory(privateKey), 0600); err != nil {
 | 
			
		||||
		return errors.Wrap(err, fmt.Sprintf("Error writing host key to %s", keyPath))
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
package sshserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
	"github.com/stretchr/testify/require"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestHasPort(t *testing.T) {
 | 
			
		||||
	type testCase struct {
 | 
			
		||||
		input          string
 | 
			
		||||
		expectedOutput string
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tests := []testCase{
 | 
			
		||||
		{"localhost", "localhost:22"},
 | 
			
		||||
		{"other.addr:22", "other.addr:22"},
 | 
			
		||||
		{"[2001:db8::1]:8080", "[2001:db8::1]:8080"},
 | 
			
		||||
		{"[::1]", "[::1]:22"},
 | 
			
		||||
		{"2001:0db8:3c4d:0015:0000:0000:1a2f:1234", "[2001:0db8:3c4d:0015:0000:0000:1a2f:1234]:22"},
 | 
			
		||||
		{"::1", "[::1]:22"},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, test := range tests {
 | 
			
		||||
		out, err := canonicalizeDest(test.input)
 | 
			
		||||
		require.Nil(t, err)
 | 
			
		||||
		assert.Equal(t, test.expectedOutput, out)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,491 +0,0 @@
 | 
			
		|||
//+build !windows
 | 
			
		||||
 | 
			
		||||
package sshserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/ecdsa"
 | 
			
		||||
	"crypto/elliptic"
 | 
			
		||||
	"crypto/rand"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/sshgen"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/sshlog"
 | 
			
		||||
	"github.com/gliderlabs/ssh"
 | 
			
		||||
	"github.com/google/uuid"
 | 
			
		||||
	"github.com/pkg/errors"
 | 
			
		||||
	gossh "golang.org/x/crypto/ssh"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	auditEventStart       = "session_start"
 | 
			
		||||
	auditEventStop        = "session_stop"
 | 
			
		||||
	auditEventExec        = "exec"
 | 
			
		||||
	auditEventScp         = "scp"
 | 
			
		||||
	auditEventResize      = "resize"
 | 
			
		||||
	auditEventShell       = "shell"
 | 
			
		||||
	sshContextSessionID   = "sessionID"
 | 
			
		||||
	sshContextEventLogger = "eventLogger"
 | 
			
		||||
	sshContextPreamble    = "sshPreamble"
 | 
			
		||||
	sshContextSSHClient   = "sshClient"
 | 
			
		||||
	SSHPreambleLength     = 2
 | 
			
		||||
	defaultSSHPort        = "22"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type auditEvent struct {
 | 
			
		||||
	Event       string `json:"event,omitempty"`
 | 
			
		||||
	EventType   string `json:"event_type,omitempty"`
 | 
			
		||||
	SessionID   string `json:"session_id,omitempty"`
 | 
			
		||||
	User        string `json:"user,omitempty"`
 | 
			
		||||
	Login       string `json:"login,omitempty"`
 | 
			
		||||
	Datetime    string `json:"datetime,omitempty"`
 | 
			
		||||
	Hostname    string `json:"hostname,omitempty"`
 | 
			
		||||
	Destination string `json:"destination,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// sshConn wraps the incoming net.Conn and a cleanup function
 | 
			
		||||
// This is done to allow the outgoing SSH client to be retrieved and closed when the conn itself is closed.
 | 
			
		||||
type sshConn struct {
 | 
			
		||||
	net.Conn
 | 
			
		||||
	cleanupFunc func()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// close calls the cleanupFunc before closing the conn
 | 
			
		||||
func (c sshConn) Close() error {
 | 
			
		||||
	c.cleanupFunc()
 | 
			
		||||
	return c.Conn.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SSHProxy struct {
 | 
			
		||||
	ssh.Server
 | 
			
		||||
	hostname   string
 | 
			
		||||
	logger     logger.Service
 | 
			
		||||
	shutdownC  chan struct{}
 | 
			
		||||
	caCert     ssh.PublicKey
 | 
			
		||||
	logManager sshlog.Manager
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type SSHPreamble struct {
 | 
			
		||||
	Destination string
 | 
			
		||||
	JWT         string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new SSHProxy and configures its host keys and authentication by the data provided
 | 
			
		||||
func New(logManager sshlog.Manager, logger logger.Service, version, localAddress, hostname, hostKeyDir string, shutdownC chan struct{}, idleTimeout, maxTimeout time.Duration) (*SSHProxy, error) {
 | 
			
		||||
	sshProxy := SSHProxy{
 | 
			
		||||
		hostname:   hostname,
 | 
			
		||||
		logger:     logger,
 | 
			
		||||
		shutdownC:  shutdownC,
 | 
			
		||||
		logManager: logManager,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sshProxy.Server = ssh.Server{
 | 
			
		||||
		Addr:             localAddress,
 | 
			
		||||
		MaxTimeout:       maxTimeout,
 | 
			
		||||
		IdleTimeout:      idleTimeout,
 | 
			
		||||
		Version:          fmt.Sprintf("SSH-2.0-Cloudflare-Access_%s_%s", version, runtime.GOOS),
 | 
			
		||||
		PublicKeyHandler: sshProxy.proxyAuthCallback,
 | 
			
		||||
		ConnCallback:     sshProxy.connCallback,
 | 
			
		||||
		ChannelHandlers: map[string]ssh.ChannelHandler{
 | 
			
		||||
			"default": sshProxy.channelHandler,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := sshProxy.configureHostKeys(hostKeyDir); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &sshProxy, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start the SSH proxy listener to start handling SSH connections from clients
 | 
			
		||||
func (s *SSHProxy) Start() error {
 | 
			
		||||
	s.logger.Infof("Starting SSH server at %s", s.Addr)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		<-s.shutdownC
 | 
			
		||||
		if err := s.Close(); err != nil {
 | 
			
		||||
			s.logger.Errorf("Cannot close SSH server: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	return s.ListenAndServe()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// proxyAuthCallback attempts to connect to ultimate SSH destination. If successful, it allows the incoming connection
 | 
			
		||||
// to connect to the proxy and saves the outgoing SSH client to the context. Otherwise, no connection to the
 | 
			
		||||
// the proxy is allowed.
 | 
			
		||||
func (s *SSHProxy) proxyAuthCallback(ctx ssh.Context, key ssh.PublicKey) bool {
 | 
			
		||||
	client, err := s.dialDestination(ctx)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	ctx.SetValue(sshContextSSHClient, client)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// connCallback reads the preamble sent from the proxy server and saves an audit event logger to the context.
 | 
			
		||||
// If any errors occur, the connection is terminated by returning nil from the callback.
 | 
			
		||||
func (s *SSHProxy) connCallback(ctx ssh.Context, conn net.Conn) net.Conn {
 | 
			
		||||
	// AUTH-2050: This is a temporary workaround of a timing issue in the tunnel muxer to allow further testing.
 | 
			
		||||
	// TODO: Remove this
 | 
			
		||||
	time.Sleep(10 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
	preamble, err := s.readPreamble(conn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
 | 
			
		||||
			s.logger.Info("Could not establish session. Client likely does not have --destination set and is using old-style ssh config")
 | 
			
		||||
		} else if err != io.EOF {
 | 
			
		||||
			s.logger.Errorf("failed to read SSH preamble: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	ctx.SetValue(sshContextPreamble, preamble)
 | 
			
		||||
 | 
			
		||||
	logger, sessionID, err := s.auditLogger()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("failed to configure logger: %s", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	ctx.SetValue(sshContextEventLogger, logger)
 | 
			
		||||
	ctx.SetValue(sshContextSessionID, sessionID)
 | 
			
		||||
 | 
			
		||||
	// attempts to retrieve and close the outgoing ssh client when the incoming conn is closed.
 | 
			
		||||
	// If no client exists, the conn is being closed before the PublicKeyCallback was called (where the client is created).
 | 
			
		||||
	cleanupFunc := func() {
 | 
			
		||||
		client, ok := ctx.Value(sshContextSSHClient).(*gossh.Client)
 | 
			
		||||
		if ok && client != nil {
 | 
			
		||||
			client.Close()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sshConn{conn, cleanupFunc}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// channelHandler proxies incoming and outgoing SSH traffic back and forth over an SSH Channel
 | 
			
		||||
func (s *SSHProxy) channelHandler(srv *ssh.Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx ssh.Context) {
 | 
			
		||||
	if newChan.ChannelType() != "session" && newChan.ChannelType() != "direct-tcpip" {
 | 
			
		||||
		msg := fmt.Sprintf("channel type %s is not supported", newChan.ChannelType())
 | 
			
		||||
		s.logger.Info(msg)
 | 
			
		||||
		if err := newChan.Reject(gossh.UnknownChannelType, msg); err != nil {
 | 
			
		||||
			s.logger.Errorf("Error rejecting SSH channel: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	localChan, localChanReqs, err := newChan.Accept()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to accept session channel: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer localChan.Close()
 | 
			
		||||
 | 
			
		||||
	// client will be closed when the sshConn is closed
 | 
			
		||||
	client, ok := ctx.Value(sshContextSSHClient).(*gossh.Client)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		s.logger.Error("Could not retrieve client from context")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	remoteChan, remoteChanReqs, err := client.OpenChannel(newChan.ChannelType(), newChan.ExtraData())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to open remote channel: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer remoteChan.Close()
 | 
			
		||||
 | 
			
		||||
	// Proxy ssh traffic back and forth between client and destination
 | 
			
		||||
	s.proxyChannel(localChan, remoteChan, localChanReqs, remoteChanReqs, conn, ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// proxyChannel couples two SSH channels and proxies SSH traffic and channel requests back and forth.
 | 
			
		||||
func (s *SSHProxy) proxyChannel(localChan, remoteChan gossh.Channel, localChanReqs, remoteChanReqs <-chan *gossh.Request, conn *gossh.ServerConn, ctx ssh.Context) {
 | 
			
		||||
	done := make(chan struct{}, 2)
 | 
			
		||||
	go func() {
 | 
			
		||||
		if _, err := io.Copy(localChan, remoteChan); err != nil {
 | 
			
		||||
			s.logger.Errorf("remote to local copy error: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		done <- struct{}{}
 | 
			
		||||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		if _, err := io.Copy(remoteChan, localChan); err != nil {
 | 
			
		||||
			s.logger.Errorf("local to remote copy error: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
		done <- struct{}{}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	// stderr streams are used non-pty sessions since they have distinct IO streams.
 | 
			
		||||
	remoteStderr := remoteChan.Stderr()
 | 
			
		||||
	localStderr := localChan.Stderr()
 | 
			
		||||
	go func() {
 | 
			
		||||
		if _, err := io.Copy(remoteStderr, localStderr); err != nil {
 | 
			
		||||
			s.logger.Errorf("stderr local to remote copy error: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	go func() {
 | 
			
		||||
		if _, err := io.Copy(localStderr, remoteStderr); err != nil {
 | 
			
		||||
			s.logger.Errorf("stderr remote to local copy error: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	s.logAuditEvent(conn, "", auditEventStart, ctx)
 | 
			
		||||
	defer s.logAuditEvent(conn, "", auditEventStop, ctx)
 | 
			
		||||
 | 
			
		||||
	// Proxy channel requests
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case req := <-localChanReqs:
 | 
			
		||||
			if req == nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if err := s.forwardChannelRequest(remoteChan, req); err != nil {
 | 
			
		||||
				s.logger.Errorf("Failed to forward request: %s", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			s.logChannelRequest(req, conn, ctx)
 | 
			
		||||
 | 
			
		||||
		case req := <-remoteChanReqs:
 | 
			
		||||
			if req == nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			if err := s.forwardChannelRequest(localChan, req); err != nil {
 | 
			
		||||
				s.logger.Errorf("Failed to forward request: %s", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		case <-done:
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// readPreamble reads a preamble from the SSH connection before any SSH traffic is sent.
 | 
			
		||||
// This preamble is a JSON encoded struct containing the users JWT and ultimate destination.
 | 
			
		||||
// The first 4 bytes contain the length of the preamble which follows immediately.
 | 
			
		||||
func (s *SSHProxy) readPreamble(conn net.Conn) (*SSHPreamble, error) {
 | 
			
		||||
	// Set conn read deadline while reading preamble to prevent hangs if preamble wasnt sent.
 | 
			
		||||
	if err := conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)); err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to set conn deadline")
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err := conn.SetReadDeadline(time.Time{}); err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to unset conn read deadline: %s", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	size := make([]byte, SSHPreambleLength)
 | 
			
		||||
	if _, err := io.ReadFull(conn, size); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	payloadLength := binary.BigEndian.Uint16(size)
 | 
			
		||||
	payload := make([]byte, payloadLength)
 | 
			
		||||
	if _, err := io.ReadFull(conn, payload); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var preamble SSHPreamble
 | 
			
		||||
	err := json.Unmarshal(payload, &preamble)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	preamble.Destination, err = canonicalizeDest(preamble.Destination)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return &preamble, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// canonicalizeDest adds a default port if one doesnt exist
 | 
			
		||||
func canonicalizeDest(dest string) (string, error) {
 | 
			
		||||
	_, _, err := net.SplitHostPort(dest)
 | 
			
		||||
	// if host and port are split without error, a port exists.
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		addrErr, ok := err.(*net.AddrError)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		// If the port is missing, append it.
 | 
			
		||||
		if addrErr.Err == "missing port in address" {
 | 
			
		||||
			return fmt.Sprintf("%s:%s", dest, defaultSSHPort), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// If there are too many colons and address is IPv6, wrap in brackets and append port. Otherwise invalid address
 | 
			
		||||
		ip := net.ParseIP(dest)
 | 
			
		||||
		if addrErr.Err == "too many colons in address" && ip != nil && ip.To4() == nil {
 | 
			
		||||
			return fmt.Sprintf("[%s]:%s", dest, defaultSSHPort), nil
 | 
			
		||||
		}
 | 
			
		||||
		return "", addrErr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dest, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// dialDestination creates a new SSH client and dials the destination server
 | 
			
		||||
func (s *SSHProxy) dialDestination(ctx ssh.Context) (*gossh.Client, error) {
 | 
			
		||||
	preamble, ok := ctx.Value(sshContextPreamble).(*SSHPreamble)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		msg := "failed to retrieve SSH preamble from context"
 | 
			
		||||
		s.logger.Error(msg)
 | 
			
		||||
		return nil, errors.New(msg)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signer, err := s.genSSHSigner(preamble.JWT)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to generate signed short lived cert: %s", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	s.logger.Debugf("Short lived certificate for %s connecting to %s:\n\n%s", ctx.User(), preamble.Destination, gossh.MarshalAuthorizedKey(signer.PublicKey()))
 | 
			
		||||
 | 
			
		||||
	clientConfig := &gossh.ClientConfig{
 | 
			
		||||
		User: ctx.User(),
 | 
			
		||||
		// AUTH-2103 TODO: proper host key check
 | 
			
		||||
		HostKeyCallback: gossh.InsecureIgnoreHostKey(),
 | 
			
		||||
		Auth:            []gossh.AuthMethod{gossh.PublicKeys(signer)},
 | 
			
		||||
		ClientVersion:   ctx.ServerVersion(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	client, err := gossh.Dial("tcp", preamble.Destination, clientConfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to connect to destination SSH server: %s", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return client, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Generates a key pair and sends public key to get signed by CA
 | 
			
		||||
func (s *SSHProxy) genSSHSigner(jwt string) (gossh.Signer, error) {
 | 
			
		||||
	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to generate ecdsa key pair")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub, err := gossh.NewPublicKey(&key.PublicKey)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to convert ecdsa public key to SSH public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pubBytes := gossh.MarshalAuthorizedKey(pub)
 | 
			
		||||
	signedCertBytes, err := sshgen.SignCert(jwt, string(pubBytes))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to retrieve cert from SSHCAAPI")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	signedPub, _, _, _, err := gossh.ParseAuthorizedKey([]byte(signedCertBytes))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to parse SSH public key")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cert, ok := signedPub.(*gossh.Certificate)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to assert public key as certificate")
 | 
			
		||||
	}
 | 
			
		||||
	signer, err := gossh.NewSignerFromKey(key)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to create signer")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	certSigner, err := gossh.NewCertSigner(cert, signer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.Wrap(err, "failed to create cert signer")
 | 
			
		||||
	}
 | 
			
		||||
	return certSigner, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// forwardChannelRequest sends request req to SSH channel sshChan, waits for reply, and sends the reply back.
 | 
			
		||||
func (s *SSHProxy) forwardChannelRequest(sshChan gossh.Channel, req *gossh.Request) error {
 | 
			
		||||
	reply, err := sshChan.SendRequest(req.Type, req.WantReply, req.Payload)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Failed to send request")
 | 
			
		||||
	}
 | 
			
		||||
	if err := req.Reply(reply, nil); err != nil {
 | 
			
		||||
		return errors.Wrap(err, "Failed to reply to request")
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// logChannelRequest creates an audit log for different types of channel requests
 | 
			
		||||
func (s *SSHProxy) logChannelRequest(req *gossh.Request, conn *gossh.ServerConn, ctx ssh.Context) {
 | 
			
		||||
	var eventType string
 | 
			
		||||
	var event string
 | 
			
		||||
	switch req.Type {
 | 
			
		||||
	case "exec":
 | 
			
		||||
		var payload struct{ Value string }
 | 
			
		||||
		if err := gossh.Unmarshal(req.Payload, &payload); err != nil {
 | 
			
		||||
			s.logger.Errorf("Failed to unmarshal channel request payload: %s:%s with error: %s", req.Type, req.Payload, err)
 | 
			
		||||
		}
 | 
			
		||||
		event = payload.Value
 | 
			
		||||
 | 
			
		||||
		eventType = auditEventExec
 | 
			
		||||
		if strings.HasPrefix(string(req.Payload), "scp") {
 | 
			
		||||
			eventType = auditEventScp
 | 
			
		||||
		}
 | 
			
		||||
	case "shell":
 | 
			
		||||
		eventType = auditEventShell
 | 
			
		||||
	case "window-change":
 | 
			
		||||
		eventType = auditEventResize
 | 
			
		||||
	default:
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	s.logAuditEvent(conn, event, eventType, ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SSHProxy) auditLogger() (io.WriteCloser, string, error) {
 | 
			
		||||
	sessionUUID, err := uuid.NewRandom()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", errors.Wrap(err, "failed to create sessionID")
 | 
			
		||||
	}
 | 
			
		||||
	sessionID := sessionUUID.String()
 | 
			
		||||
 | 
			
		||||
	writer, err := s.logManager.NewLogger(fmt.Sprintf("%s-event.log", sessionID), s.logger)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, "", errors.Wrap(err, "failed to create logger")
 | 
			
		||||
	}
 | 
			
		||||
	return writer, sessionID, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SSHProxy) logAuditEvent(conn *gossh.ServerConn, event, eventType string, ctx ssh.Context) {
 | 
			
		||||
	sessionID, sessionIDOk := ctx.Value(sshContextSessionID).(string)
 | 
			
		||||
	writer, writerOk := ctx.Value(sshContextEventLogger).(io.WriteCloser)
 | 
			
		||||
	if !writerOk || !sessionIDOk {
 | 
			
		||||
		s.logger.Error("Failed to retrieve audit logger from context")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var destination string
 | 
			
		||||
	preamble, ok := ctx.Value(sshContextPreamble).(*SSHPreamble)
 | 
			
		||||
	if ok {
 | 
			
		||||
		destination = preamble.Destination
 | 
			
		||||
	} else {
 | 
			
		||||
		s.logger.Error("Failed to retrieve SSH preamble from context")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ae := auditEvent{
 | 
			
		||||
		Event:       event,
 | 
			
		||||
		EventType:   eventType,
 | 
			
		||||
		SessionID:   sessionID,
 | 
			
		||||
		User:        conn.User(),
 | 
			
		||||
		Login:       conn.User(),
 | 
			
		||||
		Datetime:    time.Now().UTC().Format(time.RFC3339),
 | 
			
		||||
		Hostname:    s.hostname,
 | 
			
		||||
		Destination: destination,
 | 
			
		||||
	}
 | 
			
		||||
	data, err := json.Marshal(&ae)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to marshal audit event. malformed audit object: %s", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	line := string(data) + "\n"
 | 
			
		||||
	if _, err := writer.Write([]byte(line)); err != nil {
 | 
			
		||||
		s.logger.Errorf("Failed to write audit event: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
//+build windows
 | 
			
		||||
 | 
			
		||||
package sshserver
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/cloudflare/cloudflared/logger"
 | 
			
		||||
	"github.com/cloudflare/cloudflared/sshlog"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const SSHPreambleLength = 2
 | 
			
		||||
 | 
			
		||||
type SSHServer struct{}
 | 
			
		||||
 | 
			
		||||
type SSHPreamble struct {
 | 
			
		||||
	Destination string
 | 
			
		||||
	JWT         string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func New(_ sshlog.Manager, _ logger.Service, _, _, _, _ string, _ chan struct{}, _, _ time.Duration) (*SSHServer, error) {
 | 
			
		||||
	return nil, errors.New("cloudflared ssh server is not supported on windows")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *SSHServer) Start() error {
 | 
			
		||||
	return errors.New("cloudflared ssh server is not supported on windows")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,27 +0,0 @@
 | 
			
		|||
-----BEGIN OPENSSH PRIVATE KEY-----
 | 
			
		||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
 | 
			
		||||
NhAAAAAwEAAQAAAQEA0c6EklYvC9B041qEGWDNuot6G4tTVm9LCQC0vA+v2n25ru9CINV6
 | 
			
		||||
8IljmXBORXBwfG6PdLhg0SEabZUbsNX5WrIVbGovcghKS6GRsqI5+Quhm+o8eG042JE/hB
 | 
			
		||||
oYdZ19TcMEyPOGzHsx0U/BSN9ZJWVCxqN51iI6qyhz9f6jlX2LQBFEvXlhxgF3owBEf8UC
 | 
			
		||||
Zt/UvbZdmeeyKNQElPmiVLIJEAPCueECp7a2mjCiP3zqjDvSeeGk4CelB/1qZZ4V2n7fvb
 | 
			
		||||
HZjAB5JJs4KXs5o8KgvQnqgQMxiLFZ4PATt4+mxEzh4JymppbqJOo2rYwOA3TAIEWWtYRV
 | 
			
		||||
/ZKJ0AyhhQAAA8gciO8XHIjvFwAAAAdzc2gtcnNhAAABAQDRzoSSVi8L0HTjWoQZYM26i3
 | 
			
		||||
obi1NWb0sJALS8D6/afbmu70Ig1XrwiWOZcE5FcHB8bo90uGDRIRptlRuw1flashVsai9y
 | 
			
		||||
CEpLoZGyojn5C6Gb6jx4bTjYkT+EGhh1nX1NwwTI84bMezHRT8FI31klZULGo3nWIjqrKH
 | 
			
		||||
P1/qOVfYtAEUS9eWHGAXejAER/xQJm39S9tl2Z57Io1ASU+aJUsgkQA8K54QKntraaMKI/
 | 
			
		||||
fOqMO9J54aTgJ6UH/WplnhXaft+9sdmMAHkkmzgpezmjwqC9CeqBAzGIsVng8BO3j6bETO
 | 
			
		||||
HgnKamluok6jatjA4DdMAgRZa1hFX9konQDKGFAAAAAwEAAQAAAQEApVzGdKhk8ETevAst
 | 
			
		||||
rurze6JPHcKUbr3NQE1EJi2fBvCtF0oQrtxTx54h2GAB8Q0MO6bQfsiL1ojm0ZQCfUBJBs
 | 
			
		||||
jxxb9zoccS98Vilo7ybm5SdBcMjkZX1am1jCMdQCZfCpk4/kGi7yvyOe1IhG01UBodpX5X
 | 
			
		||||
mwTjhN+fdjW7LSiW6cKPClN49CZKgmtvI27FCt+/TtMzdCXOiJxJ4yZCzCRhSgssV0gWI1
 | 
			
		||||
0VJr/MHirKUvv/qCLAuOBxIr9UgdduRZUpNX+KS2rfhFEbjnUqc/57aAakpQmuPB5I+s9G
 | 
			
		||||
DnrF0HSHpq7u1XC1SvYlnFBN/0A7Hw/MX2SaBFH7mc9AAQAAAIAFuTHr6O8tCvWEawfxC0
 | 
			
		||||
qiAPQ+Yy1vthq5uewmuQujMutUnc9JAUl32PdU1DbS7APC1Dg9XL7SyAB6A+ZpRJRAKgCY
 | 
			
		||||
SneAKE6hOytH+yM206aekrz6VuZiSpBqpfEqDibVAaZIO8sv/9dtZd6kWemxNErPQoKJey
 | 
			
		||||
Z7/cuWUWQovAAAAIEA6ugIlVj1irPmElyCCt5YfPv2x8Dl54ELoP/WsffsrPHNQog64hFd
 | 
			
		||||
ahD7Wq63TA566bN85fkx8OVU5TbbEQmkHgOEV6nDRY2YsBSqIOblA/KehtfdUIqZB0iNBh
 | 
			
		||||
Gn6TV/z6HwnSR3gKv4b66Gveek6LfRAG3mbsLCgyRAbYgn6YUAAACBAOSlf+n1eh6yjtvF
 | 
			
		||||
Zecq3Zslj7O8cUs17PQx4vQ7sXNCFrIZdevWPIn9sVrt7/hsTrXunDz6eXCeclB35KZe3H
 | 
			
		||||
WPVjRoD+xnr5+sXx2qXOnKCR0LdFybso6IR5bXAI6DNSNfP7D9LPEQ+R73Jk0jPuLYzocS
 | 
			
		||||
iM89KZiuGpzr01gBAAAAEW1pa2VAQzAyWTUwVEdKR0g4AQ==
 | 
			
		||||
-----END OPENSSH PRIVATE KEY-----
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDRzoSSVi8L0HTjWoQZYM26i3obi1NWb0sJALS8D6/afbmu70Ig1XrwiWOZcE5FcHB8bo90uGDRIRptlRuw1flashVsai9yCEpLoZGyojn5C6Gb6jx4bTjYkT+EGhh1nX1NwwTI84bMezHRT8FI31klZULGo3nWIjqrKHP1/qOVfYtAEUS9eWHGAXejAER/xQJm39S9tl2Z57Io1ASU+aJUsgkQA8K54QKntraaMKI/fOqMO9J54aTgJ6UH/WplnhXaft+9sdmMAHkkmzgpezmjwqC9CeqBAzGIsVng8BO3j6bETOHgnKamluok6jatjA4DdMAgRZa1hFX9konQDKGF mike@C02Y50TGJGH8
 | 
			
		||||
| 
						 | 
				
			
			@ -1,49 +0,0 @@
 | 
			
		|||
-----BEGIN OPENSSH PRIVATE KEY-----
 | 
			
		||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAACFwAAAAdzc2gtcn
 | 
			
		||||
NhAAAAAwEAAQAAAgEA60Kneo87qPsh+zErWFl7vx93c7fyTxbZ9lUNqafgXy/BLOCc/nQS
 | 
			
		||||
McosVSLsQrbHlhYzfmZEhTiubmuYUrHchmsn1ml1HIqP8T5aDgtNbLqYnS4H5oO4Sj1+XH
 | 
			
		||||
lQtU7n7zHXgca9SnMWt1Fhkx1mvkeiOKs0eq7hV2TuIZxfmbYfIVvJGwrL0uWzbSEE1gvx
 | 
			
		||||
gTXZHxEChIQyrNviljgi4u2MD/cIi6KMeYUnaTL1FxO9G4GIFiy7ueHRwOZPIFHgYm+Vrt
 | 
			
		||||
X7XafSF0///zCrC63zzWt/6A06hFepOz2VXvm7SdckaR7qMXAb7kipsc0+dKk9ggU7Fqpx
 | 
			
		||||
ZY5cVeZo9RlRVhRXGDy7mABA/FMwvv+qYCgJ3nlZbdKbaiPLQu8ScTlJ9sMI06/ZiEY04b
 | 
			
		||||
meZ0ASM52gaDGjrFbbnuHNf5XV/oreEUhtCrryFnoIxmKgHznGjZ55q77FtTHnrAKFmKFP
 | 
			
		||||
11s3MLIX9o4RgtriOtl4KenkIfUumgtrwY/UGjOaOQUOrVH1am54wkUiVEF0Qd3AD8KCl/
 | 
			
		||||
l/xT5+t6cOspZ9GIhwa2NBmRjN/wVGp+Yrb08Re3kxPCX9bs5iLe+kHN0vuFr7RDo+eUoi
 | 
			
		||||
SPhWl6FUqx2W9NZqekmEgKn3oKrfbGaMH1VLkaKWlzQ4xJzP0iadQbIXGryLEYASydemZt
 | 
			
		||||
sAAAdQ/ovjxf6L48UAAAAHc3NoLXJzYQAAAgEA60Kneo87qPsh+zErWFl7vx93c7fyTxbZ
 | 
			
		||||
9lUNqafgXy/BLOCc/nQSMcosVSLsQrbHlhYzfmZEhTiubmuYUrHchmsn1ml1HIqP8T5aDg
 | 
			
		||||
tNbLqYnS4H5oO4Sj1+XHlQtU7n7zHXgca9SnMWt1Fhkx1mvkeiOKs0eq7hV2TuIZxfmbYf
 | 
			
		||||
IVvJGwrL0uWzbSEE1gvxgTXZHxEChIQyrNviljgi4u2MD/cIi6KMeYUnaTL1FxO9G4GIFi
 | 
			
		||||
y7ueHRwOZPIFHgYm+VrtX7XafSF0///zCrC63zzWt/6A06hFepOz2VXvm7SdckaR7qMXAb
 | 
			
		||||
7kipsc0+dKk9ggU7FqpxZY5cVeZo9RlRVhRXGDy7mABA/FMwvv+qYCgJ3nlZbdKbaiPLQu
 | 
			
		||||
8ScTlJ9sMI06/ZiEY04bmeZ0ASM52gaDGjrFbbnuHNf5XV/oreEUhtCrryFnoIxmKgHznG
 | 
			
		||||
jZ55q77FtTHnrAKFmKFP11s3MLIX9o4RgtriOtl4KenkIfUumgtrwY/UGjOaOQUOrVH1am
 | 
			
		||||
54wkUiVEF0Qd3AD8KCl/l/xT5+t6cOspZ9GIhwa2NBmRjN/wVGp+Yrb08Re3kxPCX9bs5i
 | 
			
		||||
Le+kHN0vuFr7RDo+eUoiSPhWl6FUqx2W9NZqekmEgKn3oKrfbGaMH1VLkaKWlzQ4xJzP0i
 | 
			
		||||
adQbIXGryLEYASydemZtsAAAADAQABAAACABUYzBYEhDAaHSj+dsmcdKll8/tPko4fGXqq
 | 
			
		||||
k+gT4t4GVUdl+Q4kcIFAhQs5b4BoDava39FE8H4V4CaMxYMc6g6vy0nB+TuO/Wt/0OmTf+
 | 
			
		||||
TxMsBdoV29kCgwLYWzZ1Zq9geQK6g6nzzu5ymXRa3ApDcKC3UTfUhHKHQC3AvtjvEk0NPX
 | 
			
		||||
/EfNhwuph5aQsHNVbNnOb2MGznf9tuGjckVQUWiSLs47s+t5rykylJ8tb6cbIQk3a3G5nz
 | 
			
		||||
gDFSE8Rfo6/Wk2YnDkRX9XjlKC3Q0QWzZX6hYQvs6baRT3G3jxg9SZhn8PqPc4S34VdJvA
 | 
			
		||||
rl8AbcpeZuKi/3J/5F1cD9GwMNcl4gM87piF20/r9mMvC4zBAEgyF8WBi4OjSu0+ccsEsb
 | 
			
		||||
GSpxKK04OPTB7p8mLJ8hQUiREg5OuPEEcAoDSuHgdliE7nDHzuImbpTcAZcWhkJaUdBWI6
 | 
			
		||||
qcnGPARzxAOmuzkY8Gq0MtcWge5QxnLWJyrfy43M984Cvxql/maLUij4eTbMDDwV7Qx30V
 | 
			
		||||
P2tJp5+hOnitRwB6cQIg5N7/cTQdJ6eiFYuw0v3IfHjYmaolY8F3u38Zv2PPk50CorPRDG
 | 
			
		||||
esx0a9Elm2UKPb145MtHGZtLH2mayRnDjnxr25iLwgokI06tCLCNvbkYLA7wVpJn81eKmZ
 | 
			
		||||
tQBtbfqBSiDiLjCrehAAABAQDh8vmgPR95Kx1DeBxzMSja7TStP5V58JcUvLP4dAN6zqtt
 | 
			
		||||
rMuKDfJRSIVYGkU8vXlME5E6phWP7R5lYY7+kLDbeZrYQg1AxkK8y4fUYkCLBuEcCjzWDK
 | 
			
		||||
oqZQNskk4urbCdBIP6AR7d/LMCHBb9rk2iOuUeos6JHRKbPGP1lvH3hLkbH9CA0F41sz86
 | 
			
		||||
JFg6u/XaRQ2CyhS7y7SQ8dmaANGz9LGdIRqIoZ8Hfht8t1VRbM9fzSb3xoxUItbHpk9R9g
 | 
			
		||||
GZsHSryi7AtRmHt0uBrWIv6RbIY0epCbjdCLvHflbkPgwBM7UndgkOSIwQ4SQF8Fs+e9/r
 | 
			
		||||
hV05h0Y81vd1RZvOAAABAQD5EgW3SpmYzeMmiP7MKkfIlbZtwVmRu4anTzWxlk5pJ9GXnC
 | 
			
		||||
QoInULCipWAOeJbuLIgRWLU4VzhOUbYLNKQPXECARfgoto2VXoXZZ2q2O4aXaCpeyU6nE8
 | 
			
		||||
VKbp4nU1jEg5hWB3PRwZ8Pzs4A93/9mrpVzLmCT+LW9Rlnp6tTpqcUKGugg8vr64SSgqnV
 | 
			
		||||
ZFyQgHgw+ZGOG9w714urS3U97WNTeHXAs0p2YBOu5XW3JQ3jkRo7YyZF3+TtBxbgfHRZfH
 | 
			
		||||
O2mFcMBD3Sn4t+LAbgnLye3S2/WZf/gQwdVB7BgrVqguzQ2hGoOxNiwadkIDsxb6r/u3n6
 | 
			
		||||
2lScpHFDS0WnpRAAABAQDxzkV52VX6wAWkQe/2KFH9wTG0XFANmZUnnTPR8wd+b9E7HIr0
 | 
			
		||||
Mdd8iAHOhLRvTy8mih53GGBptXK7GdABMZtkqDErbXhuC8xbi9uRLEHiRe/oBfWr8vYIZY
 | 
			
		||||
awiw3/EqxaTv0HBMicdr2S31Bs2/mjrVuJH0wAaI9ueQnZizzjgWuzeNZMWq1qk0akUUdm
 | 
			
		||||
PDVd58yBkt8lKlkOG0LJAn6JEG9oH9XiTFShHzu1dQmoC2bKVHdxL8WCcYFVtmyoMRcLZq
 | 
			
		||||
u6d4nyKha02cYZB5hM3VcizJI5HY/A+H3fBkRR0hXgkU5R89w+8x9VSJkNVx+JGC7ziK4a
 | 
			
		||||
kUjfOmR5WBdrAAAAE3Rlc3RAY2xvdWRmbGFyZS5jb20BAgMEBQYH
 | 
			
		||||
-----END OPENSSH PRIVATE KEY-----
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgOsuFqKdzp/nC3wQfKVJBdHa8axtGryKplPkDjdSXT4kAAAADAQABAAACAQDrQqd6jzuo+yH7MStYWXu/H3dzt/JPFtn2VQ2pp+BfL8Es4Jz+dBIxyixVIuxCtseWFjN+ZkSFOK5ua5hSsdyGayfWaXUcio/xPloOC01supidLgfmg7hKPX5ceVC1TufvMdeBxr1Kcxa3UWGTHWa+R6I4qzR6ruFXZO4hnF+Zth8hW8kbCsvS5bNtIQTWC/GBNdkfEQKEhDKs2+KWOCLi7YwP9wiLoox5hSdpMvUXE70bgYgWLLu54dHA5k8gUeBib5Wu1ftdp9IXT///MKsLrfPNa3/oDTqEV6k7PZVe+btJ1yRpHuoxcBvuSKmxzT50qT2CBTsWqnFljlxV5mj1GVFWFFcYPLuYAED8UzC+/6pgKAneeVlt0ptqI8tC7xJxOUn2wwjTr9mIRjThuZ5nQBIznaBoMaOsVtue4c1/ldX+it4RSG0KuvIWegjGYqAfOcaNnnmrvsW1MeesAoWYoU/XWzcwshf2jhGC2uI62Xgp6eQh9S6aC2vBj9QaM5o5BQ6tUfVqbnjCRSJUQXRB3cAPwoKX+X/FPn63pw6yln0YiHBrY0GZGM3/BUan5itvTxF7eTE8Jf1uzmIt76Qc3S+4WvtEOj55SiJI+FaXoVSrHZb01mp6SYSAqfegqt9sZowfVUuRopaXNDjEnM/SJp1BshcavIsRgBLJ16Zm2wAAAAAAAAAAAAAAAQAAAA10ZXN0VXNlckB0ZXN0AAAADAAAAAh0ZXN0VXNlcgAAAAAAAAAA//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAABFwAAAAdzc2gtcnNhAAAAAwEAAQAAAQEA0c6EklYvC9B041qEGWDNuot6G4tTVm9LCQC0vA+v2n25ru9CINV68IljmXBORXBwfG6PdLhg0SEabZUbsNX5WrIVbGovcghKS6GRsqI5+Quhm+o8eG042JE/hBoYdZ19TcMEyPOGzHsx0U/BSN9ZJWVCxqN51iI6qyhz9f6jlX2LQBFEvXlhxgF3owBEf8UCZt/UvbZdmeeyKNQElPmiVLIJEAPCueECp7a2mjCiP3zqjDvSeeGk4CelB/1qZZ4V2n7fvbHZjAB5JJs4KXs5o8KgvQnqgQMxiLFZ4PATt4+mxEzh4JymppbqJOo2rYwOA3TAIEWWtYRV/ZKJ0AyhhQAAAQ8AAAAHc3NoLXJzYQAAAQC2lL+6JYTGOdz1zNnck6onrFcVpO2onCVAKP8HdLoCeH0/upIugaCocPKuzoURYEfiHQotviNeprE/2CyAroJ5VBdqWftEeHn3FFvBCQ1gwRQ7oci4C5n72t0vjWWE6WBylS0RqpJjr6EQ8a1vuwIqAQrEJPp2yNLjRH2WD7eicBh5f43VKOMr73DtyTh4xoF0C2sNBROudt58npTaYqRHQgoI25V/aCmuYBgM3wdAGcoEZGoSerMfhID7GcWkvemq2hF8mQsspG3zgnyQXk+ahagmefzxutDnr3KdrZ637La0/XwABvBZ9L4l5RiEilVI1Shl96F2qbBW2YZ64pUQ test@cloudflare.com
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDrQqd6jzuo+yH7MStYWXu/H3dzt/JPFtn2VQ2pp+BfL8Es4Jz+dBIxyixVIuxCtseWFjN+ZkSFOK5ua5hSsdyGayfWaXUcio/xPloOC01supidLgfmg7hKPX5ceVC1TufvMdeBxr1Kcxa3UWGTHWa+R6I4qzR6ruFXZO4hnF+Zth8hW8kbCsvS5bNtIQTWC/GBNdkfEQKEhDKs2+KWOCLi7YwP9wiLoox5hSdpMvUXE70bgYgWLLu54dHA5k8gUeBib5Wu1ftdp9IXT///MKsLrfPNa3/oDTqEV6k7PZVe+btJ1yRpHuoxcBvuSKmxzT50qT2CBTsWqnFljlxV5mj1GVFWFFcYPLuYAED8UzC+/6pgKAneeVlt0ptqI8tC7xJxOUn2wwjTr9mIRjThuZ5nQBIznaBoMaOsVtue4c1/ldX+it4RSG0KuvIWegjGYqAfOcaNnnmrvsW1MeesAoWYoU/XWzcwshf2jhGC2uI62Xgp6eQh9S6aC2vBj9QaM5o5BQ6tUfVqbnjCRSJUQXRB3cAPwoKX+X/FPn63pw6yln0YiHBrY0GZGM3/BUan5itvTxF7eTE8Jf1uzmIt76Qc3S+4WvtEOj55SiJI+FaXoVSrHZb01mp6SYSAqfegqt9sZowfVUuRopaXNDjEnM/SJp1BshcavIsRgBLJ16Zm2w== test@cloudflare.com
 | 
			
		||||
| 
						 | 
				
			
			@ -1,27 +0,0 @@
 | 
			
		|||
-----BEGIN OPENSSH PRIVATE KEY-----
 | 
			
		||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
 | 
			
		||||
NhAAAAAwEAAQAAAQEAzBO7TXxbpk7sGQm/Wa29N/NFe5uuoEQGC5hxfihmcvVgeKeNKiSS
 | 
			
		||||
snxzCE1Y6SmNMoE4aQs92wtcn48GmxRwZSXbCqLq2CJrHfe9B2k3aPkJZpQkFMshcJGo7p
 | 
			
		||||
G0Vlo7dWAbYf99/YKddf290uLK7vxw9ty0pM1hXSXHNShv1b+bTQm/COMZ5jNsncjc1yBH
 | 
			
		||||
KGkFVHee9Dh4Z0xLlHipIyyNXXzI0RFYuHSNJz9GD310XQLIIroptr7+/7g6+sPPGsNlI+
 | 
			
		||||
95OScba1/PQ2b/qy+KyIwNIMSd9ziJy5xnO7Vo3LrqQrza1Pkn2i29PljUcbc/F0hhXNIq
 | 
			
		||||
ITdNWwVqsQAAA8iKllTIipZUyAAAAAdzc2gtcnNhAAABAQDME7tNfFumTuwZCb9Zrb0380
 | 
			
		||||
V7m66gRAYLmHF+KGZy9WB4p40qJJKyfHMITVjpKY0ygThpCz3bC1yfjwabFHBlJdsKourY
 | 
			
		||||
Imsd970HaTdo+QlmlCQUyyFwkajukbRWWjt1YBth/339gp11/b3S4sru/HD23LSkzWFdJc
 | 
			
		||||
c1KG/Vv5tNCb8I4xnmM2ydyNzXIEcoaQVUd570OHhnTEuUeKkjLI1dfMjREVi4dI0nP0YP
 | 
			
		||||
fXRdAsgiuim2vv7/uDr6w88aw2Uj73k5JxtrX89DZv+rL4rIjA0gxJ33OInLnGc7tWjcuu
 | 
			
		||||
pCvNrU+SfaLb0+WNRxtz8XSGFc0iohN01bBWqxAAAAAwEAAQAAAQAKEtNFEOVpQS4QUlXa
 | 
			
		||||
tGPJtj1wy4+EI7d0rRK1GoNsG0amzgZ+1Q1UuCXpe//uinmIy64gKUjlXhs1WRcHYqvlok
 | 
			
		||||
e8r6wN/Szybr8q9Xuht+FJ6fgZ+qjs6JPBKvoO5SdYNOVFIhpzABaLs3nCRiWkRFvDI8Pa
 | 
			
		||||
+rRap7m8mwFiOJtmdiIZYFxzw6xXwTsGCrWPKgTv3FKGZzXnCB9i7jC2vwT1MDYbcnzEH4
 | 
			
		||||
Ba4dxI8bp6WWEX0biRIXj3jCtLb5gisNTSxdZs254Syh75HEXunSh2YO+yVSWQtZj19ewW
 | 
			
		||||
6Rb1Z3x5rVfXcgSkg7gZd9EpbckIIg6+MFSH3wdGW6atAAAAgQDFXiMuNd4ZYwdyhjlM5n
 | 
			
		||||
nFqQDXGgnwyNdiIqAapoqTdF5aZwNnbTU0fCFaDMLCQAHgntcgCEsW9A4HzDzYhOABKElv
 | 
			
		||||
j973vXWF165wFiZwuKSfroq/6JH6CiIcjiqpszbnqSOzy1hq913RWILS6e9yMjxRv8PUjm
 | 
			
		||||
E+IkcnfcFUwAAAAIEA+jwI3ICe8PGEIezV2tvQFeQy2Z2wGslu1yvqfTYEztSmtygns3wn
 | 
			
		||||
ZBb+cBXCnpqUCtznG7hZhq7I4m1I47BYznULwwFiBTVtBASG5wNP7zeVKTVZ4SKprze+Fe
 | 
			
		||||
I/nUZDJ5Q26um7eDbhvZ/n95GY+fucMVHoSBfX1wE16XBfp88AAACBANDHcgC4qP2oyOw/
 | 
			
		||||
+p9HineMQd/ppG3fePe07jyZXLHLf0rByFveFgRAQ1m77O7FtP3fFKy3Y9nNy18LGq35ZK
 | 
			
		||||
Blsz2B23bO8NuffgAhchDG7KzKFXCo+AraIj5znp/znK5zIkaiiSOQaYywJ36EooYVpRtj
 | 
			
		||||
ep5ap6bBFDZ2e+V/AAAAEW1pa2VAQzAyWTUwVEdKR0g4AQ==
 | 
			
		||||
-----END OPENSSH PRIVATE KEY-----
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue