@@ -3,20 +3,16 @@ package tunnel
33import (
44 "encoding/json"
55 "fmt"
6- "io/ioutil"
76 "os"
8- "path/filepath"
97 "strings"
108
119 "github.com/google/uuid"
1210 "github.com/pkg/errors"
1311 "github.com/urfave/cli/v2"
1412
1513 "github.com/cloudflare/cloudflared/certutil"
16- "github.com/cloudflare/cloudflared/cmd/cloudflared/config"
1714 "github.com/cloudflare/cloudflared/connection"
1815 "github.com/cloudflare/cloudflared/logger"
19- "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
2016 "github.com/cloudflare/cloudflared/tunnelstore"
2117)
2218
@@ -32,14 +28,14 @@ func (e errInvalidJSONCredential) Error() string {
3228// subcommandContext carries structs shared between subcommands, to reduce number of arguments needed to
3329// pass between subcommands, and make sure they are only initialized once
3430type subcommandContext struct {
35- c * cli.Context
36- logger logger.Service
31+ c * cli.Context
32+ logger logger.Service
33+ isUIEnabled bool
34+ fs fileSystem
3735
3836 // These fields should be accessed using their respective Getter
3937 tunnelstoreClient tunnelstore.Client
4038 userCredential * userCredential
41-
42- isUIEnabled bool
4339}
4440
4541func newSubcommandContext (c * cli.Context ) (* subcommandContext , error ) {
@@ -55,9 +51,18 @@ func newSubcommandContext(c *cli.Context) (*subcommandContext, error) {
5551 c : c ,
5652 logger : logger ,
5753 isUIEnabled : isUIEnabled ,
54+ fs : realFileSystem {},
5855 }, nil
5956}
6057
58+ // Returns something that can find the given tunnel's credentials file.
59+ func (sc * subcommandContext ) credentialFinder (tunnelID uuid.UUID ) CredFinder {
60+ if path := sc .c .String (CredFileFlag ); path != "" {
61+ return newStaticPath (path , sc .fs )
62+ }
63+ return newSearchByID (tunnelID , sc .c , sc .logger , sc .fs )
64+ }
65+
6166type userCredential struct {
6267 cert * certutil.OriginCert
6368 certPath string
@@ -108,56 +113,27 @@ func (sc *subcommandContext) credential() (*userCredential, error) {
108113 return sc .userCredential , nil
109114}
110115
111- func (sc * subcommandContext ) readTunnelCredentials (tunnelID uuid. UUID ) (* pogs. TunnelAuth , error ) {
112- filePath , err := sc . tunnelCredentialsPath ( tunnelID )
116+ func (sc * subcommandContext ) readTunnelCredentials (credFinder CredFinder ) (connection. Credentials , error ) {
117+ filePath , err := credFinder . Path ( )
113118 if err != nil {
114- return nil , err
119+ return connection. Credentials {} , err
115120 }
116- body , err := ioutil . ReadFile (filePath )
121+ body , err := sc . fs . readFile (filePath )
117122 if err != nil {
118- return nil , errors .Wrapf (err , "couldn't read tunnel credentials from %v" , filePath )
123+ return connection. Credentials {} , errors .Wrapf (err , "couldn't read tunnel credentials from %v" , filePath )
119124 }
120125
121- var auth pogs. TunnelAuth
122- if err = json .Unmarshal (body , & auth ); err != nil {
126+ var credentials connection. Credentials
127+ if err = json .Unmarshal (body , & credentials ); err != nil {
123128 if strings .HasSuffix (filePath , ".pem" ) {
124- return nil , fmt .Errorf ("The tunnel credentials file should be .json but you gave a .pem. " +
125- "The tunnel credentials file was originally created by `cloudflared tunnel create` and named %s.json." +
126- "You may have accidentally used the filepath to cert.pem, which is generated by `cloudflared tunnel " +
127- "login`." , tunnelID )
129+ return connection. Credentials {} , fmt .Errorf ("The tunnel credentials file should be .json but you gave a .pem. " +
130+ "The tunnel credentials file was originally created by `cloudflared tunnel create`. " +
131+ "You may have accidentally used the filepath to cert.pem, which is generated by `cloudflared tunnel " +
132+ "login`." )
128133 }
129- return nil , errInvalidJSONCredential {path : filePath , err : err }
134+ return connection. Credentials {} , errInvalidJSONCredential {path : filePath , err : err }
130135 }
131- return & auth , nil
132- }
133-
134- func (sc * subcommandContext ) tunnelCredentialsPath (tunnelID uuid.UUID ) (string , error ) {
135- if filePath := sc .c .String ("credentials-file" ); filePath != "" {
136- if validFilePath (filePath ) {
137- return filePath , nil
138- }
139- return "" , fmt .Errorf ("Tunnel credentials file %s doesn't exist or is not a file" , filePath )
140- }
141-
142- // Fallback to look for tunnel credentials in the origin cert directory
143- if originCertPath , err := findOriginCert (sc .c , sc .logger ); err == nil {
144- originCertDir := filepath .Dir (originCertPath )
145- if filePath , err := tunnelFilePath (tunnelID , originCertDir ); err == nil {
146- if validFilePath (filePath ) {
147- return filePath , nil
148- }
149- }
150- }
151-
152- // Last resort look under default config directories
153- for _ , configDir := range config .DefaultConfigSearchDirectories () {
154- if filePath , err := tunnelFilePath (tunnelID , configDir ); err == nil {
155- if validFilePath (filePath ) {
156- return filePath , nil
157- }
158- }
159- }
160- return "" , fmt .Errorf ("Tunnel credentials file not found" )
136+ return credentials , nil
161137}
162138
163139func (sc * subcommandContext ) create (name string ) (* tunnelstore.Tunnel , error ) {
@@ -180,7 +156,14 @@ func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
180156 if err != nil {
181157 return nil , err
182158 }
183- if writeFileErr := writeTunnelCredentials (tunnel .ID , credential .cert .AccountID , credential .certPath , tunnelSecret , sc .logger ); err != nil {
159+ tunnelCredentials := connection.Credentials {
160+ AccountTag : credential .cert .AccountID ,
161+ TunnelSecret : tunnelSecret ,
162+ TunnelID : tunnel .ID ,
163+ TunnelName : name ,
164+ }
165+ filePath , writeFileErr := writeTunnelCredentials (credential .certPath , & tunnelCredentials )
166+ if err != nil {
184167 var errorLines []string
185168 errorLines = append (errorLines , fmt .Sprintf ("Your tunnel '%v' was created with ID %v. However, cloudflared couldn't write to the tunnel credentials file at %v.json." , tunnel .Name , tunnel .ID , tunnel .ID ))
186169 errorLines = append (errorLines , fmt .Sprintf ("The file-writing error is: %v" , writeFileErr ))
@@ -193,6 +176,7 @@ func (sc *subcommandContext) create(name string) (*tunnelstore.Tunnel, error) {
193176 errorMsg := strings .Join (errorLines , "\n " )
194177 return nil , errors .New (errorMsg )
195178 }
179+ sc .logger .Infof ("Tunnel credentials written to %v. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel." , filePath )
196180
197181 if outputFormat := sc .c .String (outputFormatFlag .Name ); outputFormat != "" {
198182 return nil , renderOutput (outputFormat , & tunnel )
@@ -243,7 +227,8 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
243227 return errors .Wrapf (err , "Error deleting tunnel %s" , tunnel .ID )
244228 }
245229
246- tunnelCredentialsPath , err := sc .tunnelCredentialsPath (tunnel .ID )
230+ credFinder := sc .credentialFinder (id )
231+ tunnelCredentialsPath , err := credFinder .Path ()
247232 if err != nil {
248233 sc .logger .Infof ("Cannot locate tunnel credentials to delete, error: %v. Please delete the file manually" , err )
249234 return nil
@@ -256,22 +241,34 @@ func (sc *subcommandContext) delete(tunnelIDs []uuid.UUID) error {
256241 return nil
257242}
258243
244+ // findCredentials will choose the right way to find the credentials file, find it,
245+ // and add the TunnelID into any old credentials (generated before TUN-3581 added the `TunnelID`
246+ // field to credentials files)
247+ func (sc * subcommandContext ) findCredentials (tunnelID uuid.UUID ) (connection.Credentials , error ) {
248+ credFinder := sc .credentialFinder (tunnelID )
249+ credentials , err := sc .readTunnelCredentials (credFinder )
250+ // This line ensures backwards compatibility with credentials files generated before
251+ // TUN-3581. Those old credentials files don't have a TunnelID field, so we enrich the struct
252+ // with the ID, which we have already resolved from the user input.
253+ credentials .TunnelID = tunnelID
254+ return credentials , err
255+ }
256+
259257func (sc * subcommandContext ) run (tunnelID uuid.UUID ) error {
260- credentials , err := sc .readTunnelCredentials (tunnelID )
258+ credentials , err := sc .findCredentials (tunnelID )
261259 if err != nil {
262260 if e , ok := err .(errInvalidJSONCredential ); ok {
263261 sc .logger .Errorf ("The credentials file at %s contained invalid JSON. This is probably caused by passing the wrong filepath. Reminder: the credentials file is a .json file created via `cloudflared tunnel create`." , e .path )
264262 sc .logger .Errorf ("Invalid JSON when parsing credentials file: %s" , e .err .Error ())
265263 }
266264 return err
267265 }
268-
269266 return StartServer (
270267 sc .c ,
271268 version ,
272269 shutdownC ,
273270 graceShutdownC ,
274- & connection.NamedTunnelConfig {Auth : * credentials , ID : tunnelID },
271+ & connection.NamedTunnelConfig {Credentials : credentials },
275272 sc .logger ,
276273 sc .isUIEnabled ,
277274 )
@@ -300,6 +297,7 @@ func (sc *subcommandContext) route(tunnelID uuid.UUID, r tunnelstore.Route) (tun
300297 return client .RouteTunnel (tunnelID , r )
301298}
302299
300+ // Query Tunnelstore to find the active tunnel with the given name.
303301func (sc * subcommandContext ) tunnelActive (name string ) (* tunnelstore.Tunnel , bool , error ) {
304302 filter := tunnelstore .NewFilter ()
305303 filter .NoDeleted ()
@@ -322,6 +320,15 @@ func (sc *subcommandContext) findID(input string) (uuid.UUID, error) {
322320 return u , nil
323321 }
324322
323+ // Look up name in the credentials file.
324+ credFinder := newStaticPath (sc .c .String (CredFileFlag ), sc .fs )
325+ if credentials , err := sc .readTunnelCredentials (credFinder ); err == nil {
326+ if credentials .TunnelID != uuid .Nil && input == credentials .TunnelName {
327+ return credentials .TunnelID , nil
328+ }
329+ }
330+
331+ // Fall back to querying Tunnelstore.
325332 if tunnel , found , err := sc .tunnelActive (input ); err != nil {
326333 return uuid .Nil , err
327334 } else if found {
0 commit comments