tailcfg: Add FirewallMode to NetInfo to record wether host using iptables or nftables

To record wether user is using iptables or nftables after we add support to nftables on linux, we
are adding a field FirewallMode to NetInfo in HostInfo to reflect what firewall mode the host is
running, and form metrics. The information is gained from a global constant in hostinfo.go. We
set it when selection heuristic made the decision, and magicsock reports this to control.

Updates: tailscale/corp#13943
Signed-off-by: KevinLiang10 <kevinliang@tailscale.com>
This commit is contained in:
KevinLiang10 2023-08-15 21:52:04 +00:00 committed by KevinLiang10
parent 95d776bd8c
commit 7ed3681cbe
7 changed files with 33 additions and 3 deletions

View File

@ -171,6 +171,7 @@ func GetEnvType() EnvType {
desktopAtomic atomic.Value // of opt.Bool
packagingType atomic.Value // of string
appType atomic.Value // of string
firewallMode atomic.Value // of string
)
// SetPushDeviceToken sets the device token for use in Hostinfo updates.
@ -182,6 +183,9 @@ func SetDeviceModel(model string) { deviceModelAtomic.Store(model) }
// SetOSVersion sets the OS version.
func SetOSVersion(v string) { osVersionAtomic.Store(v) }
// SetFirewallMode sets the firewall mode for the app.
func SetFirewallMode(v string) { firewallMode.Store(v) }
// SetPackage sets the packaging type for the app.
//
// As of 2022-03-25, this is used by Android ("nogoogle" for the
@ -203,6 +207,13 @@ func pushDeviceToken() string {
return s
}
// FirewallMode returns the firewall mode for the app.
// It is empty if unset.
func FirewallMode() string {
s, _ := firewallMode.Load().(string)
return s
}
func desktop() (ret opt.Bool) {
if runtime.GOOS != "linux" {
return opt.Bool("")

View File

@ -734,6 +734,11 @@ type NetInfo struct {
// the control plane.
DERPLatency map[string]float64 `json:",omitempty"`
// FirewallMode is the current firewall utility in use by router (iptables, nftables).
// FirewallMode ipt means iptables, nft means nftables. When it's empty user is not using
// our netfilter runners to manage firewall rules.
FirewallMode string `json:",omitempty"`
// Update BasicallyEqual when adding fields.
}
@ -741,10 +746,10 @@ func (ni *NetInfo) String() string {
if ni == nil {
return "NetInfo(nil)"
}
return fmt.Sprintf("NetInfo{varies=%v hairpin=%v ipv6=%v ipv6os=%v udp=%v icmpv4=%v derp=#%v portmap=%v link=%q}",
return fmt.Sprintf("NetInfo{varies=%v hairpin=%v ipv6=%v ipv6os=%v udp=%v icmpv4=%v derp=#%v portmap=%v link=%q firewallmode=%q}",
ni.MappingVariesByDestIP, ni.HairPinning, ni.WorkingIPv6,
ni.OSHasIPv6, ni.WorkingUDP, ni.WorkingICMPv4,
ni.PreferredDERP, ni.portMapSummary(), ni.LinkType)
ni.PreferredDERP, ni.portMapSummary(), ni.LinkType, ni.FirewallMode)
}
func (ni *NetInfo) portMapSummary() string {
@ -792,7 +797,8 @@ func (ni *NetInfo) BasicallyEqual(ni2 *NetInfo) bool {
ni.PMP == ni2.PMP &&
ni.PCP == ni2.PCP &&
ni.PreferredDERP == ni2.PreferredDERP &&
ni.LinkType == ni2.LinkType
ni.LinkType == ni2.LinkType &&
ni.FirewallMode == ni2.FirewallMode
}
// Equal reports whether h and h2 are equal.

View File

@ -195,6 +195,7 @@ func (src *NetInfo) Clone() *NetInfo {
PreferredDERP int
LinkType string
DERPLatency map[string]float64
FirewallMode string
}{})
// Clone makes a deep copy of Login.

View File

@ -571,6 +571,7 @@ func TestNetInfoFields(t *testing.T) {
"PreferredDERP",
"LinkType",
"DERPLatency",
"FirewallMode",
}
if have := fieldsOf(reflect.TypeOf(NetInfo{})); !reflect.DeepEqual(have, handled) {
t.Errorf("NetInfo.Clone/BasicallyEqually check might be out of sync\nfields: %q\nhandled: %q\n",

View File

@ -408,6 +408,7 @@ func (v NetInfoView) PreferredDERP() int { return v.ж.PreferredDER
func (v NetInfoView) LinkType() string { return v.ж.LinkType }
func (v NetInfoView) DERPLatency() views.Map[string, float64] { return views.MapOf(v.ж.DERPLatency) }
func (v NetInfoView) FirewallMode() string { return v.ж.FirewallMode }
func (v NetInfoView) String() string { return v.ж.String() }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
@ -425,6 +426,7 @@ func (v NetInfoView) String() string { return v.ж.Stri
PreferredDERP int
LinkType string
DERPLatency map[string]float64
FirewallMode string
}{})
// View returns a readonly view of Login.

View File

@ -640,6 +640,7 @@ func (c *Conn) updateNetInfo(ctx context.Context) (*netcheck.Report, error) {
if !c.setNearestDERP(ni.PreferredDERP) {
ni.PreferredDERP = 0
}
ni.FirewallMode = hostinfo.FirewallMode()
c.callNetInfoCallback(ni)
return report, nil

View File

@ -22,6 +22,7 @@
"golang.org/x/sys/unix"
"golang.org/x/time/rate"
"tailscale.com/envknob"
"tailscale.com/hostinfo"
"tailscale.com/net/netmon"
"tailscale.com/types/logger"
"tailscale.com/types/preftype"
@ -97,29 +98,36 @@ func chooseFireWallMode(logf logger.Logf, det tableDetector) linuxfw.FirewallMod
case envknob.String("TS_DEBUG_FIREWALL_MODE") == "nftables":
// TODO(KevinLiang10): Updates to a flag
logf("router: envknob TS_DEBUG_FIREWALL_MODE=nftables set")
hostinfo.SetFirewallMode("nft-forced")
return linuxfw.FirewallModeNfTables
case envknob.String("TS_DEBUG_FIREWALL_MODE") == "iptables":
logf("router: envknob TS_DEBUG_FIREWALL_MODE=iptables set")
hostinfo.SetFirewallMode("ipt-forced")
return linuxfw.FirewallModeIPTables
case nftRuleCount > 0 && iptRuleCount == 0:
logf("router: nftables is currently in use")
hostinfo.SetFirewallMode("nft-inuse")
return linuxfw.FirewallModeNfTables
case iptRuleCount > 0 && nftRuleCount == 0:
logf("router: iptables is currently in use")
hostinfo.SetFirewallMode("ipt-inuse")
return linuxfw.FirewallModeIPTables
case nftAva:
// if both iptables and nftables are available but
// neither/both are currently used, use nftables.
logf("router: nftables is available")
hostinfo.SetFirewallMode("nft")
return linuxfw.FirewallModeNfTables
case iptAva:
logf("router: iptables is available")
hostinfo.SetFirewallMode("ipt")
return linuxfw.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 doesnt support "--version", as long as it
// can determine a version then itll carry on.
hostinfo.SetFirewallMode("ipt-fb")
return linuxfw.FirewallModeIPTables
}
}