detector.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux
  4. package linuxfw
  5. import (
  6. "tailscale.com/envknob"
  7. "tailscale.com/hostinfo"
  8. "tailscale.com/types/logger"
  9. "tailscale.com/version/distro"
  10. )
  11. func detectFirewallMode(logf logger.Logf, prefHint string) FirewallMode {
  12. if distro.Get() == distro.Gokrazy {
  13. // Reduce startup logging on gokrazy. There's no way to do iptables on
  14. // gokrazy anyway.
  15. logf("GoKrazy should use nftables.")
  16. hostinfo.SetFirewallMode("nft-gokrazy")
  17. return FirewallModeNfTables
  18. }
  19. mode := envknob.String("TS_DEBUG_FIREWALL_MODE")
  20. // If the envknob isn't set, fall back to the pref suggested by c2n or
  21. // nodeattrs.
  22. if mode == "" {
  23. mode = prefHint
  24. logf("using firewall mode pref %s", prefHint)
  25. } else if prefHint != "" {
  26. logf("TS_DEBUG_FIREWALL_MODE set, overriding firewall mode from %s to %s", prefHint, mode)
  27. }
  28. // We now use iptables as default and have "auto" and "nftables" as
  29. // options for people to test further.
  30. switch mode {
  31. case "auto":
  32. return pickFirewallModeFromInstalledRules(logf, linuxFWDetector{})
  33. case "nftables":
  34. hostinfo.SetFirewallMode("nft-forced")
  35. return FirewallModeNfTables
  36. case "iptables":
  37. hostinfo.SetFirewallMode("ipt-forced")
  38. default:
  39. logf("default choosing iptables")
  40. hostinfo.SetFirewallMode("ipt-default")
  41. }
  42. return FirewallModeIPTables
  43. }
  44. // tableDetector abstracts helpers to detect the firewall mode.
  45. // It is implemented for testing purposes.
  46. type tableDetector interface {
  47. iptDetect() (int, error)
  48. nftDetect() (int, error)
  49. }
  50. type linuxFWDetector struct{}
  51. // iptDetect returns the number of iptables rules in the current namespace.
  52. func (l linuxFWDetector) iptDetect() (int, error) {
  53. return detectIptables()
  54. }
  55. // nftDetect returns the number of nftables rules in the current namespace.
  56. func (l linuxFWDetector) nftDetect() (int, error) {
  57. return detectNetfilter()
  58. }
  59. // pickFirewallModeFromInstalledRules returns the firewall mode to use based on
  60. // the environment and the system's capabilities.
  61. func pickFirewallModeFromInstalledRules(logf logger.Logf, det tableDetector) FirewallMode {
  62. if distro.Get() == distro.Gokrazy {
  63. // Reduce startup logging on gokrazy. There's no way to do iptables on
  64. // gokrazy anyway.
  65. return FirewallModeNfTables
  66. }
  67. iptAva, nftAva := true, true
  68. iptRuleCount, err := det.iptDetect()
  69. if err != nil {
  70. logf("detect iptables rule: %v", err)
  71. iptAva = false
  72. }
  73. nftRuleCount, err := det.nftDetect()
  74. if err != nil {
  75. logf("detect nftables rule: %v", err)
  76. nftAva = false
  77. }
  78. logf("nftables rule count: %d, iptables rule count: %d", nftRuleCount, iptRuleCount)
  79. switch {
  80. case nftRuleCount > 0 && iptRuleCount == 0:
  81. logf("nftables is currently in use")
  82. hostinfo.SetFirewallMode("nft-inuse")
  83. return FirewallModeNfTables
  84. case iptRuleCount > 0 && nftRuleCount == 0:
  85. logf("iptables is currently in use")
  86. hostinfo.SetFirewallMode("ipt-inuse")
  87. return FirewallModeIPTables
  88. case nftAva:
  89. // if both iptables and nftables are available but
  90. // neither/both are currently used, use nftables.
  91. logf("nftables is available")
  92. hostinfo.SetFirewallMode("nft")
  93. return FirewallModeNfTables
  94. case iptAva:
  95. logf("iptables is available")
  96. hostinfo.SetFirewallMode("ipt")
  97. return FirewallModeIPTables
  98. default:
  99. // if neither iptables nor nftables are available, use iptablesRunner as a dummy
  100. // runner which exists but won't do anything. Creating iptablesRunner errors only
  101. // if the iptables command is missing or doesn’t support "--version", as long as it
  102. // can determine a version then it’ll carry on.
  103. hostinfo.SetFirewallMode("ipt-fb")
  104. return FirewallModeIPTables
  105. }
  106. }