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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
@@ -8,6 +12,44 @@ import (
|
|||||||
"github.com/cloudflare/cloudflared/cmd/cloudflared/tunnel"
|
"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) {
|
func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||||
token := c.Args().First()
|
token := c.Args().First()
|
||||||
if _, err := tunnel.ParseToken(token); err != nil {
|
if _, err := tunnel.ParseToken(token); err != nil {
|
||||||
@@ -19,6 +61,7 @@ func buildArgsForToken(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
|||||||
}, nil
|
}, 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) {
|
func getServiceExtraArgsFromCliArgs(c *cli.Context, log *zerolog.Logger) ([]string, error) {
|
||||||
if c.NArg() > 0 {
|
if c.NArg() > 0 {
|
||||||
// currently, we only support extra args for token
|
// currently, we only support extra args for token
|
||||||
|
|||||||
@@ -24,8 +24,21 @@ func runApp(app *cli.App, _ chan struct{}) {
|
|||||||
Usage: "Manages the cloudflared system service",
|
Usage: "Manages the cloudflared system service",
|
||||||
Subcommands: []*cli.Command{
|
Subcommands: []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "install",
|
Name: "install",
|
||||||
Usage: "Install cloudflared as a system service",
|
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),
|
Action: cliutil.ConfiguredAction(installLinuxService),
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
noUpdateServiceFlag,
|
noUpdateServiceFlag,
|
||||||
@@ -48,6 +61,7 @@ const (
|
|||||||
serviceConfigFile = "config.yml"
|
serviceConfigFile = "config.yml"
|
||||||
serviceCredentialFile = "cert.pem"
|
serviceCredentialFile = "cert.pem"
|
||||||
serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile
|
serviceConfigPath = serviceConfigDir + "/" + serviceConfigFile
|
||||||
|
tokenPath = serviceConfigDir + "/" + defaultTokenFile
|
||||||
cloudflaredService = "cloudflared.service"
|
cloudflaredService = "cloudflared.service"
|
||||||
cloudflaredUpdateService = "cloudflared-update.service"
|
cloudflaredUpdateService = "cloudflared-update.service"
|
||||||
cloudflaredUpdateTimer = "cloudflared-update.timer"
|
cloudflaredUpdateTimer = "cloudflared-update.timer"
|
||||||
@@ -246,23 +260,45 @@ func installLinuxService(c *cli.Context) error {
|
|||||||
Path: etPath,
|
Path: etPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the "no update flag" is set
|
// Both installation methods below need the config directory to be present,
|
||||||
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
// either to hold the token file, or the configuration yaml
|
||||||
|
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
|
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
|
templateArgs.ExtraArgs = extraArgs
|
||||||
|
|
||||||
|
// Check if the "no update flag" is set
|
||||||
|
autoUpdate := !c.IsSet(noUpdateServiceFlag.Name)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case inits.IsSystemd():
|
case inits.IsSystemd():
|
||||||
log.Info().Msgf("Using Systemd")
|
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) {
|
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)
|
src, _, err := config.ReadConfigFile(c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -426,6 +458,9 @@ func uninstallLinuxService(c *cli.Context) error {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
log.Info().Msg("Linux service for cloudflared uninstalled successfully")
|
log.Info().Msg("Linux service for cloudflared uninstalled successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeTokenFile(tokenPath, log)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user