Skip to content

Commit 62dcb8a

Browse files
committed
Revert "TUN-7065: Remove classic tunnel creation"
This reverts commit c24f275.
1 parent 90d710e commit 62dcb8a

File tree

9 files changed

+254
-50
lines changed

9 files changed

+254
-50
lines changed

cmd/cloudflared/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ func action(graceShutdownC chan struct{}) cli.ActionFunc {
155155
if isEmptyInvocation(c) {
156156
return handleServiceMode(c, graceShutdownC)
157157
}
158+
tags := make(map[string]string)
159+
tags["hostname"] = c.String("hostname")
158160
func() {
159161
defer sentry.Recover()
160162
err = tunnel.TunnelCommand(c)

cmd/cloudflared/tunnel/cmd.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ var (
100100
routeFailMsg = fmt.Sprintf("failed to provision routing, please create it manually via Cloudflare dashboard or UI; "+
101101
"most likely you already have a conflicting record there. You can also rerun this command with --%s to overwrite "+
102102
"any existing DNS records for this hostname.", overwriteDNSFlag)
103-
deprecatedClassicTunnelErr = fmt.Errorf("Classic tunnels have been deprecated, please use Named Tunnels. (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/)")
104103
)
105104

106105
func Flags() []cli.Flag {
@@ -183,17 +182,17 @@ func TunnelCommand(c *cli.Context) error {
183182

184183
// Unauthenticated named tunnel on <random>.<quick-tunnels-service>.com
185184
shouldRunQuickTunnel := c.IsSet("url") || c.IsSet("hello-world")
186-
if !dnsProxyStandAlone(c, nil) && c.String("quick-service") != "" && shouldRunQuickTunnel {
185+
if !dnsProxyStandAlone(c, nil) && c.String("hostname") == "" && c.String("quick-service") != "" && shouldRunQuickTunnel {
187186
return RunQuickTunnel(sc)
188187
}
189188

190189
if ref := config.GetConfiguration().TunnelID; ref != "" {
191190
return fmt.Errorf("Use `cloudflared tunnel run` to start tunnel %s", ref)
192191
}
193192

194-
// classic tunnel usage is no longer supported
193+
// Start a classic tunnel if hostname is specified.
195194
if c.String("hostname") != "" {
196-
return deprecatedClassicTunnelErr
195+
return runClassicTunnel(sc)
197196
}
198197

199198
if c.IsSet("proxy-dns") {
@@ -238,6 +237,11 @@ func runAdhocNamedTunnel(sc *subcommandContext, name, credentialsOutputPath stri
238237
return nil
239238
}
240239

240+
// runClassicTunnel creates a "classic" non-named tunnel
241+
func runClassicTunnel(sc *subcommandContext) error {
242+
return StartServer(sc.c, buildInfo, nil, sc.log)
243+
}
244+
241245
func routeFromFlag(c *cli.Context) (route cfapi.HostnameRoute, ok bool) {
242246
if hostname := c.String("hostname"); hostname != "" {
243247
if lbPool := c.String("lb-pool"); lbPool != "" {
@@ -346,6 +350,14 @@ func StartServer(
346350
return waitToShutdown(&wg, cancel, errC, graceShutdownC, 0, log)
347351
}
348352

353+
url := c.String("url")
354+
hostname := c.String("hostname")
355+
if url == hostname && url != "" && hostname != "" {
356+
errText := "hostname and url shouldn't match. See --help for more information"
357+
log.Error().Msg(errText)
358+
return fmt.Errorf(errText)
359+
}
360+
349361
logTransport := logger.CreateTransportLoggerFromContext(c, logger.EnableTerminalLog)
350362

351363
observer := connection.NewObserver(log, logTransport)

cmd/cloudflared/tunnel/configuration.go

Lines changed: 96 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/cloudflare/cloudflared/supervisor"
3333
"github.com/cloudflare/cloudflared/tlsconfig"
3434
tunnelpogs "github.com/cloudflare/cloudflared/tunnelrpc/pogs"
35+
"github.com/cloudflare/cloudflared/validation"
3536
)
3637

3738
const LogFieldOriginCertPath = "originCertPath"
@@ -42,6 +43,8 @@ var (
4243
serviceUrl = developerPortal + "/reference/service/"
4344
argumentsUrl = developerPortal + "/reference/arguments/"
4445

46+
LogFieldHostname = "hostname"
47+
4548
secretFlags = [2]*altsrc.StringFlag{credentialsContentsFlag, tunnelTokenFlag}
4649
defaultFeatures = []string{supervisor.FeatureAllowRemoteConfig, supervisor.FeatureSerializedHeaders, supervisor.FeatureDatagramV2, supervisor.FeatureQUICSupportEOF}
4750

@@ -124,7 +127,7 @@ func isSecretEnvVar(key string) bool {
124127
}
125128

126129
func dnsProxyStandAlone(c *cli.Context, namedTunnel *connection.NamedTunnelProperties) bool {
127-
return c.IsSet("proxy-dns") && (!c.IsSet("tag") && !c.IsSet("hello-world") && namedTunnel == nil)
130+
return c.IsSet("proxy-dns") && (!c.IsSet("hostname") && !c.IsSet("tag") && !c.IsSet("hello-world") && namedTunnel == nil)
128131
}
129132

130133
func findOriginCert(originCertPath string, log *zerolog.Logger) (string, error) {
@@ -190,19 +193,37 @@ func prepareTunnelConfig(
190193
observer *connection.Observer,
191194
namedTunnel *connection.NamedTunnelProperties,
192195
) (*supervisor.TunnelConfig, *orchestration.Config, error) {
193-
clientID, err := uuid.NewRandom()
196+
isNamedTunnel := namedTunnel != nil
197+
198+
configHostname := c.String("hostname")
199+
hostname, err := validation.ValidateHostname(configHostname)
194200
if err != nil {
195-
return nil, nil, errors.Wrap(err, "can't generate connector UUID")
201+
log.Err(err).Str(LogFieldHostname, configHostname).Msg("Invalid hostname")
202+
return nil, nil, errors.Wrap(err, "Invalid hostname")
203+
}
204+
clientID := c.String("id")
205+
if !c.IsSet("id") {
206+
clientID, err = generateRandomClientID(log)
207+
if err != nil {
208+
return nil, nil, err
209+
}
196210
}
197-
log.Info().Msgf("Generated Connector ID: %s", clientID)
211+
198212
tags, err := NewTagSliceFromCLI(c.StringSlice("tag"))
199213
if err != nil {
200214
log.Err(err).Msg("Tag parse failure")
201215
return nil, nil, errors.Wrap(err, "Tag parse failure")
202216
}
203-
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID.String()})
217+
218+
tags = append(tags, tunnelpogs.Tag{Name: "ID", Value: clientID})
219+
220+
var (
221+
ingressRules ingress.Ingress
222+
classicTunnel *connection.ClassicTunnelProperties
223+
)
204224

205225
transportProtocol := c.String("protocol")
226+
206227
needPQ := c.Bool("post-quantum")
207228
if needPQ {
208229
if FipsEnabled {
@@ -217,52 +238,79 @@ func prepareTunnelConfig(
217238

218239
protocolFetcher := edgediscovery.ProtocolPercentage
219240

220-
features := append(c.StringSlice("features"), defaultFeatures...)
221-
if needPQ {
222-
features = append(features, supervisor.FeaturePostQuantum)
223-
}
224-
if c.IsSet(TunnelTokenFlag) {
225-
if transportProtocol == connection.AutoSelectFlag {
226-
protocolFetcher = func() (edgediscovery.ProtocolPercents, error) {
227-
// If the Tunnel is remotely managed and no protocol is set, we prefer QUIC, but still allow fall-back.
228-
preferQuic := []edgediscovery.ProtocolPercent{
229-
{
230-
Protocol: connection.QUIC.String(),
231-
Percentage: 100,
232-
},
233-
{
234-
Protocol: connection.HTTP2.String(),
235-
Percentage: 100,
236-
},
241+
cfg := config.GetConfiguration()
242+
if isNamedTunnel {
243+
clientUUID, err := uuid.NewRandom()
244+
if err != nil {
245+
return nil, nil, errors.Wrap(err, "can't generate connector UUID")
246+
}
247+
log.Info().Msgf("Generated Connector ID: %s", clientUUID)
248+
features := append(c.StringSlice("features"), defaultFeatures...)
249+
if needPQ {
250+
features = append(features, supervisor.FeaturePostQuantum)
251+
}
252+
if c.IsSet(TunnelTokenFlag) {
253+
if transportProtocol == connection.AutoSelectFlag {
254+
protocolFetcher = func() (edgediscovery.ProtocolPercents, error) {
255+
// If the Tunnel is remotely managed and no protocol is set, we prefer QUIC, but still allow fall-back.
256+
preferQuic := []edgediscovery.ProtocolPercent{
257+
{
258+
Protocol: connection.QUIC.String(),
259+
Percentage: 100,
260+
},
261+
{
262+
Protocol: connection.HTTP2.String(),
263+
Percentage: 100,
264+
},
265+
}
266+
return preferQuic, nil
237267
}
238-
return preferQuic, nil
239268
}
269+
log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API. Defaulting to protocol: quic")
240270
}
241-
log.Info().Msg("Will be fetching remotely managed configuration from Cloudflare API. Defaulting to protocol: quic")
242-
}
243-
namedTunnel.Client = tunnelpogs.ClientInfo{
244-
ClientID: clientID[:],
245-
Features: dedup(features),
246-
Version: info.Version(),
247-
Arch: info.OSArch(),
248-
}
249-
cfg := config.GetConfiguration()
250-
ingressRules, err := ingress.ParseIngress(cfg)
251-
if err != nil && err != ingress.ErrNoIngressRules {
252-
return nil, nil, err
253-
}
254-
// Only for quick tunnels will we attempt to parse the --url flag for a tunnel ingress rule
255-
if ingressRules.IsEmpty() && c.IsSet("url") && namedTunnel.QuickTunnelUrl != "" {
256-
ingressRules, err = ingress.NewSingleOrigin(c, true)
257-
if err != nil {
271+
namedTunnel.Client = tunnelpogs.ClientInfo{
272+
ClientID: clientUUID[:],
273+
Features: dedup(features),
274+
Version: info.Version(),
275+
Arch: info.OSArch(),
276+
}
277+
ingressRules, err = ingress.ParseIngress(cfg)
278+
if err != nil && err != ingress.ErrNoIngressRules {
258279
return nil, nil, err
259280
}
281+
if !ingressRules.IsEmpty() && c.IsSet("url") {
282+
return nil, nil, ingress.ErrURLIncompatibleWithIngress
283+
}
284+
} else {
285+
286+
originCertPath := c.String("origincert")
287+
originCertLog := log.With().
288+
Str(LogFieldOriginCertPath, originCertPath).
289+
Logger()
290+
291+
originCert, err := getOriginCert(originCertPath, &originCertLog)
292+
if err != nil {
293+
return nil, nil, errors.Wrap(err, "Error getting origin cert")
294+
}
295+
296+
classicTunnel = &connection.ClassicTunnelProperties{
297+
Hostname: hostname,
298+
OriginCert: originCert,
299+
// turn off use of reconnect token and auth refresh when using named tunnels
300+
UseReconnectToken: !isNamedTunnel && c.Bool("use-reconnect-token"),
301+
}
260302
}
303+
304+
// Convert single-origin configuration into multi-origin configuration.
261305
if ingressRules.IsEmpty() {
262-
return nil, nil, ingress.ErrNoIngressRules
306+
ingressRules, err = ingress.NewSingleOrigin(c, !isNamedTunnel)
307+
if err != nil {
308+
return nil, nil, err
309+
}
263310
}
264311

265-
protocolSelector, err := connection.NewProtocolSelector(transportProtocol, cfg.WarpRouting.Enabled, namedTunnel, protocolFetcher, supervisor.ResolveTTL, log, c.Bool("post-quantum"))
312+
warpRoutingEnabled := isWarpRoutingEnabled(cfg.WarpRouting, isNamedTunnel)
313+
protocolSelector, err := connection.NewProtocolSelector(transportProtocol, warpRoutingEnabled, namedTunnel, protocolFetcher, supervisor.ResolveTTL, log, c.Bool("post-quantum"))
266314
if err != nil {
267315
return nil, nil, err
268316
}
@@ -314,7 +362,7 @@ func prepareTunnelConfig(
314362
GracePeriod: gracePeriod,
315363
ReplaceExisting: c.Bool("force"),
316364
OSArch: info.OSArch(),
317-
ClientID: clientID.String(),
365+
ClientID: clientID,
318366
EdgeAddrs: c.StringSlice("edge"),
319367
Region: c.String("region"),
320368
EdgeIPVersion: edgeIPVersion,
@@ -331,6 +379,7 @@ func prepareTunnelConfig(
331379
Retries: uint(c.Int("retries")),
332380
RunFromTerminal: isRunningFromTerminal(),
333381
NamedTunnel: namedTunnel,
382+
ClassicTunnel: classicTunnel,
334383
MuxerConfig: muxerConfig,
335384
ProtocolSelector: protocolSelector,
336385
EdgeTLSConfigs: edgeTLSConfigs,
@@ -372,6 +421,10 @@ func gracePeriod(c *cli.Context) (time.Duration, error) {
372421
return period, nil
373422
}
374423

424+
func isWarpRoutingEnabled(warpConfig config.WarpRoutingConfig, isNamedTunnel bool) bool {
425+
return warpConfig.Enabled && isNamedTunnel
426+
}
427+
375428
func isRunningFromTerminal() bool {
376429
return terminal.IsTerminal(int(os.Stdout.Fd()))
377430
}

component-tests/test_proxy_dns.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ class TestProxyDns:
1212
def test_proxy_dns_with_named_tunnel(self, tmp_path, component_tests_config):
1313
run_test_scenario(tmp_path, component_tests_config, CfdModes.NAMED, run_proxy_dns=True)
1414

15+
def test_proxy_dns_with_classic_tunnel(self, tmp_path, component_tests_config):
16+
run_test_scenario(tmp_path, component_tests_config, CfdModes.CLASSIC, run_proxy_dns=True)
17+
1518
def test_proxy_dns_alone(self, tmp_path, component_tests_config):
1619
run_test_scenario(tmp_path, component_tests_config, CfdModes.PROXY_DNS, run_proxy_dns=True)
1720

1821
def test_named_tunnel_alone(self, tmp_path, component_tests_config):
1922
run_test_scenario(tmp_path, component_tests_config, CfdModes.NAMED, run_proxy_dns=False)
2023

24+
def test_classic_tunnel_alone(self, tmp_path, component_tests_config):
25+
run_test_scenario(tmp_path, component_tests_config, CfdModes.CLASSIC, run_proxy_dns=False)
26+
2127

2228
def run_test_scenario(tmp_path, component_tests_config, cfd_mode, run_proxy_dns):
2329
expect_proxy_dns = run_proxy_dns
@@ -27,6 +33,10 @@ def run_test_scenario(tmp_path, component_tests_config, cfd_mode, run_proxy_dns)
2733
expect_tunnel = True
2834
pre_args = ["tunnel"]
2935
args = ["run"]
36+
elif cfd_mode == CfdModes.CLASSIC:
37+
expect_tunnel = True
38+
pre_args = []
39+
args = []
3040
elif cfd_mode == CfdModes.PROXY_DNS:
3141
expect_proxy_dns = True
3242
pre_args = []

component-tests/test_reconnect.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ def test_named_reconnect(self, tmp_path, component_tests_config, protocol):
3333
# Repeat the test multiple times because some issues only occur after multiple reconnects
3434
self.assert_reconnect(config, cloudflared, 5)
3535

36+
def test_classic_reconnect(self, tmp_path, component_tests_config):
37+
extra_config = copy.copy(self.extra_config)
38+
extra_config["hello-world"] = True
39+
config = component_tests_config(additional_config=extra_config, cfd_mode=CfdModes.CLASSIC)
40+
with start_cloudflared(tmp_path, config, cfd_args=[], new_process=True, allow_input=True, capture_output=False) as cloudflared:
41+
self.assert_reconnect(config, cloudflared, 1)
42+
3643
def send_reconnect(self, cloudflared, secs):
3744
# Although it is recommended to use the Popen.communicate method, we cannot
3845
# use it because it blocks on reading stdout and stderr until EOF is reached

connection/h2mux.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,46 @@ func (h *h2muxConnection) ServeNamedTunnel(ctx context.Context, namedTunnel *Nam
123123
return err
124124
}
125125

126+
func (h *h2muxConnection) ServeClassicTunnel(ctx context.Context, classicTunnel *ClassicTunnelProperties, credentialManager CredentialManager, registrationOptions *tunnelpogs.RegistrationOptions, connectedFuse ConnectedFuse) error {
127+
errGroup, serveCtx := errgroup.WithContext(ctx)
128+
errGroup.Go(func() error {
129+
return h.serveMuxer(serveCtx)
130+
})
131+
132+
errGroup.Go(func() (err error) {
133+
defer func() {
134+
if err == nil {
135+
connectedFuse.Connected()
136+
}
137+
}()
138+
if classicTunnel.UseReconnectToken && connectedFuse.IsConnected() {
139+
err := h.reconnectTunnel(ctx, credentialManager, classicTunnel, registrationOptions)
140+
if err == nil {
141+
return nil
142+
}
143+
// log errors and proceed to RegisterTunnel
144+
h.observer.log.Err(err).
145+
Uint8(LogFieldConnIndex, h.connIndex).
146+
Msg("Couldn't reconnect connection. Re-registering it instead.")
147+
}
148+
return h.registerTunnel(ctx, credentialManager, classicTunnel, registrationOptions)
149+
})
150+
151+
errGroup.Go(func() error {
152+
h.controlLoop(serveCtx, connectedFuse, false)
153+
return nil
154+
})
155+
156+
err := errGroup.Wait()
157+
if err == errMuxerStopped {
158+
if h.stoppedGracefully {
159+
return nil
160+
}
161+
h.observer.log.Info().Uint8(LogFieldConnIndex, h.connIndex).Msg("Unexpected muxer shutdown")
162+
}
163+
return err
164+
}
165+
126166
func (h *h2muxConnection) serveMuxer(ctx context.Context) error {
127167
// All routines should stop when muxer finish serving. When muxer is shutdown
128168
// gracefully, it doesn't return an error, so we need to return errMuxerShutdown

ingress/ingress.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ type Ingress struct {
7171
}
7272

7373
// NewSingleOrigin constructs an Ingress set with only one rule, constructed from
74-
// CLI parameters for quick tunnels like --url or --no-chunked-encoding.
74+
// legacy CLI parameters like --url or --no-chunked-encoding.
7575
func NewSingleOrigin(c *cli.Context, allowURLFromArgs bool) (Ingress, error) {
76+
7677
service, err := parseSingleOriginService(c, allowURLFromArgs)
7778
if err != nil {
7879
return Ingress{}, err

0 commit comments

Comments
 (0)