@@ -2,6 +2,7 @@ package tail
22
33import (
44 "encoding/json"
5+ "errors"
56 "fmt"
67 "net/http"
78 "net/url"
@@ -10,28 +11,32 @@ import (
1011 "syscall"
1112 "time"
1213
14+ "github.com/google/uuid"
1315 "github.com/mattn/go-colorable"
1416 "github.com/rs/zerolog"
1517 "github.com/urfave/cli/v2"
1618 "nhooyr.io/websocket"
1719
20+ "github.com/cloudflare/cloudflared/cmd/cloudflared/cliutil"
21+ "github.com/cloudflare/cloudflared/credentials"
1822 "github.com/cloudflare/cloudflared/logger"
1923 "github.com/cloudflare/cloudflared/management"
2024)
2125
2226var (
23- version string
27+ buildInfo * cliutil. BuildInfo
2428)
2529
26- func Init (v string ) {
27- version = v
30+ func Init (bi * cliutil. BuildInfo ) {
31+ buildInfo = bi
2832}
2933
3034func Command () * cli.Command {
3135 return & cli.Command {
32- Name : "tail" ,
33- Action : Run ,
34- Usage : "Stream logs from a remote cloudflared" ,
36+ Name : "tail" ,
37+ Action : Run ,
38+ Usage : "Stream logs from a remote cloudflared" ,
39+ UsageText : "cloudflared tail [tail command options] [TUNNEL-ID]" ,
3540 Flags : []cli.Flag {
3641 & cli.StringFlag {
3742 Name : "connector-id" ,
@@ -75,6 +80,12 @@ func Command() *cli.Command {
7580 Usage : "Application logging level {debug, info, warn, error, fatal}" ,
7681 EnvVars : []string {"TUNNEL_LOGLEVEL" },
7782 },
83+ & cli.StringFlag {
84+ Name : credentials .OriginCertFlag ,
85+ Usage : "Path to the certificate generated for your origin when you run cloudflared login." ,
86+ EnvVars : []string {"TUNNEL_ORIGIN_CERT" },
87+ Value : credentials .FindDefaultOriginCertPath (),
88+ },
7889 },
7990 }
8091}
@@ -159,6 +170,59 @@ func parseFilters(c *cli.Context) (*management.StreamingFilters, error) {
159170 }, nil
160171}
161172
173+ // getManagementToken will make a call to the Cloudflare API to acquire a management token for the requested tunnel.
174+ func getManagementToken (c * cli.Context , log * zerolog.Logger ) (string , error ) {
175+ userCreds , err := credentials .Read (c .String (credentials .OriginCertFlag ), log )
176+ if err != nil {
177+ return "" , err
178+ }
179+
180+ client , err := userCreds .Client (c .String ("api-url" ), buildInfo .UserAgent (), log )
181+ if err != nil {
182+ return "" , err
183+ }
184+
185+ tunnelIDString := c .Args ().First ()
186+ if tunnelIDString == "" {
187+ return "" , errors .New ("no tunnel ID provided" )
188+ }
189+ tunnelID , err := uuid .Parse (tunnelIDString )
190+ if err != nil {
191+ return "" , errors .New ("unable to parse provided tunnel id as a valid UUID" )
192+ }
193+
194+ token , err := client .GetManagementToken (tunnelID )
195+ if err != nil {
196+ return "" , err
197+ }
198+
199+ return token , nil
200+ }
201+
202+ // buildURL will build the management url to contain the required query parameters to authenticate the request.
203+ func buildURL (c * cli.Context , log * zerolog.Logger ) (url.URL , error ) {
204+ var err error
205+ managementHostname := c .String ("management-hostname" )
206+ token := c .String ("token" )
207+ if token == "" {
208+ token , err = getManagementToken (c , log )
209+ if err != nil {
210+ return url.URL {}, fmt .Errorf ("unable to acquire management token for requested tunnel id: %w" , err )
211+ }
212+ }
213+ query := url.Values {}
214+ query .Add ("access_token" , token )
215+ connector := c .String ("connector-id" )
216+ if connector != "" {
217+ connectorID , err := uuid .Parse (connector )
218+ if err != nil {
219+ return url.URL {}, fmt .Errorf ("unabled to parse 'connector-id' flag into a valid UUID: %w" , err )
220+ }
221+ query .Add ("connector_id" , connectorID .String ())
222+ }
223+ return url.URL {Scheme : "wss" , Host : managementHostname , Path : "/logs" , RawQuery : query .Encode ()}, nil
224+ }
225+
162226// Run implements a foreground runner
163227func Run (c * cli.Context ) error {
164228 log := createLogger (c )
@@ -173,12 +237,14 @@ func Run(c *cli.Context) error {
173237 return nil
174238 }
175239
176- managementHostname := c .String ("management-hostname" )
177- token := c .String ("token" )
178- u := url.URL {Scheme : "wss" , Host : managementHostname , Path : "/logs" , RawQuery : "access_token=" + token }
240+ u , err := buildURL (c , log )
241+ if err != nil {
242+ log .Err (err ).Msg ("unable to construct management request URL" )
243+ return nil
244+ }
179245
180246 header := make (http.Header )
181- header .Add ("User-Agent" , "cloudflared/" + version )
247+ header .Add ("User-Agent" , buildInfo . UserAgent () )
182248 trace := c .String ("trace" )
183249 if trace != "" {
184250 header ["cf-trace-id" ] = []string {trace }
@@ -206,6 +272,11 @@ func Run(c *cli.Context) error {
206272 log .Error ().Err (err ).Msg ("unable to request logs from management tunnel" )
207273 return nil
208274 }
275+ log .Debug ().
276+ Str ("tunnel-id" , c .Args ().First ()).
277+ Str ("connector-id" , c .String ("connector-id" )).
278+ Interface ("filters" , filters ).
279+ Msg ("connected" )
209280
210281 readerDone := make (chan struct {})
211282
0 commit comments