mirror of
https://github.com/cloudflare/cloudflared.git
synced 2026-08-07 07:14:57 +00:00
VULN-118896: Linux service: Use --token-file instead of --token
When installed with a token, e.g., $ cloudflared service install \<token\> cloudflared will set itself up to be run by the init system using the --token argument. This results in tokens being visible in the output of ps aux by unprivileged users. Change this such that this installation method instead puts the token in a file with mode 600 in /etc/cloudflared and uses the --token-file flag instead.
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
@@ -8,6 +12,44 @@ import (
|
||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTokenFile = "token"
|
||||
tokenPerms os.FileMode = 0o600
|
||||
)
|
||||
|
||||
func writeTokenToFile(path string, token string) error {
|
||||
if _, err := tunnel.ParseToken(token); err != nil {
|
||||
return cliutil.UsageError("Provided tunnel token is not valid (%s).", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(path, []byte(token), tokenPerms); err != nil {
|
||||
return fmt.Errorf("failed to write token to %s: %v", path, err)
|
||||
}
|
||||
|
||||
// If the token file already existed with unrestrictive perms, os.WriteFile
|
||||
// above will not update them
|
||||
if err := os.Chmod(path, tokenPerms); err != nil {
|
||||
return fmt.Errorf("failed to restrict permissions on token file %s: %v", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func removeTokenFile(tokenPath string, log *zerolog.Logger) {
|
||||
err := os.Remove(tokenPath)
|
||||
|
||||
if err != nil && !errors.Is(err, os.ErrNotExist) {
|
||||
log.Warn().Msgf("Could not remove service token file at %s: %v", tokenPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
func buildArgsForTokenFile(tokenPath string) []string {
|
||||
return []string{
|
||||
"tunnel", "run", "--token-file", tokenPath,
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:unused // This function is used by macos and Windows builds, the unused warning when building for Linux is spurious
|
||||
func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||
token := c.Args().First()
|
||||
if _, err := tunnel.ParseToken(token); err != nil {
|
||||
@@ -19,6 +61,7 @@ func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// nolint:unused // This function is used by macos and Windows builds, the unused warning when building for Linux is spurious
|
||||
func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||
if c.NArg() > 0 {
|
||||
// currently, we only support extra args for token
|
||||
|
||||
@@ -26,6 +26,19 @@ func runApp(app *cli.App, _ chan struct{}) {
|
||||
{
|
||||
Name: "install",
|
||||
Usage: "Install cloudflared as a system service",
|
||||
ArgsUsage: "[TOKEN]",
|
||||
Description: `
|
||||
Installs cloudflared as a service using the detected init system (e.g., sysv,
|
||||
systemd, openrc).
|
||||
|
||||
A token may optionally be provided. If a token is provided, it will be written
|
||||
to disk in the service configuration directory and the cloudflared service
|
||||
configured to use it via the --token-file argument.
|
||||
|
||||
If no token is provided, cloudflared will attempt to find a configuration file
|
||||
with tunnel credentials from a predetermined list of configuration directory
|
||||
paths. If found, it will use that configuration file and credentials (or error
|
||||
out if no configuration file with credentials was found).`,
|
||||
Action: cliutil.ConfiguredAction(installLinuxService),
|
||||
Flags: []cli.Flag{
|
||||
noUpdateServiceFlag,
|
||||
@@ -48,6 +61,7 @@ const (
|
||||
serviceConfigFile = "config.yml"
|
||||
serviceCredentialFile = "cert.pem"
|
||||
serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile
|
||||
tokenPath = serviceConfigDir + "/" + defaultTokenFile
|
||||
cloudflaredService = "cloudflared.service"
|
||||
cloudflaredUpdateService = "cloudflared-update.service"
|
||||
cloudflaredUpdateTimer = "cloudflared-update.timer"
|
||||
@@ -246,23 +260,45 @@ func installLinuxService(c *cli.Context) error {
|
||||
Path: etPath,
|
||||
}
|
||||
|
||||
// Check if the "no update flag" is set
|
||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||
|
||||
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 {
|
||||
// Both installation methods below need the config directory to be present,
|
||||
// either to hold the token file, or the configuration yaml
|
||||
if err := ensureConfigDirExists(serviceConfigDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var extraArgs []string
|
||||
if c.NArg() == 0 {
|
||||
// If passed no arguments e.g., "$ cloudflared service install",
|
||||
// install the service using the detected config file (or error-out if
|
||||
// no config exists).
|
||||
if extraArgs, err = buildArgsForConfig(c, log); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// If passed one argument e.g., "$ cloudflared service install <token>"
|
||||
// write the token to the config directory and install the service
|
||||
// using --token-file pointing to that file. This is the quick setup
|
||||
// the tunnel UI suggests.
|
||||
|
||||
// Ensure token file is removed if install fails
|
||||
defer func() {
|
||||
if err != nil {
|
||||
removeTokenFile(tokenPath, log)
|
||||
}
|
||||
}()
|
||||
|
||||
if err = writeTokenToFile(tokenPath, c.Args().First()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
extraArgs = buildArgsForTokenFile(tokenPath)
|
||||
}
|
||||
|
||||
templateArgs.ExtraArgs = extraArgs
|
||||
|
||||
// Check if the "no update flag" is set
|
||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||
|
||||
switch {
|
||||
case inits.IsSystemd():
|
||||
log.Info().Msgf("Using Systemd")
|
||||
@@ -282,10 +318,6 @@ func installLinuxService(c *cli.Context) error {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -426,6 +458,9 @@ func uninstallLinuxService(c *cli.Context) error {
|
||||
if err == nil {
|
||||
log.Info().Msg("Linux service for cloudflared uninstalled successfully")
|
||||
}
|
||||
|
||||
removeTokenFile(tokenPath, log)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user