Skip to content

Commit b6d7076

Browse files
committed
TUN-5681: Add support for running tunnel using Token
1 parent 22cd8ce commit b6d7076

File tree

6 files changed

+101
-16
lines changed

6 files changed

+101
-16
lines changed

cfapi/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
)
66

77
type TunnelClient interface {
8-
CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error)
8+
CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error)
99
GetTunnel(tunnelID uuid.UUID) (*Tunnel, error)
1010
DeleteTunnel(tunnelID uuid.UUID) error
1111
ListTunnels(filter *TunnelFilter) ([]*Tunnel, error)

cfapi/tunnel.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ type Tunnel struct {
2323
Connections []Connection `json:"connections"`
2424
}
2525

26+
type TunnelWithToken struct {
27+
Tunnel
28+
Token string `json:"token"`
29+
}
30+
2631
type Connection struct {
2732
ColoName string `json:"colo_name"`
2833
ID uuid.UUID `json:"id"`
@@ -63,7 +68,7 @@ func (cp CleanupParams) encode() string {
6368
return cp.queryParams.Encode()
6469
}
6570

66-
func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, error) {
71+
func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error) {
6772
if name == "" {
6873
return nil, errors.New("tunnel name required")
6974
}
@@ -83,7 +88,11 @@ func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*Tunnel, er
8388

8489
switch resp.StatusCode {
8590
case http.StatusOK:
86-
return unmarshalTunnel(resp.Body)
91+
var tunnel TunnelWithToken
92+
if serdeErr := parseResponse(resp.Body, &tunnel); err != nil {
93+
return nil, serdeErr
94+
}
95+
return &tunnel, nil
8796
case http.StatusConflict:
8897
return nil, ErrTunnelNameConflict
8998
}

cmd/cloudflared/tunnel/subcommand_context.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ func (sc *subcommandContext) create(name string, credentialsFilePath string, sec
220220
}
221221
fmt.Println(" Keep this file secret. To revoke these credentials, delete the tunnel.")
222222
fmt.Printf("\nCreated tunnel %s with id %s\n", tunnel.Name, tunnel.ID)
223-
return tunnel, nil
223+
fmt.Printf("\nTunnel Token: %s\n", tunnel.Token)
224+
225+
return &tunnel.Tunnel, nil
224226
}
225227

226228
func (sc *subcommandContext) list(filter *cfapi.TunnelFilter) ([]*cfapi.Tunnel, error) {
@@ -300,6 +302,12 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error {
300302
return err
301303
}
302304

305+
return sc.runWithCredentials(credentials)
306+
}
307+
308+
func (sc *subcommandContext) runWithCredentials(credentials connection.Credentials) error {
309+
sc.log.Info().Str(LogFieldTunnelID, credentials.TunnelID.String()).Msg("Starting tunnel")
310+
303311
return StartServer(
304312
sc.c,
305313
buildInfo,

cmd/cloudflared/tunnel/subcommands.go

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package tunnel
22

33
import (
44
"crypto/rand"
5+
"encoding/base64"
56
"encoding/json"
67
"fmt"
78
"io/ioutil"
@@ -34,6 +35,7 @@ const (
3435
CredFileFlagAlias = "cred-file"
3536
CredFileFlag = "credentials-file"
3637
CredContentsFlag = "credentials-contents"
38+
TunnelTokenFlag = "token"
3739
overwriteDNSFlagName = "overwrite-dns"
3840

3941
LogFieldTunnelID = "tunnelID"
@@ -118,6 +120,11 @@ var (
118120
Usage: "Contents of the tunnel credentials JSON file to use. When provided along with credentials-file, this will take precedence.",
119121
EnvVars: []string{"TUNNEL_CRED_CONTENTS"},
120122
})
123+
tunnelTokenFlag = altsrc.NewStringFlag(&cli.StringFlag{
124+
Name: TunnelTokenFlag,
125+
Usage: "The Tunnel token. When provided along with credentials, this will take precedence.",
126+
EnvVars: []string{"TUNNEL_TOKEN"},
127+
})
121128
forceDeleteFlag = &cli.BoolFlag{
122129
Name: "force",
123130
Aliases: []string{"f"},
@@ -597,6 +604,7 @@ func buildRunCommand() *cli.Command {
597604
credentialsContentsFlag,
598605
selectProtocolFlag,
599606
featuresFlag,
607+
tunnelTokenFlag,
600608
}
601609
flags = append(flags, configureProxyFlags(false)...)
602610
return &cli.Command{
@@ -627,32 +635,52 @@ func runCommand(c *cli.Context) error {
627635
if c.NArg() > 1 {
628636
return cliutil.UsageError(`"cloudflared tunnel run" accepts only one argument, the ID or name of the tunnel to run.`)
629637
}
630-
tunnelRef := c.Args().First()
631-
if tunnelRef == "" {
632-
// see if tunnel id was in the config file
633-
tunnelRef = config.GetConfiguration().TunnelID
634-
if tunnelRef == "" {
635-
return cliutil.UsageError(`"cloudflared tunnel run" requires the ID or name of the tunnel to run as the last command line argument or in the configuration file.`)
636-
}
637-
}
638638

639639
if c.String("hostname") != "" {
640640
sc.log.Warn().Msg("The property `hostname` in your configuration is ignored because you configured a Named Tunnel " +
641641
"in the property `tunnel` to run. Make sure to provision the routing (e.g. via `cloudflared tunnel route dns/lb`) or else " +
642642
"your origin will not be reachable. You should remove the `hostname` property to avoid this warning.")
643643
}
644644

645-
return runNamedTunnel(sc, tunnelRef)
645+
// Check if token is provided and if not use default tunnelID flag method
646+
if tokenStr := c.String(TunnelTokenFlag); tokenStr != "" {
647+
if token, err := parseToken(tokenStr); err == nil {
648+
return sc.runWithCredentials(token.Credentials())
649+
}
650+
651+
return cliutil.UsageError("Provided Tunnel token is not valid.")
652+
} else {
653+
tunnelRef := c.Args().First()
654+
if tunnelRef == "" {
655+
// see if tunnel id was in the config file
656+
tunnelRef = config.GetConfiguration().TunnelID
657+
if tunnelRef == "" {
658+
return cliutil.UsageError(`"cloudflared tunnel run" requires the ID or name of the tunnel to run as the last command line argument or in the configuration file.`)
659+
}
660+
}
661+
662+
return runNamedTunnel(sc, tunnelRef)
663+
}
664+
}
665+
666+
func parseToken(tokenStr string) (*connection.TunnelToken, error) {
667+
content, err := base64.StdEncoding.DecodeString(tokenStr)
668+
if err != nil {
669+
return nil, err
670+
}
671+
672+
var token connection.TunnelToken
673+
if err := json.Unmarshal(content, &token); err != nil {
674+
return nil, err
675+
}
676+
return &token, nil
646677
}
647678

648679
func runNamedTunnel(sc *subcommandContext, tunnelRef string) error {
649680
tunnelID, err := sc.findID(tunnelRef)
650681
if err != nil {
651682
return errors.Wrap(err, "error parsing tunnel ID")
652683
}
653-
654-
sc.log.Info().Str(LogFieldTunnelID, tunnelID.String()).Msg("Starting tunnel")
655-
656684
return sc.run(tunnelID)
657685
}
658686

cmd/cloudflared/tunnel/subcommands_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
package tunnel
22

33
import (
4+
"encoding/base64"
5+
"encoding/json"
46
"path/filepath"
57
"testing"
68

79
"github.com/google/uuid"
810
homedir "github.com/mitchellh/go-homedir"
911
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
1013

1114
"github.com/cloudflare/cloudflared/cfapi"
15+
"github.com/cloudflare/cloudflared/connection"
1216
)
1317

1418
func Test_fmtConnections(t *testing.T) {
@@ -177,3 +181,24 @@ func Test_validateHostname(t *testing.T) {
177181
})
178182
}
179183
}
184+
185+
func Test_TunnelToken(t *testing.T) {
186+
token, err := parseToken("aabc")
187+
require.Error(t, err)
188+
require.Nil(t, token)
189+
190+
expectedToken := &connection.TunnelToken{
191+
AccountTag: "abc",
192+
TunnelSecret: []byte("secret"),
193+
TunnelID: uuid.New(),
194+
}
195+
196+
tokenJsonStr, err := json.Marshal(expectedToken)
197+
require.NoError(t, err)
198+
199+
token64 := base64.StdEncoding.EncodeToString(tokenJsonStr)
200+
201+
token, err = parseToken(token64)
202+
require.NoError(t, err)
203+
require.Equal(t, token, expectedToken)
204+
}

connection/connection.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ func (c *Credentials) Auth() pogs.TunnelAuth {
5050
}
5151
}
5252

53+
// TunnelToken are Credentials but encoded with custom fields namings.
54+
type TunnelToken struct {
55+
AccountTag string `json:"a"`
56+
TunnelSecret []byte `json:"s"`
57+
TunnelID uuid.UUID `json:"t"`
58+
}
59+
60+
func (t TunnelToken) Credentials() Credentials {
61+
return Credentials{
62+
AccountTag: t.AccountTag,
63+
TunnelSecret: t.TunnelSecret,
64+
TunnelID: t.TunnelID,
65+
}
66+
}
67+
5368
type ClassicTunnelProperties struct {
5469
Hostname string
5570
OriginCert []byte

0 commit comments

Comments
 (0)