Skip to content

Commit 7065233

Browse files
committed
TUN-5679: Add support for service install using Tunnel Token
1 parent c2a32de commit 7065233

File tree

6 files changed

+101
-79
lines changed

6 files changed

+101
-79
lines changed

cmd/cloudflared/common_service.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"github.com/rs/zerolog"
5+
"github.com/urfave/cli/v2"
6+
7+
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
8+
"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
9+
)
10+
11+
func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
12+
token := c.Args().First()
13+
if _, err := tunnel.ParseToken(token); err != nil {
14+
return nil, cliutil.UsageError("Provided tunnel token is not valid (%s).", err)
15+
}
16+
17+
return []string{
18+
"tunnel", "run", "--token", token,
19+
}, nil
20+
}
21+
22+
func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) {
23+
if c.NArg() > 0 {
24+
// currently, we only support extra args for token
25+
return buildArgsForToken(c, log)
26+
} else {
27+
// empty extra args
28+
return make([]string, 0), nil
29+
}
30+
}

cmd/cloudflared/linux_service.go

Lines changed: 48 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package main
66
import (
77
"fmt"
88
"os"
9-
"path/filepath"
109

1110
"github.com/rs/zerolog"
1211
"github.com/urfave/cli/v2"
@@ -26,12 +25,6 @@ func runApp(app *cli.App, graceShutdownC chan struct{}) {
2625
Name: "install",
2726
Usage: "Install Cloudflare Tunnel as a system service",
2827
Action: cliutil.ConfiguredAction(installLinuxService),
29-
Flags: []cli.Flag{
30-
&cli.BoolFlag{
31-
Name: "legacy",
32-
Usage: "Generate service file for non-named tunnels",
33-
},
34-
},
3528
},
3629
{
3730
Name: "uninstall",
@@ -62,7 +55,7 @@ After=network.target
6255
[Service]
6356
TimeoutStartSec=0
6457
Type=notify
65-
ExecStart={{ .Path }} --config /etc/cloudflared/config.yml --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }}
58+
ExecStart={{ .Path }} --no-autoupdate{{ range .ExtraArgs }} {{ . }}{{ end }}
6659
Restart=on-failure
6760
RestartSec=5s
6861
@@ -112,7 +105,7 @@ var sysvTemplate = ServiceTemplate{
112105
# Description: Cloudflare Tunnel agent
113106
### END INIT INFO
114107
name=$(basename $(readlink -f $0))
115-
cmd="{{.Path}} --config /etc/cloudflared/config.yml --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}"
108+
cmd="{{.Path}} --pidfile /var/run/$name.pid --autoupdate-freq 24h0m0s{{ range .ExtraArgs }} {{ . }}{{ end }}"
116109
pid_file="/var/run/$name.pid"
117110
stdout_log="/var/log/$name.log"
118111
stderr_log="/var/log/$name.err"
@@ -191,27 +184,6 @@ func isSystemd() bool {
191184
return false
192185
}
193186

194-
func copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile string, log *zerolog.Logger) error {
195-
srcCredentialPath := filepath.Join(userConfigDir, userCredentialFile)
196-
destCredentialPath := filepath.Join(serviceConfigDir, serviceCredentialFile)
197-
if srcCredentialPath != destCredentialPath {
198-
if err := copyCredential(srcCredentialPath, destCredentialPath); err != nil {
199-
return err
200-
}
201-
}
202-
203-
srcConfigPath := filepath.Join(userConfigDir, userConfigFile)
204-
destConfigPath := filepath.Join(serviceConfigDir, serviceConfigFile)
205-
if srcConfigPath != destConfigPath {
206-
if err := copyConfig(srcConfigPath, destConfigPath); err != nil {
207-
return err
208-
}
209-
log.Info().Msgf("Copied %s to %s", srcConfigPath, destConfigPath)
210-
}
211-
212-
return nil
213-
}
214-
215187
func installLinuxService(c *cli.Context) error {
216188
log := logger.CreateLoggerFromContext(c, logger.EnableTerminalLog)
217189

@@ -223,53 +195,20 @@ func installLinuxService(c *cli.Context) error {
223195
Path: etPath,
224196
}
225197

226-
if err := ensureConfigDirExists(serviceConfigDir); err != nil {
227-
return err
228-
}
229-
if c.Bool("legacy") {
230-
userConfigDir := filepath.Dir(c.String("config"))
231-
userConfigFile := filepath.Base(c.String("config"))
232-
userCredentialFile := config.DefaultCredentialFile
233-
if err = copyUserConfiguration(userConfigDir, userConfigFile, userCredentialFile, log); err != nil {
234-
log.Err(err).Msgf("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
235-
serviceConfigDir, serviceCredentialFile, serviceConfigFile)
236-
return err
237-
}
238-
templateArgs.ExtraArgs = []string{
239-
"--origincert", serviceConfigDir + "/" + serviceCredentialFile,
240-
}
198+
var extraArgsFunc func(c *cli.Context, log *zerolog.Logger) ([]string, error)
199+
if c.NArg() == 0 {
200+
extraArgsFunc = buildArgsForConfig
241201
} else {
242-
src, _, err := config.ReadConfigFile(c, log)
243-
if err != nil {
244-
return err
245-
}
246-
247-
// can't use context because this command doesn't define "credentials-file" flag
248-
configPresent := func(s string) bool {
249-
val, err := src.String(s)
250-
return err == nil && val != ""
251-
}
252-
if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) {
253-
return fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials:
254-
tunnel: TUNNEL-UUID
255-
credentials-file: CREDENTIALS-FILE
256-
`, src.Source())
257-
}
258-
if src.Source() != serviceConfigPath {
259-
if exists, err := config.FileExists(serviceConfigPath); err != nil || exists {
260-
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)
261-
}
262-
263-
if err := copyFile(src.Source(), serviceConfigPath); err != nil {
264-
return fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err)
265-
}
266-
}
202+
extraArgsFunc = buildArgsForToken
203+
}
267204

268-
templateArgs.ExtraArgs = []string{
269-
"tunnel", "run",
270-
}
205+
extraArgs, err := extraArgsFunc(c, log)
206+
if err != nil {
207+
return err
271208
}
272209

210+
templateArgs.ExtraArgs = extraArgs
211+
273212
switch {
274213
case isSystemd():
275214
log.Info().Msgf("Using Systemd")
@@ -280,6 +219,42 @@ credentials-file: CREDENTIALS-FILE
280219
}
281220
}
282221

222+
func buildArgsForConfig(c *cli.Context, log *zerolog.Logger) ([]string, error) {
223+
if err := ensureConfigDirExists(serviceConfigDir); err != nil {
224+
return nil, err
225+
}
226+
227+
src, _, err := config.ReadConfigFile(c, log)
228+
if err != nil {
229+
return nil, err
230+
}
231+
232+
// can't use context because this command doesn't define "credentials-file" flag
233+
configPresent := func(s string) bool {
234+
val, err := src.String(s)
235+
return err == nil && val != ""
236+
}
237+
if src.TunnelID == "" || !configPresent(tunnel.CredFileFlag) {
238+
return nil, fmt.Errorf(`Configuration file %s must contain entries for the tunnel to run and its associated credentials:
239+
tunnel: TUNNEL-UUID
240+
credentials-file: CREDENTIALS-FILE
241+
`, src.Source())
242+
}
243+
if src.Source() != serviceConfigPath {
244+
if exists, err := config.FileExists(serviceConfigPath); err != nil || exists {
245+
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)
246+
}
247+
248+
if err := copyFile(src.Source(), serviceConfigPath); err != nil {
249+
return nil, fmt.Errorf("failed to copy %s to %s: %w", src.Source(), serviceConfigPath, err)
250+
}
251+
}
252+
253+
return []string{
254+
"--config", "/etc/cloudflared/config.yml", "tunnel", "run",
255+
}, nil
256+
}
257+
283258
func installSystemd(templateArgs *ServiceTemplateArgs, log *zerolog.Logger) error {
284259
for _, serviceTemplate := range systemdTemplates {
285260
err := serviceTemplate.Generate(templateArgs)

cmd/cloudflared/macos_service.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ func newLaunchdTemplate(installPath, stdoutPath, stderrPath string) *ServiceTemp
5050
<key>ProgramArguments</key>
5151
<array>
5252
<string>{{ .Path }}</string>
53+
{{- range $i, $item := .ExtraArgs}}
54+
<string>{{ $item }}</string>
55+
{{- end}}
5356
</array>
5457
<key>RunAtLoad</key>
5558
<true/>
@@ -129,6 +132,13 @@ func installLaunchd(c *cli.Context) error {
129132
log.Err(err).Msg("Error determining install path")
130133
return errors.Wrap(err, "Error determining install path")
131134
}
135+
extraArgs, err := getServiceExtraArgsFromCliArgs(c, log)
136+
if err != nil {
137+
errMsg := "Unable to determine extra arguments for launch daemon"
138+
log.Err(err).Msg(errMsg)
139+
return errors.Wrap(err, errMsg)
140+
}
141+
132142
stdoutPath, err := stdoutPath()
133143
if err != nil {
134144
log.Err(err).Msg("error determining stdout path")
@@ -140,7 +150,7 @@ func installLaunchd(c *cli.Context) error {
140150
return errors.Wrap(err, "error determining stderr path")
141151
}
142152
launchdTemplate := newLaunchdTemplate(installPath, stdoutPath, stderrPath)
143-
templateArgs := ServiceTemplateArgs{Path: etPath}
153+
templateArgs := ServiceTemplateArgs{Path: etPath, ExtraArgs: extraArgs}
144154
err = launchdTemplate.Generate(&templateArgs)
145155
if err != nil {
146156
log.Err(err).Msg("error generating launchd template")

cmd/cloudflared/tunnel/subcommands.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ func runCommand(c *cli.Context) error {
644644

645645
// Check if token is provided and if not use default tunnelID flag method
646646
if tokenStr := c.String(TunnelTokenFlag); tokenStr != "" {
647-
if token, err := parseToken(tokenStr); err == nil {
647+
if token, err := ParseToken(tokenStr); err == nil {
648648
return sc.runWithCredentials(token.Credentials())
649649
}
650650

@@ -663,7 +663,7 @@ func runCommand(c *cli.Context) error {
663663
}
664664
}
665665

666-
func parseToken(tokenStr string) (*connection.TunnelToken, error) {
666+
func ParseToken(tokenStr string) (*connection.TunnelToken, error) {
667667
content, err := base64.StdEncoding.DecodeString(tokenStr)
668668
if err != nil {
669669
return nil, err

cmd/cloudflared/tunnel/subcommands_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func Test_validateHostname(t *testing.T) {
183183
}
184184

185185
func Test_TunnelToken(t *testing.T) {
186-
token, err := parseToken("aabc")
186+
token, err := ParseToken("aabc")
187187
require.Error(t, err)
188188
require.Nil(t, token)
189189

@@ -198,7 +198,7 @@ func Test_TunnelToken(t *testing.T) {
198198

199199
token64 := base64.StdEncoding.EncodeToString(tokenJsonStr)
200200

201-
token, err = parseToken(token64)
201+
token, err = ParseToken(token64)
202202
require.NoError(t, err)
203203
require.Equal(t, token, expectedToken)
204204
}

cmd/cloudflared/windows_service.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,15 @@ func installWindowsService(c *cli.Context) error {
193193
s.Close()
194194
return fmt.Errorf("Service %s already exists", windowsServiceName)
195195
}
196+
extraArgs, err := getServiceExtraArgsFromCliArgs(c, &log)
197+
if err != nil {
198+
errMsg := "Unable to determine extra arguments for windows service"
199+
log.Err(err).Msg(errMsg)
200+
return errors.Wrap(err, errMsg)
201+
}
202+
196203
config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription}
197-
s, err = m.CreateService(windowsServiceName, exepath, config)
204+
s, err = m.CreateService(windowsServiceName, exepath, config, extraArgs...)
198205
if err != nil {
199206
return errors.Wrap(err, "Cannot install service")
200207
}

0 commit comments

Comments
 (0)