diff --git a/CHANGES.md b/CHANGES.md index d8fe9fcc..20ab8b55 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,12 @@ **Experimental**: This is a new format for release notes. The format and availability is subject to change. +## 2021.8.4 +### Improvements +- Temporary tunnels (those hosted on trycloudflare.com that do not require a Cloudflare login) now run as Named Tunnels +underneath. We recall that these tunnels should not be relied upon for production usage as they come with no guarantee +of uptime. Previous cloudflared versions will soon be unable to run legacy temporary tunnels and will require an update +(to this version or more recent). + ## 2021.8.2 ### Improvements - Because Equinox os shutting down, all cloudflared releases are now present [here](https://github.com/cloudflare/cloudflared/releases). diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index b7221a59..a03ba888 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -206,7 +206,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri // runClassicTunnel creates a "classic" non-named tunnel func runClassicTunnel(sc *subcommandContext) error { - return StartServer(sc.c, version, nil, sc.log, sc.isUIEnabled, "") + return StartServer(sc.c, version, nil, sc.log, sc.isUIEnabled) } func routeFromFlag(c *cli.Context) (route tunnelstore.Route, ok bool) { @@ -225,7 +225,6 @@ func StartServer( namedTunnel *connection.NamedTunnelConfig, log *zerolog.Logger, isUIEnabled bool, - quickTunnelHostname string, ) error { _ = raven.SetDSN(sentryDSN) var wg sync.WaitGroup @@ -325,6 +324,15 @@ func StartServer( observer := connection.NewObserver(log, logTransport, isUIEnabled) + // Send Quick Tunnel URL to UI if applicable + var quickTunnelURL string + if namedTunnel != nil { + quickTunnelURL = namedTunnel.QuickTunnelUrl + } + if quickTunnelURL != "" { + observer.SendURL(quickTunnelURL) + } + tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, log, logTransport, observer, namedTunnel) if err != nil { log.Err(err).Msg("Couldn't start tunnel") @@ -342,7 +350,7 @@ func StartServer( defer wg.Done() readinessServer := metrics.NewReadyServer(log) observer.RegisterSink(readinessServer) - errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelHostname, log) + errC <- metrics.ServeMetrics(metricsListener, ctx.Done(), readinessServer, quickTunnelURL, log) }() if err := ingressRules.StartOrigins(&wg, log, ctx.Done(), errC); err != nil { @@ -626,6 +634,7 @@ func tunnelFlags(shouldHide bool) []cli.Flag { altsrc.NewStringFlag(&cli.StringFlag{ Name: "quick-service", Usage: "URL for a service which manages unauthenticated 'quick' tunnels.", + Value: "https://api.trycloudflare.com", Hidden: true, }), selectProtocolFlag, diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 94d64ec7..f485a49c 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -161,7 +161,7 @@ func prepareTunnelConfig( log.Err(err).Str(LogFieldHostname, configHostname).Msg("Invalid hostname") return nil, ingress.Ingress{}, errors.Wrap(err, "Invalid hostname") } - isFreeTunnel := hostname == "" + isQuickTunnel := hostname == "" clientID := c.String("id") if !c.IsSet("id") { clientID, err = generateRandomClientID(log) @@ -179,7 +179,7 @@ func prepareTunnelConfig( tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID}) var originCert []byte - if !isFreeTunnel { + if !isQuickTunnel { originCertPath := c.String("origincert") originCertLog := log.With(). Str(LogFieldOriginCertPath, originCertPath). @@ -285,7 +285,6 @@ func prepareTunnelConfig( HAConnections: c.Int("ha-connections"), IncidentLookup: origin.NewIncidentLookup(), IsAutoupdated: c.Bool("is-autoupdated"), - IsFreeTunnel: isFreeTunnel, LBPool: c.String("lb-pool"), Tags: tags, Log: log, diff --git a/cmd/cloudflared/tunnel/quick_tunnel.go b/cmd/cloudflared/tunnel/quick_tunnel.go index 9c0f1491..f3441d84 100644 --- a/cmd/cloudflared/tunnel/quick_tunnel.go +++ b/cmd/cloudflared/tunnel/quick_tunnel.go @@ -15,11 +15,17 @@ import ( const httpTimeout = 15 * time.Second +const disclaimer = "Thank you for trying Cloudflare Tunnel. Doing so, without a Cloudflare account, is a quick way to" + + " experiment and try it out. However, be aware that these account-less Tunnels have no uptime guarantee. If you " + + "intend to use Tunnels in production you should use a pre-created named tunnel by following: " + + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps" + // 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...") + sc.log.Info().Msg(disclaimer) + sc.log.Info().Msg("Requesting new quick Tunnel on trycloudflare.com...") client := http.Client{ Transport: &http.Transport{ @@ -31,18 +37,18 @@ func RunQuickTunnel(sc *subcommandContext) error { 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") + 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") + 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") + return errors.Wrap(err, "failed to parse quick Tunnel ID") } credentials := connection.Credentials{ @@ -57,8 +63,8 @@ func RunQuickTunnel(sc *subcommandContext) error { url = "https://" + url } - for _, line := range connection.AsciiBox([]string{ - "Your Quick Tunnel has been created! Visit it at:", + for _, line := range AsciiBox([]string{ + "Your quick Tunnel has been created! Visit it at (it may take some time to be reachable):", url, }, 2) { sc.log.Info().Msg(line) @@ -67,10 +73,9 @@ func RunQuickTunnel(sc *subcommandContext) error { return StartServer( sc.c, version, - &connection.NamedTunnelConfig{Credentials: credentials}, + &connection.NamedTunnelConfig{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname}, sc.log, sc.isUIEnabled, - data.Result.Hostname, ) } @@ -92,3 +97,26 @@ type QuickTunnel struct { AccountTag string `json:"account_tag"` Secret []byte `json:"secret"` } + +// Print out the given lines in a nice ASCII box. +func AsciiBox(lines []string, padding int) (box []string) { + maxLen := maxLen(lines) + spacer := strings.Repeat(" ", padding) + border := "+" + strings.Repeat("-", maxLen+(padding*2)) + "+" + box = append(box, border) + for _, line := range lines { + box = append(box, "|"+spacer+line+strings.Repeat(" ", maxLen-len(line))+spacer+"|") + } + box = append(box, border) + return +} + +func maxLen(lines []string) int { + max := 0 + for _, line := range lines { + if len(line) > max { + max = len(line) + } + } + return max +} diff --git a/cmd/cloudflared/tunnel/subcommand_context.go b/cmd/cloudflared/tunnel/subcommand_context.go index 69d16478..d5b42590 100644 --- a/cmd/cloudflared/tunnel/subcommand_context.go +++ b/cmd/cloudflared/tunnel/subcommand_context.go @@ -286,7 +286,6 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error { &connection.NamedTunnelConfig{Credentials: credentials}, sc.log, sc.isUIEnabled, - "", ) } diff --git a/connection/connection.go b/connection/connection.go index e68afb64..dbe5ef1e 100644 --- a/connection/connection.go +++ b/connection/connection.go @@ -29,8 +29,9 @@ type Config struct { } type NamedTunnelConfig struct { - Credentials Credentials - Client pogs.ClientInfo + Credentials Credentials + Client pogs.ClientInfo + QuickTunnelUrl string } // Credentials are stored in the credentials file and contain all info needed to run a tunnel. @@ -55,10 +56,6 @@ type ClassicTunnelConfig struct { UseReconnectToken bool } -func (c *ClassicTunnelConfig) IsTrialZone() bool { - return c.Hostname == "" -} - // Type indicates the connection type of the connection. type Type int diff --git a/connection/event.go b/connection/event.go index 6afde5bb..ab6d0d33 100644 --- a/connection/event.go +++ b/connection/event.go @@ -18,7 +18,7 @@ const ( Connected // Reconnecting means the connection to the edge is being re-established. Reconnecting - // SetURL means this connection's tunnel was given a URL by the edge. Used for free tunnels. + // SetURL means this connection's tunnel was given a URL by the edge. Used for quick tunnels. SetURL // RegisteringTunnel means the non-named tunnel is registering its connection. RegisteringTunnel diff --git a/connection/observer.go b/connection/observer.go index 57efaf70..5c2bf06c 100644 --- a/connection/observer.go +++ b/connection/observer.go @@ -1,13 +1,9 @@ package connection import ( - "fmt" - "net/url" "strings" "github.com/rs/zerolog" - - tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs" ) const ( @@ -54,53 +50,6 @@ func (o *Observer) logServerInfo(connIndex uint8, location, msg string) { o.metrics.registerServerLocation(uint8ToString(connIndex), location) } -func (o *Observer) logTrialHostname(registration *tunnelpogs.TunnelRegistration) error { - // 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) { - o.log.Info().Msg(line) - } - } else { - o.log.Error().Msg("Failed to connect tunnel, please try again.") - return fmt.Errorf("empty URL in response from Cloudflare edge") - } - } - return nil -} - -// Print out the given lines in a nice ASCII box. -func AsciiBox(lines []string, padding int) (box []string) { - maxLen := maxLen(lines) - spacer := strings.Repeat(" ", padding) - - border := "+" + strings.Repeat("-", maxLen+(padding*2)) + "+" - - box = append(box, border) - for _, line := range lines { - box = append(box, "|"+spacer+line+strings.Repeat(" ", maxLen-len(line))+spacer+"|") - } - box = append(box, border) - return -} - -func maxLen(lines []string) int { - max := 0 - for _, line := range lines { - if len(line) > max { - max = len(line) - } - } - return max -} - -func TrialZoneMsg(url string) []string { - return []string{ - "Your free tunnel has started! Visit it:", - " " + url, - } -} - func (o *Observer) sendRegisteringEvent(connIndex uint8) { o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel}) } @@ -109,7 +58,7 @@ func (o *Observer) sendConnectedEvent(connIndex uint8, location string) { o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) } -func (o *Observer) sendURL(url string) { +func (o *Observer) SendURL(url string) { o.sendEvent(Event{EventType: SetURL, URL: url}) if !strings.HasPrefix(url, "https://") { diff --git a/connection/observer_test.go b/connection/observer_test.go index b4a271c0..a8c68e9f 100644 --- a/connection/observer_test.go +++ b/connection/observer_test.go @@ -14,10 +14,10 @@ import ( func TestSendUrl(t *testing.T) { observer := NewObserver(&log, &log, false) - observer.sendURL("my-url.com") + observer.SendURL("my-url.com") assert.Equal(t, 1.0, getCounterValue(t, observer.metrics.userHostnamesCounts, "https://my-url.com")) - observer.sendURL("https://another-long-one.com") + observer.SendURL("https://another-long-one.com") assert.Equal(t, 1.0, getCounterValue(t, observer.metrics.userHostnamesCounts, "https://another-long-one.com")) } diff --git a/connection/rpc.go b/connection/rpc.go index 7ae55d40..e8eb6f4a 100644 --- a/connection/rpc.go +++ b/connection/rpc.go @@ -159,8 +159,6 @@ func (h *h2muxConnection) registerTunnel(ctx context.Context, credentialSetter C return h.processRegisterTunnelError(registrationErr, register) } - // Send free tunnel URL to UI - h.observer.sendURL(registration.Url) credentialSetter.SetEventDigest(h.connIndex, registration.EventDigest) return h.processRegistrationSuccess(registration, register, credentialSetter, classicTunnel) } @@ -187,14 +185,6 @@ func (h *h2muxConnection) processRegistrationSuccess( h.observer.log.Info().Msgf("Each HA connection's tunnel IDs: %v", h.observer.metrics.tunnelsHA.String()) } - // 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 classicTunnel.IsTrialZone() { - err := h.observer.logTrialHostname(registration) - if err != nil { - return err - } - } - credentialManager.SetConnDigest(h.connIndex, registration.ConnDigest) h.observer.metrics.userHostnamesCounts.WithLabelValues(registration.Url).Inc() diff --git a/metrics/readiness.go b/metrics/readiness.go index 62d9f8f4..030f71cb 100644 --- a/metrics/readiness.go +++ b/metrics/readiness.go @@ -36,8 +36,6 @@ func (rs *ReadyServer) OnTunnelEvent(c conn.Event) { rs.Lock() rs.isConnected[int(c.Index)] = false rs.Unlock() - case conn.SetURL: - break default: rs.log.Error().Msgf("Unknown connection event case %v", c) } diff --git a/origin/tunnel.go b/origin/tunnel.go index 0b7887dc..fa734b4f 100644 --- a/origin/tunnel.go +++ b/origin/tunnel.go @@ -48,7 +48,6 @@ type TunnelConfig struct { HAConnections int IncidentLookup IncidentLookup IsAutoupdated bool - IsFreeTunnel bool LBPool string Tags []tunnelpogs.Tag Log *zerolog.Logger