From 3eb9efd9f006848a7cafd8990a214ec0698dd5b1 Mon Sep 17 00:00:00 2001 From: Rishabh Bector Date: Mon, 21 Jun 2021 19:31:24 -0500 Subject: [PATCH] TUN-4521: Modify cloudflared to use zoneless-tunnels-worker for free tunnels --- cmd/cloudflared/tunnel/cmd.go | 11 ++++ cmd/cloudflared/tunnel/quick_tunnel.go | 87 ++++++++++++++++++++++++++ connection/observer.go | 6 +- 3 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 cmd/cloudflared/tunnel/quick_tunnel.go diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 2c3ee3ad..817f90f7 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -163,6 +163,12 @@ func TunnelCommand(c *cli.Context) error { return fmt.Errorf("Use `cloudflared tunnel run` to start tunnel %s", ref) } + // Unauthenticated named tunnel on ..com + // For now, default to legacy setup unless quick-service is specified + if c.String("hostname") == "" && c.String("quick-service") != "" { + return RunQuickTunnel(sc) + } + // Start a classic tunnel return runClassicTunnel(sc) } @@ -616,6 +622,11 @@ func tunnelFlags(shouldHide bool) []cli.Flag { Value: false, Hidden: shouldHide, }), + altsrc.NewStringFlag(&cli.StringFlag{ + Name: "quick-service", + Usage: "URL for a service which manages unauthenticated 'quick' tunnels.", + Hidden: true, + }), selectProtocolFlag, overwriteDNSFlag, }...) diff --git a/cmd/cloudflared/tunnel/quick_tunnel.go b/cmd/cloudflared/tunnel/quick_tunnel.go new file mode 100644 index 00000000..08c128a3 --- /dev/null +++ b/cmd/cloudflared/tunnel/quick_tunnel.go @@ -0,0 +1,87 @@ +package tunnel + +import ( + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" + + "github.com/cloudflare/cloudflared/connection" +) + +const httpTimeout = 15 * time.Second + +// RunQuickTunnel requests a tunnel from the specified service. +// We use this to power quick tunnels on trycloudflare.com, but the +// service is open-source and could be used by anyone. +func RunQuickTunnel(sc *subcommandContext) error { + sc.log.Info().Msg("Requesting new Quick Tunnel...") + + client := http.Client{ + Transport: &http.Transport{ + TLSHandshakeTimeout: httpTimeout, + ResponseHeaderTimeout: httpTimeout, + }, + Timeout: httpTimeout, + } + + resp, err := client.Post(fmt.Sprintf("%s/tunnel", sc.c.String("quick-service")), "application/json", nil) + if err != nil { + return errors.Wrap(err, "failed to request quick tunnel") + } + defer resp.Body.Close() + + var data QuickTunnelResponse + if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { + return errors.Wrap(err, "failed to unmarshal quick tunnel") + } + + tunnelID, err := uuid.Parse(data.Result.ID) + if err != nil { + return errors.Wrap(err, "failed to parse quick tunnel ID") + } + + credentials := connection.Credentials{ + AccountTag: data.Result.AccountTag, + TunnelSecret: data.Result.Secret, + TunnelID: tunnelID, + TunnelName: data.Result.Name, + } + + for _, line := range connection.AsciiBox([]string{ + "Your Quick Tunnel has been created! Visit it at:", + data.Result.Hostname, + }, 2) { + sc.log.Info().Msg(line) + } + + return StartServer( + sc.c, + version, + &connection.NamedTunnelConfig{Credentials: credentials}, + sc.log, + sc.isUIEnabled, + ) +} + +type QuickTunnelResponse struct { + Success bool + Result QuickTunnel + Errors []QuickTunnelError +} + +type QuickTunnelError struct { + Code int + Message string +} + +type QuickTunnel struct { + ID string `json:"id"` + Name string `json:"name"` + Hostname string `json:"hostname"` + AccountTag string `json:"account_tag"` + Secret []byte `json:"secret"` +} diff --git a/connection/observer.go b/connection/observer.go index 2ef9689c..bb0887a5 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -58,7 +58,7 @@ func (o *Observer) logTrialHostname(registration *tunnelpogs.TunnelRegistration) // Print out the user's trial zone URL in a nice box (if they requested and got one and UI flag is not set) if !o.uiEnabled { if registrationURL, err := url.Parse(registration.Url); err == nil { - for _, line := range asciiBox(trialZoneMsg(registrationURL.String()), 2) { + for _, line := range AsciiBox(TrialZoneMsg(registrationURL.String()), 2) { o.log.Info().Msg(line) } } else { @@ -70,7 +70,7 @@ func (o *Observer) logTrialHostname(registration *tunnelpogs.TunnelRegistration) } // Print out the given lines in a nice ASCII box. -func asciiBox(lines []string, padding int) (box []string) { +func AsciiBox(lines []string, padding int) (box []string) { maxLen := maxLen(lines) spacer := strings.Repeat(" ", padding) @@ -94,7 +94,7 @@ func maxLen(lines []string) int { return max } -func trialZoneMsg(url string) []string { +func TrialZoneMsg(url string) []string { return []string{ "Your free tunnel has started! Visit it:", " " + url,