TUN-4699: Make quick tunnels the default in cloudflared

This commit is contained in:
Rishabh Bector 2021-07-09 12:52:41 -05:00
parent bd8af7d80d
commit 18992efa0c
11 changed files with 47 additions and 85 deletions

View File

@ -206,7 +206,7 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri
// runClassicTunnel creates a "classic" non-named tunnel // runClassicTunnel creates a "classic" non-named tunnel
func runClassicTunnel(sc *subcommandContext) error { 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) { func routeFromFlag(c *cli.Context) (route tunnelstore.Route, ok bool) {
@ -225,7 +225,6 @@ func StartServer(
namedTunnel *connection.NamedTunnelConfig, namedTunnel *connection.NamedTunnelConfig,
log *zerolog.Logger, log *zerolog.Logger,
isUIEnabled bool, isUIEnabled bool,
quickTunnelHostname string,
) error { ) error {
_ = raven.SetDSN(sentryDSN) _ = raven.SetDSN(sentryDSN)
var wg sync.WaitGroup var wg sync.WaitGroup
@ -325,6 +324,15 @@ func StartServer(
observer := connection.NewObserver(log, logTransport, isUIEnabled) 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) tunnelConfig, ingressRules, err := prepareTunnelConfig(c, buildInfo, version, log, logTransport, observer, namedTunnel)
if err != nil { if err != nil {
log.Err(err).Msg("Couldn't start tunnel") log.Err(err).Msg("Couldn't start tunnel")
@ -342,7 +350,7 @@ func StartServer(
defer wg.Done() defer wg.Done()
readinessServer := metrics.NewReadyServer(log) readinessServer := metrics.NewReadyServer(log)
observer.RegisterSink(readinessServer) 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 { 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{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "quick-service", Name: "quick-service",
Usage: "URL for a service which manages unauthenticated 'quick' tunnels.", Usage: "URL for a service which manages unauthenticated 'quick' tunnels.",
Value: "https://api.trycloudflare.com",
Hidden: true, Hidden: true,
}), }),
selectProtocolFlag, selectProtocolFlag,

View File

@ -161,7 +161,7 @@ func prepareTunnelConfig(
log.Err(err).Str(LogFieldHostname, configHostname).Msg("Invalid hostname") log.Err(err).Str(LogFieldHostname, configHostname).Msg("Invalid hostname")
return nil, ingress.Ingress{}, errors.Wrap(err, "Invalid hostname") return nil, ingress.Ingress{}, errors.Wrap(err, "Invalid hostname")
} }
isFreeTunnel := hostname == "" isQuickTunnel := hostname == ""
clientID := c.String("id") clientID := c.String("id")
if !c.IsSet("id") { if !c.IsSet("id") {
clientID, err = generateRandomClientID(log) clientID, err = generateRandomClientID(log)
@ -179,7 +179,7 @@ func prepareTunnelConfig(
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID}) tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
var originCert []byte var originCert []byte
if !isFreeTunnel { if !isQuickTunnel {
originCertPath := c.String("origincert") originCertPath := c.String("origincert")
originCertLog := log.With(). originCertLog := log.With().
Str(LogFieldOriginCertPath, originCertPath). Str(LogFieldOriginCertPath, originCertPath).
@ -278,7 +278,6 @@ func prepareTunnelConfig(
HAConnections: c.Int("ha-connections"), HAConnections: c.Int("ha-connections"),
IncidentLookup: origin.NewIncidentLookup(), IncidentLookup: origin.NewIncidentLookup(),
IsAutoupdated: c.Bool("is-autoupdated"), IsAutoupdated: c.Bool("is-autoupdated"),
IsFreeTunnel: isFreeTunnel,
LBPool: c.String("lb-pool"), LBPool: c.String("lb-pool"),
Tags: tags, Tags: tags,
Log: log, Log: log,

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
@ -51,7 +52,7 @@ func RunQuickTunnel(sc *subcommandContext) error {
TunnelName: data.Result.Name, TunnelName: data.Result.Name,
} }
for _, line := range connection.AsciiBox([]string{ for _, line := range AsciiBox([]string{
"Your Quick Tunnel has been created! Visit it at:", "Your Quick Tunnel has been created! Visit it at:",
data.Result.Hostname, data.Result.Hostname,
}, 2) { }, 2) {
@ -61,10 +62,9 @@ func RunQuickTunnel(sc *subcommandContext) error {
return StartServer( return StartServer(
sc.c, sc.c,
version, version,
&connection.NamedTunnelConfig{Credentials: credentials}, &connection.NamedTunnelConfig{Credentials: credentials, QuickTunnelUrl: data.Result.Hostname},
sc.log, sc.log,
sc.isUIEnabled, sc.isUIEnabled,
data.Result.Hostname,
) )
} }
@ -86,3 +86,26 @@ type QuickTunnel struct {
AccountTag string `json:"account_tag"` AccountTag string `json:"account_tag"`
Secret []byte `json:"secret"` 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
}

View File

@ -286,7 +286,6 @@ func (sc *subcommandContext) run(tunnelID uuid.UUID) error {
&connection.NamedTunnelConfig{Credentials: credentials}, &connection.NamedTunnelConfig{Credentials: credentials},
sc.log, sc.log,
sc.isUIEnabled, sc.isUIEnabled,
"",
) )
} }

View File

@ -24,12 +24,13 @@ class TestReconnect:
# Repeat the test multiple times because some issues only occur after multiple reconnects # Repeat the test multiple times because some issues only occur after multiple reconnects
self.assert_reconnect(config, cloudflared, 5) self.assert_reconnect(config, cloudflared, 5)
@pytest.mark.skipif(platform.system() == "Windows", reason=f"Currently buggy on Windows TUN-4699")
def test_classic_reconnect(self, tmp_path, component_tests_config): def test_classic_reconnect(self, tmp_path, component_tests_config):
extra_config = copy.copy(self.extra_config) extra_config = copy.copy(self.extra_config)
extra_config["hello-world"] = True extra_config["hello-world"] = True
config = component_tests_config( config = component_tests_config(
additional_config=extra_config, named_tunnel=False) additional_config=extra_config, named_tunnel=False)
with start_cloudflared(tmp_path, config, cfd_args=[], new_process=True, allow_input=True, capture_output=False) as cloudflared: with start_cloudflared(tmp_path, config, cfd_args=[], new_process=True, allow_input=True, capture_output=True) as cloudflared:
self.assert_reconnect(config, cloudflared, 1) self.assert_reconnect(config, cloudflared, 1)
def send_reconnect(self, cloudflared, secs): def send_reconnect(self, cloudflared, secs):

View File

@ -29,8 +29,9 @@ type Config struct {
} }
type NamedTunnelConfig struct { type NamedTunnelConfig struct {
Credentials Credentials Credentials Credentials
Client pogs.ClientInfo Client pogs.ClientInfo
QuickTunnelUrl string
} }
// Credentials are stored in the credentials file and contain all info needed to run a tunnel. // 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 UseReconnectToken bool
} }
func (c *ClassicTunnelConfig) IsTrialZone() bool {
return c.Hostname == ""
}
// Type indicates the connection type of the connection. // Type indicates the connection type of the connection.
type Type int type Type int

View File

@ -18,7 +18,7 @@ const (
Connected Connected
// Reconnecting means the connection to the edge is being re-established. // Reconnecting means the connection to the edge is being re-established.
Reconnecting 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 SetURL
// RegisteringTunnel means the non-named tunnel is registering its connection. // RegisteringTunnel means the non-named tunnel is registering its connection.
RegisteringTunnel RegisteringTunnel

View File

@ -1,13 +1,7 @@
package connection package connection
import ( import (
"fmt"
"net/url"
"strings"
"github.com/rs/zerolog" "github.com/rs/zerolog"
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
) )
const ( const (
@ -54,53 +48,6 @@ func (o *Observer) logServerInfo(connIndex uint8, location, msg string) {
o.metrics.registerServerLocation(uint8ToString(connIndex), location) 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) { func (o *Observer) sendRegisteringEvent(connIndex uint8) {
o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel}) o.sendEvent(Event{Index: connIndex, EventType: RegisteringTunnel})
} }
@ -109,7 +56,7 @@ func (o *Observer) sendConnectedEvent(connIndex uint8, location string) {
o.sendEvent(Event{Index: connIndex, EventType: Connected, Location: location}) 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}) o.sendEvent(Event{EventType: SetURL, URL: url})
} }

View File

@ -159,8 +159,6 @@ func (h *h2muxConnection) registerTunnel(ctx context.Context, credentialSetter C
return h.processRegisterTunnelError(registrationErr, register) return h.processRegisterTunnelError(registrationErr, register)
} }
// Send free tunnel URL to UI
h.observer.sendURL(registration.Url)
credentialSetter.SetEventDigest(h.connIndex, registration.EventDigest) credentialSetter.SetEventDigest(h.connIndex, registration.EventDigest)
return h.processRegistrationSuccess(registration, register, credentialSetter, classicTunnel) 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()) 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) credentialManager.SetConnDigest(h.connIndex, registration.ConnDigest)
h.observer.metrics.userHostnamesCounts.WithLabelValues(registration.Url).Inc() h.observer.metrics.userHostnamesCounts.WithLabelValues(registration.Url).Inc()

View File

@ -36,8 +36,6 @@ func (rs *ReadyServer) OnTunnelEvent(c conn.Event) {
rs.Lock() rs.Lock()
rs.isConnected[int(c.Index)] = false rs.isConnected[int(c.Index)] = false
rs.Unlock() rs.Unlock()
case conn.SetURL:
break
default: default:
rs.log.Error().Msgf("Unknown connection event case %v", c) rs.log.Error().Msgf("Unknown connection event case %v", c)
} }

View File

@ -46,7 +46,6 @@ type TunnelConfig struct {
HAConnections int HAConnections int
IncidentLookup IncidentLookup IncidentLookup IncidentLookup
IsAutoupdated bool IsAutoupdated bool
IsFreeTunnel bool
LBPool string LBPool string
Tags []tunnelpogs.Tag Tags []tunnelpogs.Tag
Log *zerolog.Logger Log *zerolog.Logger