From faa43cde1c4400a3908dc7c50344ebf0953e72c2 Mon Sep 17 00:00:00 2001 From: iBug Date: Tue, 18 Jul 2023 01:02:22 +0800 Subject: [PATCH] Write tunnel token to EnvironmentFile, resolve #802 --- cmd/cloudflared/common_service.go | 16 +++++----- cmd/cloudflared/linux_service.go | 25 +++++++++------ cmd/cloudflared/service_template.go | 48 ++++++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 21 deletions(-) diff --git a/cmd/cloudflared/common_service.go b/cmd/cloudflared/common_service.go index db7338c0..2f6591e6 100644 --- a/cmd/cloudflared/common_service.go +++ b/cmd/cloudflared/common_service.go @@ -8,23 +8,25 @@ import ( "github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel" ) -func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) { +type buildArgsFunc = func(c *cli.Context, log *zerolog.Logger) ([]string, map[string]string, error) + +func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, map[string]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 nil, nil, cliutil.UsageError("Provided tunnel token is not valid (%s).", err) } - return []string{ - "tunnel", "run", "--token", token, - }, nil + return []string{"tunnel", "run"}, + map[string]string{"TUNNEL_TOKEN": token}, + nil } -func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) { +func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, map[string]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 + return make([]string, 0), nil, nil } } diff --git a/cmd/cloudflared/linux_service.go b/cmd/cloudflared/linux_service.go index cc379cdb..a0c2cf48 100644 --- a/cmd/cloudflared/linux_service.go +++ b/cmd/cloudflared/linux_service.go @@ -43,6 +43,7 @@ const ( serviceConfigFile = "config.yml" serviceCredentialFile = "cert.pem" serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile + serviceEnvFile = "/etc/default/cloudflared" cloudflaredService = "cloudflared.service" ) @@ -56,6 +57,8 @@ After=network.target [Service] TimeoutStartSec=0 Type=notify +{{- if ne .EnvFile "" }} +EnvironmentFile=-{{ .EnvFile }}{{ end }} ExecStart={{ .Path }} --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }} Restart=on-failure RestartSec=5s @@ -196,19 +199,23 @@ func installLinuxService(c *cli.Context) error { Path: etPath, } - var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error) + var extraArgsFunc buildArgsFunc if c.NArg() == 0 { extraArgsFunc = buildArgsForConfig } else { extraArgsFunc = buildArgsForToken } - extraArgs, err := extraArgsFunc(c, log) + extraArgs, extraEnv, err := extraArgsFunc(c, log) if err != nil { return err } templateArgs.ExtraArgs = extraArgs + templateArgs.Env = extraEnv + if len(extraEnv) > 0 { + templateArgs.EnvFile = serviceEnvFile + } switch { case isSystemd(): @@ -225,14 +232,14 @@ func installLinuxService(c *cli.Context) error { return err } -func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) { +func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, map[string]string, error) { if err := ensureConfigDirExists(serviceConfigDir); err != nil { - return nil, err + return nil, nil, err } src, _, err := config.ReadConfigFile(c, log) if err != nil { - return nil, err + return nil, nil, err } // can't use context because this command doesn't define "credentials-file" flag @@ -241,24 +248,24 @@ func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) { 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: + return nil, 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) + return nil, 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 nil, nil, fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err) } } return []string{ "--config", "/etc/cloudflared/config.yml", "tunnel", "run", - }, nil + }, nil, nil } func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error { diff --git a/cmd/cloudflared/service_template.go b/cmd/cloudflared/service_template.go index feaf86cc..6f0e6ef7 100644 --- a/cmd/cloudflared/service_template.go +++ b/cmd/cloudflared/service_template.go @@ -25,14 +25,48 @@ type ServiceTemplate struct { type ServiceTemplateArgs struct { Path string ExtraArgs []string + EnvFile string + Env map[string]string +} + +func resolvePath(s string) (string, error) { + resolvedPath, err := homedir.Expand(s) + if err != nil { + return "", fmt.Errorf("error resolving path %s: %v", s, err) + } + return resolvedPath, nil } func (st *ServiceTemplate) ResolvePath() (string, error) { - resolvedPath, err := homedir.Expand(st.Path) - if err != nil { - return "", fmt.Errorf("error resolving path %s: %v", st.Path, err) + return resolvePath(st.Path) +} + +func (st *ServiceTemplate) GenerateEnvFile(args *ServiceTemplateArgs) error { + if args.EnvFile == "" { + return nil } - return resolvedPath, nil + filePath, err := resolvePath(args.EnvFile) + if err != nil { + return err + } + if _, err = os.Stat(filePath); err == nil { + return fmt.Errorf("%s", serviceAlreadyExistsWarn(filePath)) + } + fileMode := os.FileMode(0o640) + if st.FileMode != 0 { + fileMode &= st.FileMode + } + buf := new(bytes.Buffer) + for k, v := range args.Env { + if _, err = fmt.Fprintf(buf, "%s=%s\n", k, v); err != nil { + return fmt.Errorf("error writing internal buffer: %v", err) + } + } + if err = os.WriteFile(filePath, buf.Bytes(), fileMode); err != nil { + _ = os.Remove(filePath) + return fmt.Errorf("error writing %s: %v", filePath, err) + } + return nil } func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error { @@ -45,7 +79,11 @@ func (st *ServiceTemplate) Generate(args *ServiceTemplateArgs) error { return err } if _, err = os.Stat(resolvedPath); err == nil { - return fmt.Errorf(serviceAlreadyExistsWarn(resolvedPath)) + return fmt.Errorf("%s", serviceAlreadyExistsWarn(resolvedPath)) + } + + if err = st.GenerateEnvFile(args); err != nil { + return err } var buffer bytes.Buffer