From 9811ac5f3451860a9f52796241f66e44d4e9f8e3 Mon Sep 17 00:00:00 2001 From: Felix Engelmann Date: Tue, 29 Jul 2025 14:36:22 -0400 Subject: [PATCH] added flag to not accept remote config --- cmd/cloudflared/flags/flags.go | 3 ++ cmd/cloudflared/tunnel/cmd.go | 8 +++++ cmd/cloudflared/tunnel/configuration.go | 1 + orchestration/orchestrator.go | 33 ++++++++++++------- orchestration/orchestrator_test.go | 42 +++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/cmd/cloudflared/flags/flags.go b/cmd/cloudflared/flags/flags.go index 1f8f2855..bd3d5bb3 100644 --- a/cmd/cloudflared/flags/flags.go +++ b/cmd/cloudflared/flags/flags.go @@ -123,6 +123,9 @@ const ( // NoAutoUpdate is the command line flag to disable cloudflared from checking for updates NoAutoUpdate = "no-autoupdate" + // NoConfigUpdate is the command line flag to disable cloudflared from accepting remote ingress configuration + NoConfigUpdate = "no-configupdate" + // LogLevel is the command line flag for the cloudflared logging level LogLevel = "loglevel" diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 925333a4..ccb7b1b7 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -77,6 +77,7 @@ var ( "config", cfdflags.AutoUpdateFreq, cfdflags.NoAutoUpdate, + cfdflags.NoConfigUpdate, cfdflags.Metrics, "pidfile", "url", @@ -921,6 +922,13 @@ func configureCloudflaredFlags(shouldHide bool) []cli.Flag { Value: false, Hidden: shouldHide, }), + altsrc.NewBoolFlag(&cli.BoolFlag{ + Name: cfdflags.NoConfigUpdate, + Usage: "Disable accepting new ingress configurations from remote.", + EnvVars: []string{"NO_CONFIGUPDATE"}, + Value: false, + Hidden: shouldHide, + }), altsrc.NewStringFlag(&cli.StringFlag{ Name: cfdflags.Metrics, Value: metrics.GetMetricsDefaultAddress(metrics.Runtime), diff --git a/cmd/cloudflared/tunnel/configuration.go b/cmd/cloudflared/tunnel/configuration.go index 63f78426..9e8db8e8 100644 --- a/cmd/cloudflared/tunnel/configuration.go +++ b/cmd/cloudflared/tunnel/configuration.go @@ -45,6 +45,7 @@ var ( configFlags = []string{ flags.AutoUpdateFreq, flags.NoAutoUpdate, + flags.NoConfigUpdate, flags.Retries, flags.Protocol, flags.LogLevel, diff --git a/orchestration/orchestrator.go b/orchestration/orchestrator.go index 9840bd36..b2f6fdce 100644 --- a/orchestration/orchestrator.go +++ b/orchestration/orchestrator.go @@ -99,24 +99,33 @@ func (o *Orchestrator) UpdateConfig(version int32, config []byte) *pogs.UpdateCo Err: err, } } + if val, ok := o.config.ConfigurationFlags[flags.NoConfigUpdate]; ok && val == "true" { + body, err := json.Marshal(o.config.Ingress) + if err != nil { + o.log.Err(err) + } + o.log.Info().Str("acting config", string(body)).Msg("Update disabled, keeping old config") + } else { + if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting); err != nil { + o.log.Err(err). + Int32("version", version). + Str("config", string(config)). + Msgf("Failed to update ingress") + return &pogs.UpdateConfigurationResponse{ + LastAppliedVersion: o.currentVersion, + Err: err, + } + } - if err := o.updateIngress(newConf.Ingress, newConf.WarpRouting); err != nil { - o.log.Err(err). + o.log.Info(). Int32("version", version). Str("config", string(config)). - Msgf("Failed to update ingress") - return &pogs.UpdateConfigurationResponse{ - LastAppliedVersion: o.currentVersion, - Err: err, - } + Msg("Updated to new configuration") + configVersion.Set(float64(version)) } + o.currentVersion = version - o.log.Info(). - Int32("version", version). - Str("config", string(config)). - Msg("Updated to new configuration") - configVersion.Set(float64(version)) return &pogs.UpdateConfigurationResponse{ LastAppliedVersion: o.currentVersion, } diff --git a/orchestration/orchestrator_test.go b/orchestration/orchestrator_test.go index 7a14b2d4..183cfd38 100644 --- a/orchestration/orchestrator_test.go +++ b/orchestration/orchestrator_test.go @@ -218,6 +218,48 @@ func TestUpdateConfiguration_FromMigration(t *testing.T) { require.Len(t, orchestrator.config.Ingress.Rules, 1) } +// Validates that a new version 0 will be applied if the configuration is loaded locally. +// This will happen when a locally managed tunnel is migrated to remote configuration and receives its first configuration. +func TestNoUpdateConfiguration(t *testing.T) { + originDialer := ingress.NewOriginDialer(ingress.OriginConfig{ + DefaultDialer: testDefaultDialer, + TCPWriteTimeout: 1 * time.Second, + }, &testLogger) + flags := map[string]string{ + "no-configupdate":"true", + } + initConfig := &Config{ + Ingress: &ingress.Ingress{}, + OriginDialerService: originDialer, + ConfigurationFlags: flags, + + } + orchestrator, err := NewOrchestrator(t.Context(), initConfig, testTags, []ingress.Rule{}, &testLogger) + require.NoError(t, err) + initOriginProxy, err := orchestrator.GetOriginProxy() + require.NoError(t, err) + require.Implements(t, (*connection.OriginProxy)(nil), initOriginProxy) + + configJSONV2 := []byte(` +{ + "ingress": [ + { + "hostname": "jira.tunnel.org", + "service": "http://192.16.19.1" + }, + { + "service": "http_status:404" + } + ], + "warp-routing": { + } +} +`) + updateWithValidation(t, orchestrator, 0, configJSONV2) + require.Len(t, orchestrator.config.Ingress.Rules, 1) +} + + // Validates that the default ingress rule will be set if there is no rule provided from the remote. func TestUpdateConfiguration_WithoutIngressRule(t *testing.T) { originDialer := ingress.NewOriginDialer(ingress.OriginConfig{