diff --git a/cmd/cloudflared/hello.go b/cmd/cloudflared/hello.go
index 9f73ade8..b2cd332d 100644
--- a/cmd/cloudflared/hello.go
+++ b/cmd/cloudflared/hello.go
@@ -103,7 +103,7 @@ func hello(c *cli.Context) error {
}
func startHelloWorldServer(listener net.Listener, shutdownC <-chan struct{}) error {
- Log.Infof("Starting Hello World server at %s", listener.Addr())
+ logger.Infof("Starting Hello World server at %s", listener.Addr())
serverName := defaultServerName
if hostname, err := os.Hostname(); err == nil {
serverName = hostname
diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go
index d0b15937..4e859617 100644
--- a/cmd/cloudflared/linux_service.go
+++ b/cmd/cloudflared/linux_service.go
@@ -90,8 +90,8 @@ var sysvTemplate = ServiceTemplate{
# Short-Description: Argo Tunnel
# Description: Argo Tunnel agent
### END INIT INFO
-cmd="{{.Path}} --config /etc/cloudflared/config.yml --origincert /etc/cloudflared/cert.pem --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s"
name=$(basename $(readlink -f $0))
+cmd="{{.Path}} --config /etc/cloudflared/config.yml --origincert /etc/cloudflared/cert.pem --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s"
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
@@ -184,17 +184,17 @@ func installLinuxService(c *cli.Context) error {
defaultConfigDir := filepath.Dir(c.String("config"))
defaultConfigFile := filepath.Base(c.String("config"))
if err = copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile); err != nil {
- Log.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
+ logger.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
serviceConfigDir, credentialFile, defaultConfigFiles[0])
return err
}
switch {
case isSystemd():
- Log.Infof("Using Systemd")
+ logger.Infof("Using Systemd")
return installSystemd(&templateArgs)
default:
- Log.Infof("Using Sysv")
+ logger.Infof("Using Sysv")
return installSysv(&templateArgs)
}
}
@@ -203,30 +203,30 @@ func installSystemd(templateArgs *ServiceTemplateArgs) error {
for _, serviceTemplate := range systemdTemplates {
err := serviceTemplate.Generate(templateArgs)
if err != nil {
- Log.WithError(err).Infof("error generating service template")
+ logger.WithError(err).Infof("error generating service template")
return err
}
}
if err := runCommand("systemctl", "enable", "cloudflared.service"); err != nil {
- Log.WithError(err).Infof("systemctl enable cloudflared.service error")
+ logger.WithError(err).Infof("systemctl enable cloudflared.service error")
return err
}
if err := runCommand("systemctl", "start", "cloudflared-update.timer"); err != nil {
- Log.WithError(err).Infof("systemctl start cloudflared-update.timer error")
+ logger.WithError(err).Infof("systemctl start cloudflared-update.timer error")
return err
}
- Log.Infof("systemctl daemon-reload")
+ logger.Infof("systemctl daemon-reload")
return runCommand("systemctl", "daemon-reload")
}
func installSysv(templateArgs *ServiceTemplateArgs) error {
confPath, err := sysvTemplate.ResolvePath()
if err != nil {
- Log.WithError(err).Infof("error resolving system path")
+ logger.WithError(err).Infof("error resolving system path")
return err
}
if err := sysvTemplate.Generate(templateArgs); err != nil {
- Log.WithError(err).Infof("error generating system template")
+ logger.WithError(err).Infof("error generating system template")
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
@@ -245,36 +245,36 @@ func installSysv(templateArgs *ServiceTemplateArgs) error {
func uninstallLinuxService(c *cli.Context) error {
switch {
case isSystemd():
- Log.Infof("Using Systemd")
+ logger.Infof("Using Systemd")
return uninstallSystemd()
default:
- Log.Infof("Using Sysv")
+ logger.Infof("Using Sysv")
return uninstallSysv()
}
}
func uninstallSystemd() error {
if err := runCommand("systemctl", "disable", "cloudflared.service"); err != nil {
- Log.WithError(err).Infof("systemctl disable cloudflared.service error")
+ logger.WithError(err).Infof("systemctl disable cloudflared.service error")
return err
}
if err := runCommand("systemctl", "stop", "cloudflared-update.timer"); err != nil {
- Log.WithError(err).Infof("systemctl stop cloudflared-update.timer error")
+ logger.WithError(err).Infof("systemctl stop cloudflared-update.timer error")
return err
}
for _, serviceTemplate := range systemdTemplates {
if err := serviceTemplate.Remove(); err != nil {
- Log.WithError(err).Infof("error removing service template")
+ logger.WithError(err).Infof("error removing service template")
return err
}
}
- Log.Infof("Successfully uninstall cloudflared service")
+ logger.Infof("Successfully uninstall cloudflared service")
return nil
}
func uninstallSysv() error {
if err := sysvTemplate.Remove(); err != nil {
- Log.WithError(err).Infof("error removing service template")
+ logger.WithError(err).Infof("error removing service template")
return err
}
for _, i := range [...]string{"2", "3", "4", "5"} {
@@ -287,6 +287,6 @@ func uninstallSysv() error {
continue
}
}
- Log.Infof("Successfully uninstall cloudflared service")
+ logger.Infof("Successfully uninstall cloudflared service")
return nil
}
diff --git a/cmd/cloudflared/login.go b/cmd/cloudflared/login.go
index fa3c675e..1ba234ec 100644
--- a/cmd/cloudflared/login.go
+++ b/cmd/cloudflared/login.go
@@ -136,7 +136,7 @@ func download(certURL, filePath string) bool {
return true
}
if err != nil {
- Log.WithError(err).Error("Error fetching certificate")
+ logger.WithError(err).Error("Error fetching certificate")
return false
}
}
@@ -179,16 +179,16 @@ func putSuccess(client *http.Client, certURL string) {
// indicate success to the relay server
req, err := http.NewRequest("PUT", certURL+"/ok", nil)
if err != nil {
- Log.WithError(err).Error("HTTP request error")
+ logger.WithError(err).Error("HTTP request error")
return
}
resp, err := client.Do(req)
if err != nil {
- Log.WithError(err).Error("HTTP error")
+ logger.WithError(err).Error("HTTP error")
return
}
resp.Body.Close()
if resp.StatusCode != 200 {
- Log.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
+ logger.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
}
}
diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go
index b0ce57b3..37737c57 100644
--- a/cmd/cloudflared/macos_service.go
+++ b/cmd/cloudflared/macos_service.go
@@ -9,7 +9,9 @@ import (
"gopkg.in/urfave/cli.v2"
)
-const launchAgentIdentifier = "com.cloudflare.cloudflared"
+const (
+ launchdIdentifier = "com.cloudflare.cloudflared"
+)
func runApp(app *cli.App) {
app.Commands = append(app.Commands, &cli.Command{
@@ -32,7 +34,7 @@ func runApp(app *cli.App) {
}
var launchdTemplate = ServiceTemplate{
- Path: installPath(launchAgentIdentifier),
+ Path: installPath(),
Content: fmt.Sprintf(`
@@ -46,9 +48,9 @@ var launchdTemplate = ServiceTemplate{
RunAtLoad
StandardOutPath
- /tmp/%s.out.log
- StandardErrorPath
- /tmp/%s.err.log
+ %s
+ StandardErrorPath
+ %s
KeepAlive
SuccessfulExit
@@ -57,54 +59,84 @@ var launchdTemplate = ServiceTemplate{
ThrottleInterval
20
-`, launchAgentIdentifier, launchAgentIdentifier, launchAgentIdentifier),
+`, launchdIdentifier, stdoutPath(), stderrPath()),
}
-func installPath(launchAgentIdentifier string) string {
- pathPattern := "~/Library/LaunchAgents/%s.plist"
+func isRootUser() bool {
+ return os.Geteuid() == 0
+}
+func installPath() string {
// User is root, use /Library/LaunchDaemons instead of home directory
- if os.Geteuid() == 0 {
- pathPattern = "/Library/LaunchDaemons/%s.plist"
+ if isRootUser() {
+ return fmt.Sprintf("/Library/LaunchDaemons/%s.plist", launchdIdentifier)
}
+ return fmt.Sprintf("%s/Library/LaunchAgents/%s.plist", userHomeDir(), launchdIdentifier)
+}
- return fmt.Sprintf(pathPattern, launchAgentIdentifier)
+func stdoutPath() string {
+ if isRootUser() {
+ return fmt.Sprintf("/Library/Logs/%s.out.log", launchdIdentifier)
+ }
+ return fmt.Sprintf("%s/Library/Logs/%s.out.log", userHomeDir(), launchdIdentifier)
+}
+
+func stderrPath() string {
+ if isRootUser() {
+ return fmt.Sprintf("/Library/Logs/%s.err.log", launchdIdentifier)
+ }
+ return fmt.Sprintf("%s/Library/Logs/%s.err.log", userHomeDir(), launchdIdentifier)
}
func installLaunchd(c *cli.Context) error {
- Log.Infof("Installing Argo Tunnel as an user launch agent")
+ if isRootUser() {
+ logger.Infof("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. " +
+ "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 {
- Log.WithError(err).Infof("error determining executable path")
+ logger.WithError(err).Infof("error determining executable path")
return fmt.Errorf("error determining executable path: %v", err)
}
templateArgs := ServiceTemplateArgs{Path: etPath}
err = launchdTemplate.Generate(&templateArgs)
if err != nil {
- Log.WithError(err).Infof("error generating launchd template")
+ logger.WithError(err).Infof("error generating launchd template")
return err
}
plistPath, err := launchdTemplate.ResolvePath()
if err != nil {
- Log.WithError(err).Infof("error resolving launchd template path")
+ logger.WithError(err).Infof("error resolving launchd template path")
return err
}
- Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
+
+ logger.Infof("Outputs are logged to %s and %s", stderrPath(), stdoutPath())
return runCommand("launchctl", "load", plistPath)
}
func uninstallLaunchd(c *cli.Context) error {
- Log.Infof("Uninstalling Argo Tunnel as an user launch agent")
+
+ if isRootUser() {
+ logger.Infof("Uninstalling Argo Tunnel as a system launch daemon")
+ } else {
+ logger.Infof("Uninstalling Argo Tunnel as an user launch agent")
+ }
plistPath, err := launchdTemplate.ResolvePath()
if err != nil {
- Log.WithError(err).Infof("error resolving launchd template path")
+ logger.WithError(err).Infof("error resolving launchd template path")
return err
}
err = runCommand("launchctl", "unload", plistPath)
if err != nil {
- Log.WithError(err).Infof("error unloading")
+ logger.WithError(err).Infof("error unloading")
return err
}
- Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
+
+ logger.Infof("Outputs are logged to %s and %s", stderrPath(), stdoutPath())
return launchdTemplate.Remove()
}
diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go
index 6d93fdc8..c0b6ebb1 100644
--- a/cmd/cloudflared/main.go
+++ b/cmd/cloudflared/main.go
@@ -11,12 +11,12 @@ import (
"os"
"os/signal"
"path/filepath"
- "runtime"
"strings"
"sync"
"syscall"
"time"
+ "github.com/cloudflare/cloudflared/log"
"github.com/cloudflare/cloudflared/metrics"
"github.com/cloudflare/cloudflared/origin"
"github.com/cloudflare/cloudflared/tlsconfig"
@@ -48,7 +48,7 @@ const (
var listeners = gracenet.Net{}
var Version = "DEV"
var BuildTime = "unknown"
-var Log *logrus.Logger
+var logger = log.CreateLogger()
var defaultConfigFiles = []string{"config.yml", "config.yaml"}
// Launchd doesn't set root env variables, so there is default
@@ -59,15 +59,6 @@ var defaultConfigDirs = []string{"~/.cloudflared", "~/.cloudflare-warp", "~/clou
// May be closed by the Windows service runner.
var shutdownC chan struct{}
-type BuildAndRuntimeInfo struct {
- GoOS string `json:"go_os"`
- GoVersion string `json:"go_version"`
- GoArch string `json:"go_arch"`
- WarpVersion string `json:"warp_version"`
- WarpFlags map[string]interface{} `json:"warp_flags"`
- WarpEnvs map[string]string `json:"warp_envs"`
-}
-
func main() {
metrics.RegisterBuildInfo(BuildTime, Version)
raven.SetDSN(sentryDSN)
@@ -306,18 +297,17 @@ func main() {
return nil
}
app.Before = func(context *cli.Context) error {
- Log = logrus.New()
inputSource, err := findInputSourceContext(context)
if err != nil {
- Log.WithError(err).Infof("Cannot load configuration from %s", context.String("config"))
+ logger.WithError(err).Infof("Cannot load configuration from %s", context.String("config"))
return err
} else if inputSource != nil {
err := altsrc.ApplyInputSourceValues(context, inputSource, app.Flags)
if err != nil {
- Log.WithError(err).Infof("Cannot apply configuration from %s", context.String("config"))
+ logger.WithError(err).Infof("Cannot apply configuration from %s", context.String("config"))
return err
}
- Log.Infof("Applied configuration from %s", context.String("config"))
+ logger.Infof("Applied configuration from %s", context.String("config"))
}
return nil
}
@@ -404,45 +394,50 @@ func startServer(c *cli.Context) {
// c.NumFlags() == 0 && c.NArg() == 0. For cloudflared to work, the user needs to at
// least provide a hostname.
if c.NumFlags() == 0 && c.NArg() == 0 && os.Getenv("TUNNEL_HOSTNAME") == "" {
- Log.Infof("No arguments were provided. You need to at least specify the hostname for this tunnel. See %s", quickStartUrl)
+ logger.Infof("No arguments were provided. You need to at least specify the hostname for this tunnel. See %s", quickStartUrl)
cli.ShowAppHelp(c)
return
}
logLevel, err := logrus.ParseLevel(c.String("loglevel"))
if err != nil {
- Log.WithError(err).Fatal("Unknown logging level specified")
+ logger.WithError(err).Fatal("Unknown logging level specified")
}
- Log.SetLevel(logLevel)
+ logger.SetLevel(logLevel)
protoLogLevel, err := logrus.ParseLevel(c.String("proto-loglevel"))
if err != nil {
- Log.WithError(err).Fatal("Unknown protocol logging level specified")
+ logger.WithError(err).Fatal("Unknown protocol logging level specified")
}
protoLogger := logrus.New()
protoLogger.Level = protoLogLevel
if c.String("logfile") != "" {
if err := initLogFile(c, protoLogger); err != nil {
- Log.Error(err)
+ logger.Error(err)
}
}
+ buildInfo := origin.GetBuildInfo()
+ logger.Infof("Build info: %+v", *buildInfo)
+ logger.Infof("Version %s", Version)
+ logClientOptions(c)
+
if c.IsSet("proxy-dns") {
port := c.Int("proxy-dns-port")
if port <= 0 || port > 65535 {
- Log.Fatal("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
+ logger.Fatal("The 'proxy-dns-port' must be a valid port number in <1, 65535> range.")
}
wg.Add(1)
listener, err := tunneldns.CreateListener(c.String("proxy-dns-address"), uint16(port), c.StringSlice("proxy-dns-upstream"))
if err != nil {
close(dnsReadySignal)
listener.Stop()
- Log.WithError(err).Fatal("Cannot create the DNS over HTTPS proxy server")
+ logger.WithError(err).Fatal("Cannot create the DNS over HTTPS proxy server")
}
go func() {
err := listener.Start(dnsReadySignal)
if err != nil {
- Log.WithError(err).Fatal("Cannot start the DNS over HTTPS proxy server")
+ logger.WithError(err).Fatal("Cannot start the DNS over HTTPS proxy server")
} else {
<-shutdownC
}
@@ -453,13 +448,14 @@ func startServer(c *cli.Context) {
close(dnsReadySignal)
}
- if isAutoupdateEnabled(c) {
+ isRunningFromTerminal := isRunningFromTerminal()
+ if isAutoupdateEnabled(c, isRunningFromTerminal) {
// Wait for proxy-dns to come up (if used)
<-dnsReadySignal
if initUpdate() {
return
}
- Log.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
+ logger.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
go autoupdate(c.Duration("autoupdate-freq"), shutdownC)
}
@@ -473,7 +469,7 @@ func startServer(c *cli.Context) {
hostname, err := validation.ValidateHostname(c.String("hostname"))
if err != nil {
- Log.WithError(err).Fatal("Invalid hostname")
+ logger.WithError(err).Fatal("Invalid hostname")
}
clientID := c.String("id")
if !c.IsSet("id") {
@@ -482,7 +478,7 @@ func startServer(c *cli.Context) {
tags, err := NewTagSliceFromCLI(c.StringSlice("tag"))
if err != nil {
- Log.WithError(err).Fatal("Tag parse failure")
+ logger.WithError(err).Fatal("Tag parse failure")
}
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
@@ -491,7 +487,7 @@ func startServer(c *cli.Context) {
listener, err := createListener("127.0.0.1:")
if err != nil {
listener.Close()
- Log.WithError(err).Fatal("Cannot start Hello World Server")
+ logger.WithError(err).Fatal("Cannot start Hello World Server")
}
go func() {
startHelloWorldServer(listener, shutdownC)
@@ -503,26 +499,26 @@ func startServer(c *cli.Context) {
url, err := validateUrl(c)
if err != nil {
- Log.WithError(err).Fatal("Error validating url")
+ logger.WithError(err).Fatal("Error validating url")
}
- Log.Infof("Proxying tunnel requests to %s", url)
+ logger.Infof("Proxying tunnel requests to %s", url)
// Fail if the user provided an old authentication method
if c.IsSet("api-key") || c.IsSet("api-email") || c.IsSet("api-ca-key") {
- Log.Fatal("You don't need to give us your api-key anymore. Please use the new log in method. Just run cloudflared login")
+ logger.Fatal("You don't need to give us your api-key anymore. Please use the new log in method. Just run cloudflared login")
}
// Check that the user has acquired a certificate using the log in command
originCertPath, err := homedir.Expand(c.String("origincert"))
if err != nil {
- Log.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
+ logger.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
}
ok, err := fileExists(originCertPath)
if err != nil {
- Log.Fatalf("Cannot check if origin cert exists at path %s", c.String("origincert"))
+ logger.Fatalf("Cannot check if origin cert exists at path %s", c.String("origincert"))
}
if !ok {
- Log.Fatalf(`Cannot find a valid certificate for your origin at the path:
+ logger.Fatalf(`Cannot find a valid certificate for your origin at the path:
%s
@@ -535,7 +531,7 @@ If you don't have a certificate signed by Cloudflare, run the command:
// Easier to send the certificate as []byte via RPC than decoding it at this point
originCert, err := ioutil.ReadFile(originCertPath)
if err != nil {
- Log.WithError(err).Fatalf("Cannot read %s to load origin certificate", originCertPath)
+ logger.WithError(err).Fatalf("Cannot read %s to load origin certificate", originCertPath)
}
tunnelMetrics := origin.NewTunnelMetrics()
@@ -558,27 +554,29 @@ If you don't have a certificate signed by Cloudflare, run the command:
}
tunnelConfig := &origin.TunnelConfig{
- EdgeAddrs: c.StringSlice("edge"),
- OriginUrl: url,
- Hostname: hostname,
- OriginCert: originCert,
- TlsConfig: tlsconfig.CreateTunnelConfig(c, c.StringSlice("edge")),
- ClientTlsConfig: httpTransport.TLSClientConfig,
- Retries: c.Uint("retries"),
- HeartbeatInterval: c.Duration("heartbeat-interval"),
- MaxHeartbeats: c.Uint64("heartbeat-count"),
- ClientID: clientID,
- ReportedVersion: Version,
- LBPool: c.String("lb-pool"),
- Tags: tags,
- HAConnections: c.Int("ha-connections"),
- HTTPTransport: httpTransport,
- Metrics: tunnelMetrics,
- MetricsUpdateFreq: c.Duration("metrics-update-freq"),
- ProtocolLogger: protoLogger,
- Logger: Log,
- IsAutoupdated: c.Bool("is-autoupdated"),
- GracePeriod: c.Duration("grace-period"),
+ EdgeAddrs: c.StringSlice("edge"),
+ OriginUrl: url,
+ Hostname: hostname,
+ OriginCert: originCert,
+ TlsConfig: tlsconfig.CreateTunnelConfig(c, c.StringSlice("edge")),
+ ClientTlsConfig: httpTransport.TLSClientConfig,
+ Retries: c.Uint("retries"),
+ HeartbeatInterval: c.Duration("heartbeat-interval"),
+ MaxHeartbeats: c.Uint64("heartbeat-count"),
+ ClientID: clientID,
+ BuildInfo: buildInfo,
+ ReportedVersion: Version,
+ LBPool: c.String("lb-pool"),
+ Tags: tags,
+ HAConnections: c.Int("ha-connections"),
+ HTTPTransport: httpTransport,
+ Metrics: tunnelMetrics,
+ MetricsUpdateFreq: c.Duration("metrics-update-freq"),
+ ProtocolLogger: protoLogger,
+ Logger: logger,
+ IsAutoupdated: c.Bool("is-autoupdated"),
+ GracePeriod: c.Duration("grace-period"),
+ RunFromTerminal: isRunningFromTerminal,
}
go writePidFile(connectedSignal, c.String("pidfile"))
@@ -595,21 +593,21 @@ func runServer(c *cli.Context, wg *sync.WaitGroup, errC chan error, shutdownC ch
wg.Add(1)
metricsListener, err := listeners.Listen("tcp", c.String("metrics"))
if err != nil {
- Log.WithError(err).Fatal("Error opening metrics server listener")
+ logger.WithError(err).Fatal("Error opening metrics server listener")
}
go func() {
- errC <- metrics.ServeMetrics(metricsListener, shutdownC)
+ errC <- metrics.ServeMetrics(metricsListener, shutdownC, logger)
wg.Done()
}()
var errCode int
err = WaitForSignal(errC, shutdownC)
if err != nil {
- Log.WithError(err).Fatal("Quitting due to error")
+ logger.WithError(err).Fatal("Quitting due to error")
raven.CaptureErrorAndWait(err, nil)
errCode = 1
} else {
- Log.Info("Graceful shutdown...")
+ logger.Info("Graceful shutdown...")
}
// Wait for clean exit, discarding all errors
go func() {
@@ -648,7 +646,7 @@ func initUpdate() bool {
if updateApplied() {
os.Args = append(os.Args, "--is-autoupdated=true")
if _, err := listeners.StartProcess(); err != nil {
- Log.WithError(err).Error("Unable to restart server automatically")
+ logger.WithError(err).Error("Unable to restart server automatically")
return false
}
return true
@@ -661,7 +659,7 @@ func autoupdate(freq time.Duration, shutdownC chan struct{}) {
if updateApplied() {
os.Args = append(os.Args, "--is-autoupdated=true")
if _, err := listeners.StartProcess(); err != nil {
- Log.WithError(err).Error("Unable to restart server automatically")
+ logger.WithError(err).Error("Unable to restart server automatically")
}
close(shutdownC)
return
@@ -673,11 +671,11 @@ func autoupdate(freq time.Duration, shutdownC chan struct{}) {
func updateApplied() bool {
releaseInfo := checkForUpdates()
if releaseInfo.Updated {
- Log.Infof("Updated to version %s", releaseInfo.Version)
+ logger.Infof("Updated to version %s", releaseInfo.Version)
return true
}
if releaseInfo.Error != nil {
- Log.WithError(releaseInfo.Error).Error("Update check failed")
+ logger.WithError(releaseInfo.Error).Error("Update check failed")
}
return false
}
@@ -748,7 +746,7 @@ func writePidFile(waitForSignal chan struct{}, pidFile string) {
}
file, err := os.Create(pidFile)
if err != nil {
- Log.WithError(err).Errorf("Unable to write pid to %s", pidFile)
+ logger.WithError(err).Errorf("Unable to write pid to %s", pidFile)
}
defer file.Close()
fmt.Fprintf(file, "%d", os.Getpid())
@@ -790,16 +788,22 @@ func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
logrus.PanicLevel: filePath,
}
- Log.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
+ logger.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
protoLogger.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
- flags := make(map[string]interface{})
- envs := make(map[string]string)
+ return nil
+}
+func logClientOptions(c *cli.Context) {
+ flags := make(map[string]interface{})
for _, flag := range c.LocalFlagNames() {
flags[flag] = c.Generic(flag)
}
+ if len(flags) > 0 {
+ logger.Infof("Flags %v", flags)
+ }
+ envs := make(map[string]string)
// Find env variables for Argo Tunnel
for _, env := range os.Environ() {
// All Argo Tunnel env variables start with TUNNEL_
@@ -810,24 +814,33 @@ func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
}
}
}
-
- Log.Infof("Argo Tunnel build and runtime configuration: %+v", BuildAndRuntimeInfo{
- GoOS: runtime.GOOS,
- GoVersion: runtime.Version(),
- GoArch: runtime.GOARCH,
- WarpVersion: Version,
- WarpFlags: flags,
- WarpEnvs: envs,
- })
-
- return nil
+ if len(envs) > 0 {
+ logger.Infof("Environmental variables %v", envs)
+ }
}
-func isAutoupdateEnabled(c *cli.Context) bool {
- if terminal.IsTerminal(int(os.Stdout.Fd())) {
- Log.Info(noAutoupdateMessage)
+func isAutoupdateEnabled(c *cli.Context, isRunningFromTerminal bool) bool {
+ if isRunningFromTerminal {
+ logger.Info(noAutoupdateMessage)
return false
}
return !c.Bool("no-autoupdate") && c.Duration("autoupdate-freq") != 0
}
+
+
+func isRunningFromTerminal() bool {
+ return terminal.IsTerminal(int(os.Stdout.Fd()))
+}
+
+func userHomeDir() string {
+ // This returns the home dir of the executing user using OS-specific method
+ // for discovering the home dir. It's not recommended to call this function
+ // when the user has root permission as $HOME depends on what options the user
+ // use with sudo.
+ homeDir, err := homedir.Dir()
+ if err != nil {
+ logger.WithError(err).Fatal("Cannot determine home directory for the user.")
+ }
+ return homeDir
+}
diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go
index 063e26cb..afec0fd7 100644
--- a/cmd/cloudflared/service_template.go
+++ b/cmd/cloudflared/service_template.go
@@ -73,21 +73,21 @@ func runCommand(command string, args ...string) error {
cmd := exec.Command(command, args...)
stderr, err := cmd.StderrPipe()
if err != nil {
- Log.WithError(err).Infof("error getting stderr pipe")
+ logger.WithError(err).Infof("error getting stderr pipe")
return fmt.Errorf("error getting stderr pipe: %v", err)
}
err = cmd.Start()
if err != nil {
- Log.WithError(err).Infof("error starting %s", command)
+ logger.WithError(err).Infof("error starting %s", command)
return fmt.Errorf("error starting %s: %v", command, err)
}
commandErr, _ := ioutil.ReadAll(stderr)
if len(commandErr) > 0 {
- Log.Errorf("%s: %s", command, commandErr)
+ logger.Errorf("%s: %s", command, commandErr)
}
err = cmd.Wait()
if err != nil {
- Log.WithError(err).Infof("%s returned error", command)
+ logger.WithError(err).Infof("%s returned error", command)
return fmt.Errorf("%s returned with error: %v", command, err)
}
return nil
@@ -159,7 +159,7 @@ func copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile strin
destConfigPath := filepath.Join(serviceConfigDir, defaultConfigFile)
destFile, exists, err := openFile(destConfigPath, true)
if err != nil {
- Log.WithError(err).Infof("cannot open %s", destConfigPath)
+ logger.WithError(err).Infof("cannot open %s", destConfigPath)
return err
} else if exists {
// config already exists, do nothing
@@ -185,7 +185,7 @@ func copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile strin
if err != nil {
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
}
- Log.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
+ logger.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
}
return nil
diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go
index b003bb50..f1442863 100644
--- a/cmd/cloudflared/windows_service.go
+++ b/cmd/cloudflared/windows_service.go
@@ -41,7 +41,7 @@ func runApp(app *cli.App) {
isIntSess, err := svc.IsAnInteractiveSession()
if err != nil {
- Log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
+ logger.Fatalf("failed to determine if we are running in an interactive session: %v", err)
}
if isIntSess {
@@ -51,7 +51,7 @@ func runApp(app *cli.App) {
elog, err := eventlog.Open(windowsServiceName)
if err != nil {
- Log.WithError(err).Infof("Cannot open event log for %s", windowsServiceName)
+ logger.WithError(err).Infof("Cannot open event log for %s", windowsServiceName)
return
}
defer elog.Close()
@@ -104,62 +104,62 @@ loop:
}
func installWindowsService(c *cli.Context) error {
- Log.Infof("Installing Argo Tunnel Windows service")
+ logger.Infof("Installing Argo Tunnel Windows service")
exepath, err := os.Executable()
if err != nil {
- Log.Infof("Cannot find path name that start the process")
+ logger.Infof("Cannot find path name that start the process")
return err
}
m, err := mgr.Connect()
if err != nil {
- Log.WithError(err).Infof("Cannot establish a connection to the service control manager")
+ logger.WithError(err).Infof("Cannot establish a connection to the service control manager")
return err
}
defer m.Disconnect()
s, err := m.OpenService(windowsServiceName)
if err == nil {
s.Close()
- Log.Errorf("service %s already exists", windowsServiceName)
+ logger.Errorf("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 {
- Log.Infof("Cannot install service %s", windowsServiceName)
+ logger.Infof("Cannot install service %s", windowsServiceName)
return err
}
defer s.Close()
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
if err != nil {
s.Delete()
- Log.WithError(err).Infof("Cannot install event logger")
+ logger.WithError(err).Infof("Cannot install event logger")
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
}
return nil
}
func uninstallWindowsService(c *cli.Context) error {
- Log.Infof("Uninstalling Argo Tunnel Windows Service")
+ logger.Infof("Uninstalling Argo Tunnel Windows Service")
m, err := mgr.Connect()
if err != nil {
- Log.Infof("Cannot establish a connection to the service control manager")
+ logger.Infof("Cannot establish a connection to the service control manager")
return err
}
defer m.Disconnect()
s, err := m.OpenService(windowsServiceName)
if err != nil {
- Log.Infof("service %s is not installed", windowsServiceName)
+ logger.Infof("service %s is not installed", windowsServiceName)
return fmt.Errorf("service %s is not installed", windowsServiceName)
}
defer s.Close()
err = s.Delete()
if err != nil {
- Log.Errorf("Cannot delete service %s", windowsServiceName)
+ logger.Errorf("Cannot delete service %s", windowsServiceName)
return err
}
err = eventlog.Remove(windowsServiceName)
if err != nil {
- Log.Infof("Cannot remove event logger")
+ logger.Infof("Cannot remove event logger")
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
}
return nil
diff --git a/log/log.go b/log/log.go
new file mode 100644
index 00000000..f1824f2e
--- /dev/null
+++ b/log/log.go
@@ -0,0 +1,85 @@
+// this forks the logrus json formatter to rename msg -> message as that's the
+// expected field. Ideally the logger should make it easier for us.
+package log
+
+import (
+ "encoding/json"
+ "fmt"
+ "time"
+
+ "github.com/mattn/go-colorable"
+ "github.com/sirupsen/logrus"
+)
+
+var (
+ DefaultTimestampFormat = time.RFC3339
+)
+
+type JSONFormatter struct {
+ // TimestampFormat sets the format used for marshaling timestamps.
+ TimestampFormat string
+}
+
+func CreateLogger() *logrus.Logger {
+ logger := logrus.New()
+ logger.Out = colorable.NewColorableStderr()
+ logger.Formatter = &logrus.TextFormatter{ForceColors: true}
+
+ return logger
+}
+
+func (f *JSONFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+ data := make(logrus.Fields, len(entry.Data)+3)
+ for k, v := range entry.Data {
+ switch v := v.(type) {
+ case error:
+ // Otherwise errors are ignored by `encoding/json`
+ // https://github.com/sirupsen/logrus/issues/137
+ data[k] = v.Error()
+ default:
+ data[k] = v
+ }
+ }
+ prefixFieldClashes(data)
+
+ timestampFormat := f.TimestampFormat
+ if timestampFormat == "" {
+ timestampFormat = DefaultTimestampFormat
+ }
+
+ data["time"] = entry.Time.Format(timestampFormat)
+ data["message"] = entry.Message
+ data["level"] = entry.Level.String()
+
+ serialized, err := json.Marshal(data)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+ }
+ return append(serialized, '\n'), nil
+}
+
+// This is to not silently overwrite `time`, `msg` and `level` fields when
+// dumping it. If this code wasn't there doing:
+//
+// logrus.WithField("level", 1).Info("hello")
+//
+// Would just silently drop the user provided level. Instead with this code
+// it'll logged as:
+//
+// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
+//
+// It's not exported because it's still using Data in an opinionated way. It's to
+// avoid code duplication between the two default formatters.
+func prefixFieldClashes(data logrus.Fields) {
+ if t, ok := data["time"]; ok {
+ data["fields.time"] = t
+ }
+
+ if m, ok := data["msg"]; ok {
+ data["fields.msg"] = m
+ }
+
+ if l, ok := data["level"]; ok {
+ data["fields.level"] = l
+ }
+}
diff --git a/metrics/metrics.go b/metrics/metrics.go
index 4707b4ed..4b6156ab 100644
--- a/metrics/metrics.go
+++ b/metrics/metrics.go
@@ -13,7 +13,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
- log "github.com/sirupsen/logrus"
+ "github.com/sirupsen/logrus"
)
const (
@@ -21,7 +21,7 @@ const (
startupTime = time.Millisecond * 500
)
-func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
+func ServeMetrics(l net.Listener, shutdownC <-chan struct{}, logger *logrus.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 }
@@ -39,7 +39,7 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
defer wg.Done()
err = server.Serve(l)
}()
- log.WithField("addr", l.Addr()).Info("Starting metrics server")
+ logger.WithField("addr", l.Addr()).Info("Starting metrics server")
// server.Serve will hang if server.Shutdown is called before the server is
// fully started up. So add artificial delay.
time.Sleep(startupTime)
@@ -51,10 +51,10 @@ func ServeMetrics(l net.Listener, shutdownC <-chan struct{}) (err error) {
wg.Wait()
if err == http.ErrServerClosed {
- log.Info("Metrics server stopped")
+ logger.Info("Metrics server stopped")
return nil
}
- log.WithError(err).Error("Metrics server quit with error")
+ logger.WithError(err).Error("Metrics server quit with error")
return err
}
diff --git a/origin/build_info.go b/origin/build_info.go
new file mode 100644
index 00000000..72f0965a
--- /dev/null
+++ b/origin/build_info.go
@@ -0,0 +1,19 @@
+package origin
+
+import (
+ "runtime"
+)
+
+type BuildInfo struct {
+ GoOS string `json:"go_os"`
+ GoVersion string `json:"go_version"`
+ GoArch string `json:"go_arch"`
+}
+
+func GetBuildInfo() *BuildInfo {
+ return &BuildInfo{
+ GoOS: runtime.GOOS,
+ GoVersion: runtime.Version(),
+ GoArch: runtime.GOARCH,
+ }
+}
diff --git a/origin/metrics.go b/origin/metrics.go
index 9908c554..8ee560c1 100644
--- a/origin/metrics.go
+++ b/origin/metrics.go
@@ -27,7 +27,7 @@ type muxerMetrics struct {
outBoundRateMax *prometheus.GaugeVec
}
-type tunnelMetrics struct {
+type TunnelMetrics struct {
haConnections prometheus.Gauge
totalRequests prometheus.Counter
requestsPerTunnel *prometheus.CounterVec
@@ -229,7 +229,7 @@ func convertRTTMilliSec(t time.Duration) float64 {
}
// Metrics that can be collected without asking the edge
-func NewTunnelMetrics() *tunnelMetrics {
+func NewTunnelMetrics() *TunnelMetrics {
haConnections := prometheus.NewGauge(
prometheus.GaugeOpts{
Name: "ha_connections",
@@ -305,7 +305,7 @@ func NewTunnelMetrics() *tunnelMetrics {
)
prometheus.MustRegister(serverLocations)
- return &tunnelMetrics{
+ return &TunnelMetrics{
haConnections: haConnections,
totalRequests: totalRequests,
requestsPerTunnel: requestsPerTunnel,
@@ -322,19 +322,19 @@ func NewTunnelMetrics() *tunnelMetrics {
}
}
-func (t *tunnelMetrics) incrementHaConnections() {
+func (t *TunnelMetrics) incrementHaConnections() {
t.haConnections.Inc()
}
-func (t *tunnelMetrics) decrementHaConnections() {
+func (t *TunnelMetrics) decrementHaConnections() {
t.haConnections.Dec()
}
-func (t *tunnelMetrics) updateMuxerMetrics(connectionID string, metrics *h2mux.MuxerMetrics) {
+func (t *TunnelMetrics) updateMuxerMetrics(connectionID string, metrics *h2mux.MuxerMetrics) {
t.muxerMetrics.update(connectionID, metrics)
}
-func (t *tunnelMetrics) incrementRequests(connectionID string) {
+func (t *TunnelMetrics) incrementRequests(connectionID string) {
t.concurrentRequestsLock.Lock()
var concurrentRequests uint64
var ok bool
@@ -356,25 +356,25 @@ func (t *tunnelMetrics) incrementRequests(connectionID string) {
t.concurrentRequestsPerTunnel.WithLabelValues(connectionID).Inc()
}
-func (t *tunnelMetrics) decrementConcurrentRequests(connectionID string) {
+func (t *TunnelMetrics) decrementConcurrentRequests(connectionID string) {
t.concurrentRequestsLock.Lock()
if _, ok := t.concurrentRequests[connectionID]; ok {
t.concurrentRequests[connectionID] -= 1
} else {
- Log.Error("Concurrent requests per tunnel metrics went wrong; you can't decrement concurrent requests count without increment it first.")
+ logger.Error("Concurrent requests per tunnel metrics went wrong; you can't decrement concurrent requests count without increment it first.")
}
t.concurrentRequestsLock.Unlock()
t.concurrentRequestsPerTunnel.WithLabelValues(connectionID).Dec()
}
-func (t *tunnelMetrics) incrementResponses(connectionID, code string) {
+func (t *TunnelMetrics) incrementResponses(connectionID, code string) {
t.responseByCode.WithLabelValues(code).Inc()
t.responseCodePerTunnel.WithLabelValues(connectionID, code).Inc()
}
-func (t *tunnelMetrics) registerServerLocation(connectionID, loc string) {
+func (t *TunnelMetrics) registerServerLocation(connectionID, loc string) {
t.locationLock.Lock()
defer t.locationLock.Unlock()
if oldLoc, ok := t.oldServerLocations[connectionID]; ok && oldLoc == loc {
diff --git a/origin/supervisor.go b/origin/supervisor.go
index 4deb57c4..9a315ad8 100644
--- a/origin/supervisor.go
+++ b/origin/supervisor.go
@@ -73,7 +73,7 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
case tunnelError := <-s.tunnelErrors:
tunnelsActive--
if tunnelError.err != nil {
- Log.WithError(tunnelError.err).Warn("Tunnel disconnected due to error")
+ logger.WithError(tunnelError.err).Warn("Tunnel disconnected due to error")
tunnelsWaiting = append(tunnelsWaiting, tunnelError.index)
s.waitForNextTunnel(tunnelError.index)
@@ -109,10 +109,10 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
s.lastResolve = time.Now()
s.resolverC = nil
if result.err == nil {
- Log.Debug("Service discovery refresh complete")
+ logger.Debug("Service discovery refresh complete")
s.edgeIPs = result.edgeIPs
} else {
- Log.WithError(result.err).Error("Service discovery error")
+ logger.WithError(result.err).Error("Service discovery error")
}
}
}
@@ -121,12 +121,12 @@ func (s *Supervisor) Run(ctx context.Context, connectedSignal chan struct{}) err
func (s *Supervisor) initialize(ctx context.Context, connectedSignal chan struct{}) error {
edgeIPs, err := ResolveEdgeIPs(s.config.EdgeAddrs)
if err != nil {
- Log.Infof("ResolveEdgeIPs err")
+ logger.Infof("ResolveEdgeIPs err")
return err
}
s.edgeIPs = edgeIPs
if s.config.HAConnections > len(edgeIPs) {
- Log.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, len(edgeIPs))
+ logger.Warnf("You requested %d HA connections but I can give you at most %d.", s.config.HAConnections, len(edgeIPs))
s.config.HAConnections = len(edgeIPs)
}
s.lastResolve = time.Now()
diff --git a/origin/tunnel.go b/origin/tunnel.go
index 96d06469..8257d3d1 100644
--- a/origin/tunnel.go
+++ b/origin/tunnel.go
@@ -7,7 +7,6 @@ import (
"net"
"net/http"
"net/url"
- "runtime"
"strconv"
"strings"
"sync"
@@ -28,7 +27,7 @@ import (
rpc "zombiezen.com/go/capnproto2/rpc"
)
-var Log *logrus.Logger
+var logger *logrus.Logger
const (
dialTimeout = 15 * time.Second
@@ -48,17 +47,19 @@ type TunnelConfig struct {
HeartbeatInterval time.Duration
MaxHeartbeats uint64
ClientID string
+ BuildInfo *BuildInfo
ReportedVersion string
LBPool string
Tags []tunnelpogs.Tag
HAConnections int
HTTPTransport http.RoundTripper
- Metrics *tunnelMetrics
+ Metrics *TunnelMetrics
MetricsUpdateFreq time.Duration
ProtocolLogger *logrus.Logger
Logger *logrus.Logger
IsAutoupdated bool
GracePeriod time.Duration
+ RunFromTerminal bool
}
type dialError struct {
@@ -92,18 +93,19 @@ func (c *TunnelConfig) RegistrationOptions(connectionID uint8, OriginLocalIP str
return &tunnelpogs.RegistrationOptions{
ClientID: c.ClientID,
Version: c.ReportedVersion,
- OS: fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH),
+ OS: fmt.Sprintf("%s_%s", c.BuildInfo.GoOS, c.BuildInfo.GoArch),
ExistingTunnelPolicy: policy,
PoolName: c.LBPool,
Tags: c.Tags,
ConnectionID: connectionID,
OriginLocalIP: OriginLocalIP,
IsAutoupdated: c.IsAutoupdated,
+ RunFromTerminal: c.RunFromTerminal,
}
}
func StartTunnelDaemon(config *TunnelConfig, shutdownC <-chan struct{}, connectedSignal chan struct{}) error {
- Log = config.Logger
+ logger = config.Logger
ctx, cancel := context.WithCancel(context.Background())
go func() {
<-shutdownC
@@ -138,7 +140,7 @@ func ServeTunnelLoop(ctx context.Context, config *TunnelConfig, addr *net.TCPAdd
err, recoverable := ServeTunnel(ctx, config, addr, connectionID, connectedFuse, &backoff)
if recoverable {
if duration, ok := backoff.GetBackoffDuration(ctx); ok {
- Log.Infof("Retrying in %s seconds", duration)
+ logger.Infof("Retrying in %s seconds", duration)
backoff.Backoff(ctx)
continue
}
@@ -171,7 +173,7 @@ func ServeTunnel(
// Returns error from parsing the origin URL or handshake errors
handler, originLocalIP, err := NewTunnelHandler(ctx, config, addr.String(), connectionID)
if err != nil {
- errLog := Log.WithError(err)
+ errLog := logger.WithError(err)
switch err.(type) {
case dialError:
errLog.Error("Unable to dial edge")
@@ -218,21 +220,21 @@ func ServeTunnel(
registerErr := <-registerErrC
wg.Wait()
if err != nil {
- Log.WithError(err).Error("Tunnel error")
+ logger.WithError(err).Error("Tunnel error")
return err, true
}
if registerErr != nil {
// Don't retry on errors like entitlement failure or version too old
if e, ok := registerErr.(printableRegisterTunnelError); ok {
- Log.Error(e)
+ logger.Error(e)
return e.cause, !e.permanent
} else if e, ok := registerErr.(dupConnRegisterTunnelError); ok {
- Log.Info("Already connected to this server, selecting a different one")
+ logger.Info("Already connected to this server, selecting a different one")
return e, true
}
// Only log errors to Sentry that may have been caused by the client side, to reduce dupes
raven.CaptureError(registerErr, nil)
- Log.Error("Cannot register")
+ logger.Error("Cannot register")
return err, true
}
return nil, false
@@ -249,7 +251,7 @@ func IsRPCStreamResponse(headers []h2mux.Header) bool {
}
func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfig, connectionID uint8, originLocalIP string) error {
- logger := Log.WithField("subsystem", "rpc")
+ logger := logger.WithField("subsystem", "rpc")
logger.Debug("initiating RPC stream to register")
stream, err := muxer.OpenStream([]h2mux.Header{
{Name: ":method", Value: "RPC"},
@@ -304,7 +306,7 @@ func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfi
}
func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration) error {
- logger := Log.WithField("subsystem", "rpc")
+ logger := logger.WithField("subsystem", "rpc")
logger.Debug("initiating RPC stream to unregister")
stream, err := muxer.OpenStream([]h2mux.Header{
{Name: ":method", Value: "RPC"},
@@ -335,7 +337,7 @@ func UnregisterTunnel(muxer *h2mux.Muxer, gracePeriod time.Duration) error {
func LogServerInfo(logger *logrus.Entry,
promise tunnelrpc.ServerInfo_Promise,
connectionID uint8,
- metrics *tunnelMetrics,
+ metrics *TunnelMetrics,
) {
serverInfoMessage, err := promise.Struct()
if err != nil {
@@ -347,7 +349,7 @@ func LogServerInfo(logger *logrus.Entry,
logger.WithError(err).Warn("Failed to retrieve server information")
return
}
- Log.Infof("Connected to %s", serverInfo.LocationName)
+ logger.Infof("Connected to %s", serverInfo.LocationName)
metrics.registerServerLocation(uint8ToString(connectionID), serverInfo.LocationName)
}
@@ -398,7 +400,7 @@ type TunnelHandler struct {
httpClient http.RoundTripper
tlsConfig *tls.Config
tags []tunnelpogs.Tag
- metrics *tunnelMetrics
+ metrics *TunnelMetrics
// connectionID is only used by metrics, and prometheus requires labels to be string
connectionID string
}
@@ -407,12 +409,12 @@ var dialer = net.Dialer{DualStack: true}
// NewTunnelHandler returns a TunnelHandler, origin LAN IP and error
func NewTunnelHandler(ctx context.Context, config *TunnelConfig, addr string, connectionID uint8) (*TunnelHandler, string, error) {
- url, err := validation.ValidateUrl(config.OriginUrl)
+ originURL, err := validation.ValidateUrl(config.OriginUrl)
if err != nil {
- return nil, "", fmt.Errorf("Unable to parse origin url %#v", url)
+ return nil, "", fmt.Errorf("Unable to parse origin url %#v", originURL)
}
h := &TunnelHandler{
- originUrl: url,
+ originUrl: originURL,
httpClient: config.HTTPTransport,
tlsConfig: config.ClientTlsConfig,
tags: config.Tags,
@@ -464,11 +466,11 @@ func (h *TunnelHandler) ServeStream(stream *h2mux.MuxedStream) error {
h.metrics.incrementRequests(h.connectionID)
req, err := http.NewRequest("GET", h.originUrl, h2mux.MuxedStreamReader{MuxedStream: stream})
if err != nil {
- Log.WithError(err).Panic("Unexpected error from http.NewRequest")
+ logger.WithError(err).Panic("Unexpected error from http.NewRequest")
}
err = H2RequestHeadersToH1Request(stream.Headers, req)
if err != nil {
- Log.WithError(err).Error("invalid request received")
+ logger.WithError(err).Error("invalid request received")
}
h.AppendTagHeaders(req)
cfRay := FindCfRayHeader(req)
@@ -501,7 +503,7 @@ func (h *TunnelHandler) ServeStream(stream *h2mux.MuxedStream) error {
}
func (h *TunnelHandler) logError(stream *h2mux.MuxedStream, err error) {
- Log.WithError(err).Error("HTTP request error")
+ logger.WithError(err).Error("HTTP request error")
stream.WriteHeaders([]h2mux.Header{{Name: ":status", Value: "502"}})
stream.Write([]byte("502 Bad Gateway"))
h.metrics.incrementResponses(h.connectionID, "502")
@@ -509,20 +511,20 @@ func (h *TunnelHandler) logError(stream *h2mux.MuxedStream, err error) {
func (h *TunnelHandler) logRequest(req *http.Request, cfRay string) {
if cfRay != "" {
- Log.WithField("CF-RAY", cfRay).Infof("%s %s %s", req.Method, req.URL, req.Proto)
+ logger.WithField("CF-RAY", cfRay).Infof("%s %s %s", req.Method, req.URL, req.Proto)
} else {
- Log.Warnf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", req.Method, req.URL, req.Proto)
+ logger.Warnf("All requests should have a CF-RAY header. Please open a support ticket with Cloudflare. %s %s %s ", req.Method, req.URL, req.Proto)
}
- Log.Debugf("Request Headers %+v", req.Header)
+ logger.Debugf("Request Headers %+v", req.Header)
}
func (h *TunnelHandler) logResponse(r *http.Response, cfRay string) {
if cfRay != "" {
- Log.WithField("CF-RAY", cfRay).Infof("%s", r.Status)
+ logger.WithField("CF-RAY", cfRay).Infof("%s", r.Status)
} else {
- Log.Infof("%s", r.Status)
+ logger.Infof("%s", r.Status)
}
- Log.Debugf("Response Headers %+v", r.Header)
+ logger.Debugf("Response Headers %+v", r.Header)
}
func (h *TunnelHandler) UpdateMetrics(connectionID string) {
diff --git a/tlsconfig/tlsconfig.go b/tlsconfig/tlsconfig.go
index d81e94f2..ba76cf97 100644
--- a/tlsconfig/tlsconfig.go
+++ b/tlsconfig/tlsconfig.go
@@ -8,10 +8,12 @@ import (
"io/ioutil"
"net"
- log "github.com/sirupsen/logrus"
- cli "gopkg.in/urfave/cli.v2"
+ "github.com/cloudflare/cloudflared/log"
+ "gopkg.in/urfave/cli.v2"
)
+var logger = log.CreateLogger()
+
// CLIFlags names the flags used to configure TLS for a command or subsystem.
// The nil value for a field means the flag is ignored.
type CLIFlags struct {
@@ -29,7 +31,7 @@ func (f CLIFlags) GetConfig(c *cli.Context) *tls.Config {
if c.IsSet(f.Cert) && c.IsSet(f.Key) {
cert, err := tls.LoadX509KeyPair(c.String(f.Cert), c.String(f.Key))
if err != nil {
- log.WithError(err).Fatal("Error parsing X509 key pair")
+ logger.WithError(err).Fatal("Error parsing X509 key pair")
}
config.Certificates = []tls.Certificate{cert}
config.BuildNameToCertificate()
@@ -53,11 +55,11 @@ func (f CLIFlags) GetConfig(c *cli.Context) *tls.Config {
func LoadCert(certPath string) *x509.CertPool {
caCert, err := ioutil.ReadFile(certPath)
if err != nil {
- log.WithError(err).Fatalf("Error reading certificate %s", certPath)
+ logger.WithError(err).Fatalf("Error reading certificate %s", certPath)
}
ca := x509.NewCertPool()
if !ca.AppendCertsFromPEM(caCert) {
- log.WithError(err).Fatalf("Error parsing certificate %s", certPath)
+ logger.WithError(err).Fatalf("Error parsing certificate %s", certPath)
}
return ca
}
@@ -66,23 +68,23 @@ func LoadOriginCertsPool() *x509.CertPool {
// First, obtain the system certificate pool
certPool, systemCertPoolErr := x509.SystemCertPool()
if systemCertPoolErr != nil {
- log.Warn("error obtaining the system certificates: %s", systemCertPoolErr)
+ logger.Warnf("error obtaining the system certificates: %s", systemCertPoolErr)
certPool = x509.NewCertPool()
}
// Next, append the Cloudflare CA pool into the system pool
if !certPool.AppendCertsFromPEM([]byte(cloudflareRootCA)) {
- log.Warn("could not append the CF certificate to the system certificate pool")
+ logger.Warn("could not append the CF certificate to the system certificate pool")
if systemCertPoolErr != nil { // Obtaining both certificates failed; this is a fatal error
- log.WithError(systemCertPoolErr).Fatalf("Error loading the certificate pool")
+ logger.WithError(systemCertPoolErr).Fatalf("Error loading the certificate pool")
}
}
// Finally, add the Hello certificate into the pool (since it's self-signed)
helloCertificate, err := GetHelloCertificateX509()
if err != nil {
- log.Warn("error obtaining the Hello server certificate")
+ logger.Warn("error obtaining the Hello server certificate")
}
certPool.AddCert(helloCertificate)
diff --git a/tunneldns/tunnel.go b/tunneldns/tunnel.go
index efd1229d..13db1fb6 100644
--- a/tunneldns/tunnel.go
+++ b/tunneldns/tunnel.go
@@ -8,16 +8,18 @@ import (
"sync"
"syscall"
- "gopkg.in/urfave/cli.v2"
-
+ "github.com/cloudflare/cloudflared/log"
"github.com/cloudflare/cloudflared/metrics"
+
"github.com/coredns/coredns/core/dnsserver"
"github.com/coredns/coredns/plugin"
"github.com/coredns/coredns/plugin/cache"
"github.com/pkg/errors"
- log "github.com/sirupsen/logrus"
+ "gopkg.in/urfave/cli.v2"
)
+var logger = log.CreateLogger()
+
// Listener is an adapter between CoreDNS server and Warp runnable
type Listener struct {
server *dnsserver.Server
@@ -28,14 +30,14 @@ type Listener struct {
func Run(c *cli.Context) error {
metricsListener, err := net.Listen("tcp", c.String("metrics"))
if err != nil {
- log.WithError(err).Fatal("Failed to open the metrics listener")
+ logger.WithError(err).Fatal("Failed to open the metrics listener")
}
- go metrics.ServeMetrics(metricsListener, nil)
+ go metrics.ServeMetrics(metricsListener, nil, logger)
listener, err := CreateListener(c.String("address"), uint16(c.Uint("port")), c.StringSlice("upstream"))
if err != nil {
- log.WithError(err).Errorf("Failed to create the listeners")
+ logger.WithError(err).Errorf("Failed to create the listeners")
return err
}
@@ -43,7 +45,7 @@ func Run(c *cli.Context) error {
readySignal := make(chan struct{})
err = listener.Start(readySignal)
if err != nil {
- log.WithError(err).Errorf("Failed to start the listeners")
+ logger.WithError(err).Errorf("Failed to start the listeners")
return listener.Stop()
}
<-readySignal
@@ -57,7 +59,7 @@ func Run(c *cli.Context) error {
// Shut down server
err = listener.Stop()
if err != nil {
- log.WithError(err).Errorf("failed to stop")
+ logger.WithError(err).Errorf("failed to stop")
}
return err
}
@@ -78,7 +80,7 @@ func createConfig(address string, port uint16, p plugin.Handler) *dnsserver.Conf
// Start blocks for serving requests
func (l *Listener) Start(readySignal chan struct{}) error {
defer close(readySignal)
- log.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server")
+ logger.WithField("addr", l.server.Address()).Infof("Starting DNS over HTTPS proxy server")
// Start UDP listener
if udp, err := l.server.ListenPacket(); err == nil {
@@ -119,7 +121,7 @@ func CreateListener(address string, port uint16, upstreams []string) (*Listener,
// Build the list of upstreams
upstreamList := make([]Upstream, 0)
for _, url := range upstreams {
- log.WithField("url", url).Infof("Adding DNS upstream")
+ logger.WithField("url", url).Infof("Adding DNS upstream")
upstream, err := NewUpstreamHTTPS(url)
if err != nil {
return nil, errors.Wrap(err, "failed to create HTTPS upstream")
diff --git a/tunnelrpc/pogs/tunnelrpc.go b/tunnelrpc/pogs/tunnelrpc.go
index ab14060a..8d297a2f 100644
--- a/tunnelrpc/pogs/tunnelrpc.go
+++ b/tunnelrpc/pogs/tunnelrpc.go
@@ -52,7 +52,7 @@ type RegistrationOptions struct {
ConnectionID uint8 `capnp:"connectionId"`
OriginLocalIP string `capnp:"originLocalIp"`
IsAutoupdated bool `capnp:"isAutoupdated"`
- gracePeriodNanoSec int64 `capnp:"gracePeriodNanoSec"`
+ RunFromTerminal bool `capnp:"runFromTerminal"`
}
func MarshalRegistrationOptions(s tunnelrpc.RegistrationOptions, p *RegistrationOptions) error {
diff --git a/tunnelrpc/tunnelrpc.capnp b/tunnelrpc/tunnelrpc.capnp
index d90ef200..cbc25755 100644
--- a/tunnelrpc/tunnelrpc.capnp
+++ b/tunnelrpc/tunnelrpc.capnp
@@ -37,6 +37,8 @@ struct RegistrationOptions {
originLocalIp @7 :Text;
# whether Argo Tunnel client has been autoupdated
isAutoupdated @8 :Bool;
+ # whether Argo Tunnel client is run from a terminal
+ runFromTerminal @9 :Bool;
}
struct Tag {
diff --git a/tunnelrpc/tunnelrpc.capnp.go b/tunnelrpc/tunnelrpc.capnp.go
index 0b54a66b..112b59d1 100644
--- a/tunnelrpc/tunnelrpc.capnp.go
+++ b/tunnelrpc/tunnelrpc.capnp.go
@@ -403,6 +403,14 @@ func (s RegistrationOptions) SetIsAutoupdated(v bool) {
s.Struct.SetBit(24, v)
}
+func (s RegistrationOptions) RunFromTerminal() bool {
+ return s.Struct.Bit(25)
+}
+
+func (s RegistrationOptions) SetRunFromTerminal(v bool) {
+ s.Struct.SetBit(25, v)
+}
+
// RegistrationOptions_List is a list of RegistrationOptions.
type RegistrationOptions_List struct{ capnp.List }
@@ -1218,86 +1226,88 @@ func (p TunnelServer_unregisterTunnel_Results_Promise) Struct() (TunnelServer_un
return TunnelServer_unregisterTunnel_Results{s}, err
}
-const schema_db8274f9144abc7e = "x\xda\x9cU_\x88Te\x1b\x7f~\xef;3g\x84" +
- "]g\x0fg\x04\xbf\xe1\x93\xe5\x93\x15\xff\xe0\xfa\xe9\xa7" +
- "\xfb\xa1\xdb\x9f\xd9\xdd\xd4\x98m]\xe7\xdd\xd1\x10\xf5\xc2" +
- "\xe3\xcc\xeb\xec\xd9f\xce\x19\xce9c)\xa4%B " +
- "$\x96uc\x14\xe4e\x17\x15t\x11\x84\x81\xdd$\xe1" +
- "\x85\x08\x19EB\x94l\xa1(\xd6\xa2\x90\x91\x9cx\xcf" +
- "\xec\x999\xae\xa5\xd6\xdd9\xcf\xfb>\xcf\xf3{\xfe\xfc" +
- "~\xef\xea\x1f\xd9\x10[\x93\xfc.I$6&S\xc1" +
- "\xe3\xf5\x0b\xa7\xff\xff\xe6\xf9\xa3\xa4\xe7Xp\xe8\xcch" +
- "\xf6\x8e\x7f\xe4[\"\xac\xbd\xc4\x0e\xc2\xb8\xca4\"c" +
- "\x9am%\x04\x17V\x9e\xf9\xe4\xc4G\xaf\xbcEb)" +
- "@\x94\xd0\x88\xd6\xdee\xbf\x81`\xe8n\x9d$\xb9:\xea\xe7\xd7" +
- "\x94\xeb0\xff\x80\x10,\xba1\xd2m\xdf\xba\x89\xa1\x9b" +
- "\x10L:\x9eo\x9buID\xe8\"\x86.\xc2a\xa7" +
- "\xe1[\x8e\xed\xa1\xa7C\x07\x02zb-\xf8\x93\x01\x0f" +
- "7\xfdIi\xfbV\xd9T\xceD\xe1l;\x90\x17\x13" +
- "\x89!\x0e1\x16\x83\\\xf8_\xac\x8e\x08\xf2\x96\xbd\x9d" +
- ":\xb4\xe7\xe4\x81\x08U\xaf\xac\x9bV-\xfa\x8b\x8a\x19" +
- "&\xed\x99\xce\x9d\x07\xe1\x9b\x08\xbb\xea\x86\xe8\xb66z" +
- "\xc3\x0a\x15\xc6\xbe6\xc6\x1b\xaa\x83\xd79\xc4\xaf1\x8c" +
- "\xb7U\x07\x7f\xe6\x10\xbf\xc70\xde\xc9\x11\x89[\x1c\x13" +
- "`\x00\xcf\x82\x13\xe9w\xdf#\x9a\x00G\xa9\x0b\x0cz" +
- "\x82g\x91 2\xe6a\x94\xa8\x94V\xf6\xac\xb2'\x13" +
- "Y$\x15\xb7\xb0\x82\xa8\xd4\xa5\xec\xcb\x94=\xc5\xb2H" +
- "\x11\x19K0ET\xeaS\xf6\xd5\xca\xae%\xb3\x8a\x96" +
- "F?\\\xa2\xd2Je_\xaf\xec\xe9\x85Y\xa4\x89\x8c" +
- "\x81\xd0\xbeN\xd9\x87\xc0\x10\x94k\x96\xb4\xfdB%>" +
- "\xce\xfd\xd2\xf5,\xc7\x8e\xfe\xb9\xe3\xb5\xfb%gY\x89" +
- "\xd6\xae\x15\x9d\x8c\xa2%2\x1d\xe9& C\x08\x1a\x8e" +
- "S\x1b\xbfwM2\xbeY\xf50\x9fP\xe4@OG" +
- "\x0a\x09\xca\x18\x84\xa4-\xfb\x16e\x1c\xbbPA\x8a\x18" +
- "R\xed\xb9\x8d9\xd4[6k\x85F\x1b\x89\xe5\x0d7" +
- "}\xa7\xd9\xa0\xde\x8a\xe9\xcb\x0a@\x0c\x88M\x94\xcd\x9d" +
- "hocp\x9bYU\x13L\xb7'\xb8|\x05\x91\xe8" +
- "\xe3\x10\xabc\x13\xecW[\xb6\x8cC\xacc\xc8\xa8U" +
- "oo\xd4~\xb3\xd6\x94\xf7\xed\xce\xc34\xa5*\xfd\xd6" +
- "W\xc1\xde\xe7\xf4\x15MW3\xeb\xde?\xf4\x9e\x90^" +
- "FIC\\\x8f\x06\x89D\x9aCd\x19\xf2n\xa8\x1c" +
- "\xe8\xe9H\xf6\x1c\"\xf2\xbfJ\x97oei\xb10I" +
- "\xd4~'\x11=\x0f\xba8HL/h\xe8\xa0\x81\xb5\x9ffDO\xb0\xbe" +
- "\xfc\x181}\x89\x16D:E\xf9V\xca!\x04Qu" +
- "\xd4\x1b\xd67\x84 \x12CDzF4\x84\"\x1e\xbd" +
- "\xdd\xf7ii\xaf\xf7(\x1d\x8b\xde\xad\x87\xf7\xab\x95'" +
- "\xa3\xf0\xaan\xc5\xe2N\x11\x89.\x0e\xb1\x90!\xa89" +
- "\xb3\xc2\x96\x19\x8f\xad\xd0\x83\x04\xa7\x058\x92\x9d\x8cr" +
- "V\xf1{\xda\xf1M\xa5\x89\xbb9\xc4dl[\xa52" +
- "\xee\xe1\x10\xb5\x98\xdeXJ\x99&9\xc4\xd1\x8e\xde\xbc" +
- "|\x8cH\x1c\xe5\x10'\x184\xe9\xba\x11$\xad\xe9v" +
- "T\xb2\xe6T\xc7,[z\x8a\xb6\xb3LUG\x8a\x9f" +
- "\x0d\xe9\xd6M[\xda\xf07\x9bV\xad\xe9J\xb5Z-" +
- "\xd2\xfd\x11\x00\x00\xff\xffy%\x9bh"
+const schema_db8274f9144abc7e = "x\xda\x9cU_l\x14U\x17\xff\x9d{w;\xdb\xa4" +
+ "e;\x99m\x02\x1bH\x13\xd2\x86B\xbe\xf2\xc1\xc7\x87" +
+ "B\xfd\xd3?\x02\xba\xb5\x94\xbdl1\x08}`\xd8\xbd" +
+ "l\xa7\xce\xcelffQH\x04%\x18#\x89\x04E" +
+ "^0\x9a@\xe2\x8b\x0fj\xe2\x83\x89\xc1\x04_\xe4\x81" +
+ "\x07^\xd4h$1J\x88\x91h\x88\x8d\x9ahb\xc6" +
+ "\xdc\xd9\xce\xecR\x14\xd0\xb7\x99\xdf=\xf7\x9c\xdf9\xf7" +
+ "\x9c\xdfY\xf7+\x1be\xeb\xd3_\xa7\x01\xb1%\xdd\x11" +
+ ">X\xbbr\xfe\xbe3\x97\x8fC\xcf\xb3\xf0\xc8\x85\x89" +
+ "\xdco\xc1\xb1\xaf\x00\xda\xf0\x19;L\xc6\xf7L\x03\x8c" +
+ "\xebl\x07(\xbc\xf2\x9f\x0b\x1f\x9ez\xff\xc5\xd7!V" +
+ "\x11\x01)\x0d\xd8\xf0\x07\xfb\x9d@\x86\xceG@\xe1k" +
+ "_|4U{\xe5\xecy\xe8\xab\xe2\xf3\xcd\x9c1\xa4" +
+ "\xc2\xde\x02]\xbd\xb8>\xf5A\xf3$\xcd\xd5\xd1\x10\xbf" +
+ "\xa1\xae\x8e\xf1wA\xe1\x8a\x1f\xc7\xbb\x9d\x9b\xc7.B" +
+ "\xcfS\x8bE\xd3\xf0\x1b>A\xc6/\xea\xd3\xf8)2" +
+ "\x9e\xd8{\xfa\xd5\xf4\xf5\xd3\x97 \xf2\xd4n\xdd\xa1\xac" +
+ "_Nyd\x9c\x8b\x82\xbf\x91\xba\x9f\x81\xc2\xfc{\x0f" +
+ "\xbc3^\xf9\xf2\xf2\"\xdfQf\x9d\xda\xbc\xd1\xab\xa9" +
+ "/]{\x1a\x14\xb2\xeb\xe6\xb2\xe7>\x7f\xf8j[\x0a" +
+ "5\xed[B*\x9czb\xef\\\xe7\xb3\xd7\xae-\xa4" +
+ "@\xea\xc8\xd4\xa2\x14\x1a\x9a\xca~\xe3\xbe19\xb3i" +
+ "\xf7\x0d\xe8y~K!\xcfh\xc3d\xbc\x15\x059\xa7" +
+ "]2Vg4 \xa0\x85\xb1\xc8a\xa4\x19r" +
+ "\x94\xc28;\xf4E\xf9\x8dR\x18+)\xc5b\x08\x8c" +
+ "R\x91\xee\xbd\xdc\xb7\x09q\x9f\x7f/\x15\x8b\x97\xde\xdd" +
+ "\xeb\xd5\x8c\x93U|U\xb5\xda\xfc\xce\x01\xa2\x8b\x93X" +
+ "\xca(\xb4\xdd\x05U\xccN\xb5\xb5\xd0\x9d\xd4\xaaI8" +
+ "\xd6\xac\xac\xba\xac\xfc\xf7$\xfeM%\xa83\x9c\xc4l" +
+ "[\xb7J\x05\xee\xe3$\xec6A\xb5\xd4b\x98\xe5$" +
+ "\x8e'\x1a\xa5?\x7f\x02\x10\xc79\x89S\x8c4\xe9y" +
+ "1%\xad\xe1\xb5$\xd6v\xab\x93\x96#}5\xd0\x0b" +
+ "3\xac\x8e\xd4\xe4\xd6\xa5W3\x1d\xe9P\xb0\xcd\xb4\xec" +
+ "\x86'\x91\x0c\xdd\x9f\x01\x00\x00\xff\xffR3\x9d#"
func init() {
schemas.Register(schema_db8274f9144abc7e,