diff --git a/cmd/cloudflared/common_service.go b/cmd/cloudflared/common_service.go new file mode 100644 index 00000000..db7338c0 --- /dev/null +++ b/cmd/cloudflared/common_service.go @@ -0,0 +1,30 @@ +package main + +import ( + "github.com/rs/zerolog" + "github.com/urfave/cli/v2" + + "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" + "github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel" +) + +func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) { + token := c.Args().First() + if _, err := tunnel.ParseToken(token); err != nil { + return nil, cliutil.UsageError("Provided tunnel token is not valid (%s).", err) + } + + return []string{ + "tunnel", "run", "--token", token, + }, nil +} + +func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) { + if c.NArg() > 0 { + // currently, we only support extra args for token + return buildArgsForToken(c, log) + } else { + // empty extra args + return make([]string, 0), nil + } +} diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index 85d195fa..2d3060e7 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -6,7 +6,6 @@ package main import ( "fmt" "os" - "path/filepath" "github.com/rs/zerolog" "github.com/urfave/cli/v2" @@ -26,12 +25,6 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) { Name: "install", Usage: "Install Cloudflare Tunnel as a system service", Action: cliutil.ConfiguredAction(installLinuxService), - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "legacy", - Usage: "Generate service file for non-named tunnels", - }, - }, }, { Name: "uninstall", @@ -62,7 +55,7 @@ After=network.target [Service] TimeoutStartSec=0 Type=notify -ExecStart={{ .Path }} --config /etc/cloudflared/config.yml --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} +ExecStart={{ .Path }} --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} Restart=on-failure RestartSec=5s @@ -112,7 +105,7 @@ var sysvTemplate = ServiceTemplate{ # Description: Cloudflare Tunnel agent ### END INIT INFO name=$(basename $(readlink -f $0)) -cmd="{{.Path}} --config /etc/cloudflared/config.yml --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}" +cmd="{{.Path}} --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}" pid_file="/var/run/$name.pid" stdout_log="/var/log/$name.log" stderr_log="/var/log/$name.err" @@ -191,27 +184,6 @@ func isSystemd() bool { return false } -func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, log *zerolog.Logger) error { - srcCredentialPath := filepath.Join(userConfigDir, userCredentialFile) - destCredentialPath := filepath.Join(serviceConfigDir, serviceCredentialFile) - if srcCredentialPath != destCredentialPath { - if err := copyCredential(srcCredentialPath, destCredentialPath); err != nil { - return err - } - } - - srcConfigPath := filepath.Join(userConfigDir, userConfigFile) - destConfigPath := filepath.Join(serviceConfigDir, serviceConfigFile) - if srcConfigPath != destConfigPath { - if err := copyConfig(srcConfigPath, destConfigPath); err != nil { - return err - } - log.Info().Msgf("Copied %s to %s", srcConfigPath, destConfigPath) - } - - return nil -} - func installLinuxService(c *cli.Context) error { log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog) @@ -223,52 +195,19 @@ func installLinuxService(c *cli.Context) error { Path: etPath, } - if err := ensureConfigDirExists(serviceConfigDir); err != nil { + var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error) + if c.NArg() == 0 { + extraArgsFunc = buildArgsForConfig + } else { + extraArgsFunc = buildArgsForToken + } + + extraArgs, err := extraArgsFunc(c, log) + if err != nil { return err } - if c.Bool("legacy") { - userConfigDir := filepath.Dir(c.String("config")) - userConfigFile := filepath.Base(c.String("config")) - userCredentialFile := config.DefaultCredentialFile - if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, log); err != nil { - log.Err(err).Msgf("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s", - serviceConfigDir, serviceCredentialFile, serviceConfigFile) - return err - } - templateArgs.ExtraArgs = []string{ - "--origincert", serviceConfigDir + "/" + serviceCredentialFile, - } - } else { - src, _, err := config.ReadConfigFile(c, log) - if err != nil { - return err - } - // can't use context because this command doesn't define "credentials-file" flag - configPresent := func(s string) bool { - val, err := src.String(s) - return err == nil && val != "" - } - if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) { - return fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials: -tunnel: TUNNEL-UUID -credentials-file: CREDENTIALS-FILE -`, src.Source()) - } - if src.Source() != serviceConfigPath { - if exists, err := config.FileExists(serviceConfigPath); err != nil || exists { - return fmt.Errorf("Possible conflicting configuration in %[1]s and %[2]s. Either remove %[2]s or run `cloudflared --config %[2]s service install`", src.Source(), serviceConfigPath) - } - - if err := copyFile(src.Source(), serviceConfigPath); err != nil { - return fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err) - } - } - - templateArgs.ExtraArgs = []string{ - "tunnel", "run", - } - } + templateArgs.ExtraArgs = extraArgs switch { case isSystemd(): @@ -280,6 +219,42 @@ credentials-file: CREDENTIALS-FILE } } +func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) { + if err := ensureConfigDirExists(serviceConfigDir); err != nil { + return nil, err + } + + src, _, err := config.ReadConfigFile(c, log) + if err != nil { + return nil, err + } + + // can't use context because this command doesn't define "credentials-file" flag + configPresent := func(s string) bool { + val, err := src.String(s) + return err == nil && val != "" + } + if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) { + return nil, fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials: +tunnel: TUNNEL-UUID +credentials-file: CREDENTIALS-FILE +`, src.Source()) + } + if src.Source() != serviceConfigPath { + if exists, err := config.FileExists(serviceConfigPath); err != nil || exists { + return nil, fmt.Errorf("Possible conflicting configuration in %[1]s and %[2]s. Either remove %[2]s or run `cloudflared --config %[2]s service install`", src.Source(), serviceConfigPath) + } + + if err := copyFile(src.Source(), serviceConfigPath); err != nil { + return nil, fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err) + } + } + + return []string{ + "--config", "/etc/cloudflared/config.yml", "tunnel", "run", + }, nil +} + func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { for _, serviceTemplate := range systemdTemplates { err := serviceTemplate.Generate(templateArgs) diff --git a/cmd/cloudflared/macos_service.go b/cmd/cloudflared/macos_service.go index e987df87..542b8849 100644 --- a/cmd/cloudflared/macos_service.go +++ b/cmd/cloudflared/macos_service.go @@ -50,6 +50,9 @@ func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemp ProgramArguments {{ .Path }} + {{- range $i, $item := .ExtraArgs}} + {{ $item }} + {{- end}} RunAtLoad @@ -129,6 +132,13 @@ func installLaunchd(c *cli.Context) error { log.Err(err).Msg("Error determining install path") return errors.Wrap(err, "Error determining install path") } + extraArgs, err := getServiceExtraArgsFromCliArgs(c, log) + if err != nil { + errMsg := "Unable to determine extra arguments for launch daemon" + log.Err(err).Msg(errMsg) + return errors.Wrap(err, errMsg) + } + stdoutPath, err := stdoutPath() if err != nil { log.Err(err).Msg("error determining stdout path") @@ -140,7 +150,7 @@ func installLaunchd(c *cli.Context) error { return errors.Wrap(err, "error determining stderr path") } launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath) - templateArgs := ServiceTemplateArgs{Path: etPath} + templateArgs := ServiceTemplateArgs{Path: etPath, ExtraArgs: extraArgs} err = launchdTemplate.Generate(&templateArgs) if err != nil { log.Err(err).Msg("error generating launchd template") diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 8362d8f8..905c8ad6 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -644,7 +644,7 @@ func runCommand(c *cli.Context) error { // Check if token is provided and if not use default tunnelID flag method if tokenStr := c.String(TunnelTokenFlag); tokenStr != "" { - if token, err := parseToken(tokenStr); err == nil { + if token, err := ParseToken(tokenStr); err == nil { return sc.runWithCredentials(token.Credentials()) } @@ -663,7 +663,7 @@ func runCommand(c *cli.Context) error { } } -func parseToken(tokenStr string) (*connection.TunnelToken, error) { +func ParseToken(tokenStr string) (*connection.TunnelToken, error) { content, err := base64.StdEncoding.DecodeString(tokenStr) if err != nil { return nil, err diff --git a/cmd/cloudflared/tunnel/subcommands_test.go b/cmd/cloudflared/tunnel/subcommands_test.go index 81f542c7..2016fe6d 100644 --- a/cmd/cloudflared/tunnel/subcommands_test.go +++ b/cmd/cloudflared/tunnel/subcommands_test.go @@ -183,7 +183,7 @@ func Test_validateHostname(t *testing.T) { } func Test_TunnelToken(t *testing.T) { - token, err := parseToken("aabc") + token, err := ParseToken("aabc") require.Error(t, err) require.Nil(t, token) @@ -198,7 +198,7 @@ func Test_TunnelToken(t *testing.T) { token64 := base64.StdEncoding.EncodeToString(tokenJsonStr) - token, err = parseToken(token64) + token, err = ParseToken(token64) require.NoError(t, err) require.Equal(t, token, expectedToken) } diff --git a/cmd/cloudflared/windows_service.go b/cmd/cloudflared/windows_service.go index 2eba4780..6006c0b0 100644 --- a/cmd/cloudflared/windows_service.go +++ b/cmd/cloudflared/windows_service.go @@ -193,8 +193,15 @@ func installWindowsService(c *cli.Context) error { s.Close() return fmt.Errorf("Service %s already exists", windowsServiceName) } + extraArgs, err := getServiceExtraArgsFromCliArgs(c, &log) + if err != nil { + errMsg := "Unable to determine extra arguments for windows service" + log.Err(err).Msg(errMsg) + return errors.Wrap(err, errMsg) + } + config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription} - s, err = m.CreateService(windowsServiceName, exepath, config) + s, err = m.CreateService(windowsServiceName, exepath, config, extraArgs...) if err != nil { return errors.Wrap(err, "Cannot install service") }