diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index d5b42590..4450cf3c 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -261,8 +261,16 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error { // and add the TunnelID into any old credentials (generated before TUN-3581 added the `TunnelID` // field to credentials files) func (sc *subcommandContext) findCredentials(tunnelID uuid.UUID) (connection.Credentials, error) { - credFinder := sc.credentialFinder(tunnelID) - credentials, err := sc.readTunnelCredentials(credFinder) + var credentials connection.Credentials + var err error + if credentialsContents := sc.c.String(CredContentsFlag); credentialsContents != "" { + if err = json.Unmarshal([]byte(credentialsContents), &credentials); err != nil { + err = errInvalidJSONCredential{path: "TUNNEL_CRED_CONTENTS", err: err} + } + } else { + credFinder := sc.credentialFinder(tunnelID) + credentials, err = sc.readTunnelCredentials(credFinder) + } // This line ensures backwards compatibility with credentials files generated before // TUN-3581. Those old credentials files don't have a TunnelID field, so we enrich the struct // with the ID, which we have already resolved from the user input. diff --git a/cmd/cloudflared/tunnel/subcommand_context_test.go b/cmd/cloudflared/tunnel/subcommand_context_test.go index be57a081..2155b3dc 100644 --- a/cmd/cloudflared/tunnel/subcommand_context_test.go +++ b/cmd/cloudflared/tunnel/subcommand_context_test.go @@ -191,6 +191,51 @@ func Test_subcommandContext_findCredentials(t *testing.T) { TunnelName: name, }, }, + { + name: "TUNNEL_CRED_CONTENTS given contains old credentials contents", + fields: fields{ + log: &log, + fs: fs, + c: func() *cli.Context { + flagSet := flag.NewFlagSet("test0", flag.PanicOnError) + flagSet.String(CredContentsFlag, "", "") + c := cli.NewContext(cli.NewApp(), flagSet, nil) + _ = c.Set(CredContentsFlag, fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s"}`, accountTag, secretB64)) + return c + }(), + }, + args: args{ + tunnelID: tunnelID, + }, + want: connection.Credentials{ + AccountTag: accountTag, + TunnelID: tunnelID, + TunnelSecret: secret, + }, + }, + { + name: "TUNNEL_CRED_CONTENTS given contains new credentials contents", + fields: fields{ + log: &log, + fs: fs, + c: func() *cli.Context { + flagSet := flag.NewFlagSet("test0", flag.PanicOnError) + flagSet.String(CredContentsFlag, "", "") + c := cli.NewContext(cli.NewApp(), flagSet, nil) + _ = c.Set(CredContentsFlag, fmt.Sprintf(`{"AccountTag":"%s","TunnelSecret":"%s","TunnelID":"%s","TunnelName":"%s"}`, accountTag, secretB64, tunnelID, name)) + return c + }(), + }, + args: args{ + tunnelID: tunnelID, + }, + want: connection.Credentials{ + AccountTag: accountTag, + TunnelID: tunnelID, + TunnelSecret: secret, + TunnelName: name, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/cloudflared/tunnel/subcommands.go b/cmd/cloudflared/tunnel/subcommands.go index 377230af..6cfe3af2 100644 --- a/cmd/cloudflared/tunnel/subcommands.go +++ b/cmd/cloudflared/tunnel/subcommands.go @@ -33,6 +33,7 @@ const ( connsSortByOptions = "id, startedAt, numConnections, version" CredFileFlagAlias = "cred-file" CredFileFlag = "credentials-file" + CredContentsFlag = "credentials-contents" overwriteDNSFlagName = "overwrite-dns" LogFieldTunnelID = "tunnelID" @@ -111,8 +112,13 @@ var ( Usage: "Filepath at which to read/write the tunnel credentials", EnvVars: []string{"TUNNEL_CRED_FILE"}, } - credentialsFileFlag = altsrc.NewStringFlag(credentialsFileFlagCLIOnly) - forceDeleteFlag = &cli.BoolFlag{ + credentialsFileFlag = altsrc.NewStringFlag(credentialsFileFlagCLIOnly) + credentialsContentsFlag = altsrc.NewStringFlag(&cli.StringFlag{ + Name: CredContentsFlag, + Usage: "Contents of the tunnel credentials JSON file to use. When provided along with credentials-file, this will take precedence.", + EnvVars: []string{"TUNNEL_CRED_CONTENTS"}, + }) + forceDeleteFlag = &cli.BoolFlag{ Name: "force", Aliases: []string{"f"}, Usage: "Cleans up any stale connections before the tunnel is deleted. cloudflared will not " + @@ -579,6 +585,7 @@ func buildRunCommand() *cli.Command { flags := []cli.Flag{ forceFlag, credentialsFileFlag, + credentialsContentsFlag, selectProtocolFlag, featuresFlag, }