TUN-3131: Allow user to specify tunnel credentials path, and remove it in tunnel delete command

This commit is contained in:
cthuang 2020-07-02 17:31:12 +08:00
parent 1ed9e0fceb
commit 87e06100df
2 changed files with 75 additions and 13 deletions

View File

@ -12,18 +12,24 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/urfave/cli.v2" "gopkg.in/urfave/cli.v2"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/cloudflare/cloudflared/certutil" "github.com/cloudflare/cloudflared/certutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil" "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
"github.com/cloudflare/cloudflared/cmd/cloudflared/config"
"github.com/cloudflare/cloudflared/logger" "github.com/cloudflare/cloudflared/logger"
"github.com/cloudflare/cloudflared/origin" "github.com/cloudflare/cloudflared/origin"
"github.com/cloudflare/cloudflared/tunnelrpc/pogs" "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
"github.com/cloudflare/cloudflared/tunnelstore" "github.com/cloudflare/cloudflared/tunnelstore"
) )
const (
credFileFlagAlias = "cred-file"
)
var ( var (
showDeletedFlag = &cli.BoolFlag{ showDeletedFlag = &cli.BoolFlag{
Name: "show-deleted", Name: "show-deleted",
@ -43,6 +49,11 @@ var (
"overwrite the previous tunnel. If you want to use a single hostname with multiple " + "overwrite the previous tunnel. If you want to use a single hostname with multiple " +
"tunnels, you can do so with Cloudflare's Load Balancer product.", "tunnels, you can do so with Cloudflare's Load Balancer product.",
} }
credentialsFileFlag = &cli.StringFlag{
Name: "credentials-file",
Aliases: []string{credFileFlagAlias},
Usage: "File path of tunnel credentials",
}
) )
const hideSubcommands = true const hideSubcommands = true
@ -118,13 +129,15 @@ func createTunnel(c *cli.Context) error {
return nil return nil
} }
func tunnelFilePath(tunnelID, originCertPath string) (string, error) { func tunnelFilePath(tunnelID, directory string) (string, error) {
fileName := fmt.Sprintf("%v.json", tunnelID) fileName := fmt.Sprintf("%v.json", tunnelID)
return filepath.Clean(fmt.Sprintf("%v/../%v", originCertPath, fileName)), nil filePath := filepath.Clean(fmt.Sprintf("%s/%s", directory, fileName))
return homedir.Expand(filePath)
} }
func writeTunnelCredentials(tunnelID, accountID, originCertPath string, tunnelSecret []byte, logger logger.Service) error { func writeTunnelCredentials(tunnelID, accountID, originCertPath string, tunnelSecret []byte, logger logger.Service) error {
filePath, err := tunnelFilePath(tunnelID, originCertPath) originCertDir := filepath.Dir(originCertPath)
filePath, err := tunnelFilePath(tunnelID, originCertDir)
if err != nil { if err != nil {
return err return err
} }
@ -140,8 +153,8 @@ func writeTunnelCredentials(tunnelID, accountID, originCertPath string, tunnelSe
return ioutil.WriteFile(filePath, body, 400) return ioutil.WriteFile(filePath, body, 400)
} }
func readTunnelCredentials(tunnelID, originCertPath string) (*pogs.TunnelAuth, error) { func readTunnelCredentials(c *cli.Context, tunnelID string, logger logger.Service) (*pogs.TunnelAuth, error) {
filePath, err := tunnelFilePath(tunnelID, originCertPath) filePath, err := tunnelCredentialsPath(c, tunnelID, logger)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -157,6 +170,42 @@ func readTunnelCredentials(tunnelID, originCertPath string) (*pogs.TunnelAuth, e
return &auth, nil return &auth, nil
} }
func tunnelCredentialsPath(c *cli.Context, tunnelID string, logger logger.Service) (string, error) {
if filePath := c.String("credentials-file"); filePath != "" {
if validFilePath(filePath) {
return filePath, nil
}
}
// Fallback to look for tunnel credentials in the origin cert directory
if originCertPath, err := findOriginCert(c, logger); err == nil {
originCertDir := filepath.Dir(originCertPath)
if filePath, err := tunnelFilePath(tunnelID, originCertDir); err == nil {
if validFilePath(filePath) {
return filePath, nil
}
}
}
// Last resort look under default config directories
for _, configDir := range config.DefaultConfigDirs {
if filePath, err := tunnelFilePath(tunnelID, configDir); err == nil {
if validFilePath(filePath) {
return filePath, nil
}
}
}
return "", fmt.Errorf("Tunnel credentials file not found")
}
func validFilePath(path string) bool {
fileStat, err := os.Stat(path)
if err != nil {
return false
}
return !fileStat.IsDir()
}
func buildListCommand() *cli.Command { func buildListCommand() *cli.Command {
return &cli.Command{ return &cli.Command{
Name: "list", Name: "list",
@ -246,6 +295,7 @@ func buildDeleteCommand() *cli.Command {
Usage: "Delete existing tunnel with given ID", Usage: "Delete existing tunnel with given ID",
ArgsUsage: "TUNNEL-ID", ArgsUsage: "TUNNEL-ID",
Hidden: hideSubcommands, Hidden: hideSubcommands,
Flags: []cli.Flag{credentialsFileFlag},
} }
} }
@ -274,6 +324,16 @@ func deleteTunnel(c *cli.Context) error {
return errors.Wrapf(err, "Error deleting tunnel %s", id) return errors.Wrapf(err, "Error deleting tunnel %s", id)
} }
tunnelCredentialsPath, err := tunnelCredentialsPath(c, id, logger)
if err != nil {
logger.Infof("Cannot locate tunnel credentials to delete, error: %v. Please delete the file manually", err)
return nil
}
if err = os.Remove(tunnelCredentialsPath); err != nil {
logger.Infof("Cannot delete tunnel credentials, error: %v. Please delete the file manually", err)
}
return nil return nil
} }
@ -320,7 +380,7 @@ func buildRunCommand() *cli.Command {
Usage: "Proxy a local web server by running the given tunnel", Usage: "Proxy a local web server by running the given tunnel",
ArgsUsage: "TUNNEL-ID", ArgsUsage: "TUNNEL-ID",
Hidden: hideSubcommands, Hidden: hideSubcommands,
Flags: []cli.Flag{forceFlag}, Flags: []cli.Flag{forceFlag, credentialsFileFlag},
} }
} }
@ -339,11 +399,7 @@ func runTunnel(c *cli.Context) error {
return errors.Wrap(err, "error setting up logger") return errors.Wrap(err, "error setting up logger")
} }
originCertPath, err := findOriginCert(c, logger) credentials, err := readTunnelCredentials(c, id, logger)
if err != nil {
return errors.Wrap(err, "Error locating origin cert")
}
credentials, err := readTunnelCredentials(id, originCertPath)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,9 +1,12 @@
package tunnel package tunnel
import ( import (
"fmt"
"path/filepath"
"testing" "testing"
"github.com/cloudflare/cloudflared/tunnelstore" "github.com/cloudflare/cloudflared/tunnelstore"
"github.com/mitchellh/go-homedir"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -72,8 +75,11 @@ func Test_fmtConnections(t *testing.T) {
} }
func TestTunnelfilePath(t *testing.T) { func TestTunnelfilePath(t *testing.T) {
actual, err := tunnelFilePath("tunnel", "~/.cloudflared/cert.pem") originCertDir := filepath.Dir("~/.cloudflared/cert.pem")
actual, err := tunnelFilePath("tunnel", originCertDir)
assert.NoError(t, err) assert.NoError(t, err)
expected := "~/.cloudflared/tunnel.json" homeDir, err := homedir.Dir()
assert.NoError(t, err)
expected := fmt.Sprintf("%s/.cloudflared/tunnel.json", homeDir)
assert.Equal(t, expected, actual) assert.Equal(t, expected, actual)
} }