ipn: [serve] warn that foreground funnel won't work if shields are up (#14685)
Some checks are pending
checklocks / checklocks (push) Waiting to run
CodeQL / Analyze (go) (push) Waiting to run
Dockerfile build / deploy (push) Waiting to run
CI / race-root-integration (1/4) (push) Waiting to run
CI / race-root-integration (2/4) (push) Waiting to run
CI / race-root-integration (3/4) (push) Waiting to run
CI / race-root-integration (4/4) (push) Waiting to run
CI / test (-coverprofile=/tmp/coverage.out, amd64) (push) Waiting to run
CI / test (-race, amd64, 1/3) (push) Waiting to run
CI / test (-race, amd64, 2/3) (push) Waiting to run
CI / test (-race, amd64, 3/3) (push) Waiting to run
CI / test (386) (push) Waiting to run
CI / windows (push) Waiting to run
CI / privileged (push) Waiting to run
CI / vm (push) Waiting to run
CI / race-build (push) Waiting to run
CI / cross (386, linux) (push) Waiting to run
CI / cross (amd64, darwin) (push) Waiting to run
CI / cross (amd64, freebsd) (push) Waiting to run
CI / cross (amd64, openbsd) (push) Waiting to run
CI / cross (amd64, windows) (push) Waiting to run
CI / cross (arm, 5, linux) (push) Waiting to run
CI / cross (arm, 7, linux) (push) Waiting to run
CI / cross (arm64, darwin) (push) Waiting to run
CI / cross (arm64, linux) (push) Waiting to run
CI / cross (arm64, windows) (push) Waiting to run
CI / cross (loong64, linux) (push) Waiting to run
CI / ios (push) Waiting to run
CI / crossmin (amd64, illumos) (push) Waiting to run
CI / crossmin (amd64, plan9) (push) Waiting to run
CI / crossmin (amd64, solaris) (push) Waiting to run
CI / crossmin (ppc64, aix) (push) Waiting to run
CI / android (push) Waiting to run
CI / wasm (push) Waiting to run
CI / tailscale_go (push) Waiting to run
CI / fuzz (push) Waiting to run
CI / depaware (push) Waiting to run
CI / go_generate (push) Waiting to run
CI / go_mod_tidy (push) Waiting to run
CI / licenses (push) Waiting to run
CI / staticcheck (386, windows) (push) Waiting to run
CI / staticcheck (amd64, darwin) (push) Waiting to run
CI / staticcheck (amd64, linux) (push) Waiting to run
CI / staticcheck (amd64, windows) (push) Waiting to run
CI / notify_slack (push) Blocked by required conditions
CI / check_mergeability (push) Blocked by required conditions

We throw error early with a warning if users attempt to enable background funnel
for a node that does not allow incoming connections
(shields up), but if it done in foreground mode, we just silently fail
(the funnel command succeeds, but the connections are not allowed).
This change makes sure that we also error early in foreground mode.

Updates tailscale/tailscale#11049

Signed-off-by: Irbe Krumina <irbe@tailscale.com>
This commit is contained in:
Irbe Krumina 2025-01-19 19:00:21 +00:00 committed by GitHub
parent c79b736a85
commit 6c30840cac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 97 additions and 8 deletions

View File

@ -63,12 +63,12 @@ type ServeConfig struct {
// traffic is allowed, from trusted ingress peers. // traffic is allowed, from trusted ingress peers.
AllowFunnel map[HostPort]bool `json:",omitempty"` AllowFunnel map[HostPort]bool `json:",omitempty"`
// Foreground is a map of an IPN Bus session ID to an alternate foreground // Foreground is a map of an IPN Bus session ID to an alternate foreground serve config that's valid for the
// serve config that's valid for the life of that WatchIPNBus session ID. // life of that WatchIPNBus session ID. This allows the config to specify ephemeral configs that are used
// This. This allows the config to specify ephemeral configs that are // in the CLI's foreground mode to ensure ungraceful shutdowns of either the client or the LocalBackend does not
// used in the CLI's foreground mode to ensure ungraceful shutdowns // expose ports that users are not aware of. In practice this contains any serve config set via 'tailscale
// of either the client or the LocalBackend does not expose ports // serve' command run without the '--bg' flag. ServeConfig contained by Foreground is not expected itself to contain
// that users are not aware of. // another Foreground block.
Foreground map[string]*ServeConfig `json:",omitempty"` Foreground map[string]*ServeConfig `json:",omitempty"`
// ETag is the checksum of the serve config that's populated // ETag is the checksum of the serve config that's populated
@ -389,8 +389,7 @@ func (sc *ServeConfig) RemoveTCPForwarding(port uint16) {
// View version of ServeConfig.IsFunnelOn. // View version of ServeConfig.IsFunnelOn.
func (v ServeConfigView) IsFunnelOn() bool { return v.ж.IsFunnelOn() } func (v ServeConfigView) IsFunnelOn() bool { return v.ж.IsFunnelOn() }
// IsFunnelOn reports whether if ServeConfig is currently allowing funnel // IsFunnelOn reports whether any funnel endpoint is currently enabled for this node.
// traffic for any host:port.
func (sc *ServeConfig) IsFunnelOn() bool { func (sc *ServeConfig) IsFunnelOn() bool {
if sc == nil { if sc == nil {
return false return false
@ -400,6 +399,11 @@ func (sc *ServeConfig) IsFunnelOn() bool {
return true return true
} }
} }
for _, conf := range sc.Foreground {
if conf.IsFunnelOn() {
return true
}
}
return false return false
} }

View File

@ -182,3 +182,88 @@ func TestExpandProxyTargetDev(t *testing.T) {
}) })
} }
} }
func TestIsFunnelOn(t *testing.T) {
tests := []struct {
name string
sc *ServeConfig
want bool
}{
{
name: "nil_config",
},
{
name: "empty_config",
sc: &ServeConfig{},
},
{
name: "funnel_enabled_in_background",
sc: &ServeConfig{
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:443": true,
},
},
want: true,
},
{
name: "funnel_disabled_in_background",
sc: &ServeConfig{
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:443": false,
},
},
},
{
name: "funnel_enabled_in_foreground",
sc: &ServeConfig{
Foreground: map[string]*ServeConfig{
"abc123": {
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:443": true,
},
},
},
},
want: true,
},
{
name: "funnel_disabled_in_both",
sc: &ServeConfig{
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:443": false,
},
Foreground: map[string]*ServeConfig{
"abc123": {
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:8443": false,
},
},
},
},
},
{
name: "funnel_enabled_in_both",
sc: &ServeConfig{
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:443": true,
},
Foreground: map[string]*ServeConfig{
"abc123": {
AllowFunnel: map[HostPort]bool{
"tailnet.xyz:8443": true,
},
},
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.sc.IsFunnelOn(); got != tt.want {
t.Errorf("ServeConfig.IsFunnelOn() = %v, want %v", got, tt.want)
}
})
}
}