Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
//go:build linux
package linuxfw
import ( "errors" "os/exec"
"tailscale.com/envknob" "tailscale.com/feature" "tailscale.com/feature/buildfeatures" "tailscale.com/hostinfo" "tailscale.com/types/logger" "tailscale.com/version/distro")
func detectFirewallMode(logf logger.Logf, prefHint string) FirewallMode { if distro.Get() == distro.Gokrazy { // Reduce startup logging on gokrazy. There's no way to do iptables on // gokrazy anyway. logf("GoKrazy should use nftables.") hostinfo.SetFirewallMode("nft-gokrazy") return FirewallModeNfTables } if distro.Get() == distro.JetKVM { // JetKVM doesn't have iptables. hostinfo.SetFirewallMode("nft-jetkvm") return FirewallModeNfTables }
mode := envknob.String("TS_DEBUG_FIREWALL_MODE") // If the envknob isn't set, fall back to the pref suggested by c2n or // nodeattrs. if mode == "" { mode = prefHint logf("using firewall mode pref %s", prefHint) } else if prefHint != "" { logf("TS_DEBUG_FIREWALL_MODE set, overriding firewall mode from %s to %s", prefHint, mode) }
var det linuxFWDetector if mode == "" { // We have no preference, so check if `iptables` is even available. if buildfeatures.HasIPTables { _, err := det.iptDetect() if err != nil && errors.Is(err, exec.ErrNotFound) { logf("iptables not found: %v; falling back to nftables", err) mode = "nftables" } } }
// We now use iptables as default and have "auto" and "nftables" as // options for people to test further. switch mode { case "auto": return pickFirewallModeFromInstalledRules(logf, det) case "nftables": hostinfo.SetFirewallMode("nft-forced") return FirewallModeNfTables case "iptables": hostinfo.SetFirewallMode("ipt-forced") return FirewallModeIPTables } if buildfeatures.HasIPTables { logf("default choosing iptables") hostinfo.SetFirewallMode("ipt-default") return FirewallModeIPTables } logf("default choosing nftables") hostinfo.SetFirewallMode("nft-default") return FirewallModeNfTables}
// tableDetector abstracts helpers to detect the firewall mode.// It is implemented for testing purposes.type tableDetector interface { iptDetect() (int, error) nftDetect() (int, error)}
type linuxFWDetector struct{}
// iptDetect returns the number of iptables rules in the current namespace.func (ld linuxFWDetector) iptDetect() (int, error) { return detectIptables()}
var hookDetectNetfilter feature.Hook[func() (int, error)]
// ErrUnsupported is the error returned from all functions on non-Linux// platforms.var ErrUnsupported = errors.New("linuxfw:unsupported")
// nftDetect returns the number of nftables rules in the current namespace.func (ld linuxFWDetector) nftDetect() (int, error) { if f, ok := hookDetectNetfilter.GetOk(); ok { return f() } return 0, ErrUnsupported}
// pickFirewallModeFromInstalledRules returns the firewall mode to use based on// the environment and the system's capabilities.func pickFirewallModeFromInstalledRules(logf logger.Logf, det tableDetector) FirewallMode { if !buildfeatures.HasIPTables { hostinfo.SetFirewallMode("nft-noipt") return FirewallModeNfTables } if distro.Get() == distro.Gokrazy { // Reduce startup logging on gokrazy. There's no way to do iptables on // gokrazy anyway. return FirewallModeNfTables }
iptAva, nftAva := true, true iptRuleCount, err := det.iptDetect() if err != nil { logf("detect iptables rule: %v", err) iptAva = false } nftRuleCount, err := det.nftDetect() if err != nil { logf("detect nftables rule: %v", err) nftAva = false } logf("nftables rule count: %d, iptables rule count: %d", nftRuleCount, iptRuleCount) switch { case nftRuleCount > 0 && iptRuleCount == 0: logf("nftables is currently in use") hostinfo.SetFirewallMode("nft-inuse") return FirewallModeNfTables case iptRuleCount > 0 && nftRuleCount == 0: logf("iptables is currently in use") hostinfo.SetFirewallMode("ipt-inuse") return FirewallModeIPTables case nftAva: // if both iptables and nftables are available but // neither/both are currently used, use nftables. logf("nftables is available") hostinfo.SetFirewallMode("nft") return FirewallModeNfTables case iptAva: logf("iptables is available") hostinfo.SetFirewallMode("ipt") return FirewallModeIPTables default: // if neither iptables nor nftables are available, use iptablesRunner as a dummy // runner which exists but won't do anything. Creating iptablesRunner errors only // if the iptables command is missing or doesn’t support "--version", as long as it // can determine a version then it’ll carry on. hostinfo.SetFirewallMode("ipt-fb") return FirewallModeIPTables }}