health: warn about reverse path filtering and exit nodes

When reverse path filtering is in strict mode on Linux, using an exit
node blocks all network connectivity. This change adds a warning about
this to `tailscale status` and the logs.

Example in `tailscale status`:

```
- not connected to home DERP region 22
- The following issues on your machine will likely make usage of exit nodes impossible: [interface "eth0" has strict reverse-path filtering enabled], please set rp_filter=2 instead of rp_filter=1; see https://github.com/tailscale/tailscale/issues/3310
```

Example in the logs:
```
2024/02/21 21:17:07 health("overall"): error: multiple errors:
	not in map poll
	The following issues on your machine will likely make usage of exit nodes impossible: [interface "eth0" has strict reverse-path filtering enabled], please set rp_filter=2 instead of rp_filter=1; see https://github.com/tailscale/tailscale/issues/3310
```

Updates #3310

Signed-off-by: Anton Tolchanov <anton@tailscale.com>
This commit is contained in:
Anton Tolchanov
2024-01-03 00:23:58 +00:00
committed by Anton Tolchanov
parent 7ef1fb113d
commit 8cc5c51888
6 changed files with 69 additions and 19 deletions

View File

@@ -153,7 +153,7 @@ func CheckIPForwarding(routes []netip.Prefix, state *interfaces.State) (warn, er
// This function returns an error if it is unable to determine whether reverse
// path filtering is enabled, or a warning describing configuration issues if
// reverse path fitering is non-functional or partly functional.
func CheckReversePathFiltering(routes []netip.Prefix, state *interfaces.State) (warn []string, err error) {
func CheckReversePathFiltering(state *interfaces.State) (warn []string, err error) {
if runtime.GOOS != "linux" {
return nil, nil
}
@@ -166,12 +166,6 @@ func CheckReversePathFiltering(routes []netip.Prefix, state *interfaces.State) (
}
}
// Reverse path filtering as a syscall is only implemented on Linux for IPv4.
wantV4, _ := protocolsRequiredForForwarding(routes, state)
if !wantV4 {
return nil, nil
}
// The kernel uses the maximum value for rp_filter between the 'all'
// setting and each per-interface config, so we need to fetch both.
allSetting, err := reversePathFilterValueLinux("all")
@@ -205,7 +199,7 @@ func CheckReversePathFiltering(routes []netip.Prefix, state *interfaces.State) (
iSetting = allSetting
}
if iSetting == filtStrict {
warn = append(warn, fmt.Sprintf("Interface %q has strict reverse-path filtering enabled", iface.Name))
warn = append(warn, fmt.Sprintf("interface %q has strict reverse-path filtering enabled", iface.Name))
}
}
return warn, nil

View File

@@ -6,7 +6,6 @@ package netutil
import (
"io"
"net"
"net/netip"
"runtime"
"testing"
)
@@ -71,9 +70,7 @@ func TestCheckReversePathFiltering(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("skipping on %s", runtime.GOOS)
}
warn, err := CheckReversePathFiltering([]netip.Prefix{
netip.MustParsePrefix("192.168.1.1/24"),
}, nil)
warn, err := CheckReversePathFiltering(nil)
t.Logf("err: %v", err)
t.Logf("warnings: %v", warn)
}