detector.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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) 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. envMode := envknob.String("TS_DEBUG_FIREWALL_MODE")
  20. // We now use iptables as default and have "auto" and "nftables" as
  21. // options for people to test further.
  22. switch envMode {
  23. case "auto":
  24. return pickFirewallModeFromInstalledRules(logf, linuxFWDetector{})
  25. case "nftables":
  26. logf("envknob TS_DEBUG_FIREWALL_MODE=nftables set")
  27. hostinfo.SetFirewallMode("nft-forced")
  28. return FirewallModeNfTables
  29. case "iptables":
  30. logf("envknob TS_DEBUG_FIREWALL_MODE=iptables set")
  31. hostinfo.SetFirewallMode("ipt-forced")
  32. default:
  33. logf("default choosing iptables")
  34. hostinfo.SetFirewallMode("ipt-default")
  35. }
  36. return FirewallModeIPTables
  37. }
  38. // tableDetector abstracts helpers to detect the firewall mode.
  39. // It is implemented for testing purposes.
  40. type tableDetector interface {
  41. iptDetect() (int, error)
  42. nftDetect() (int, error)
  43. }
  44. type linuxFWDetector struct{}
  45. // iptDetect returns the number of iptables rules in the current namespace.
  46. func (l linuxFWDetector) iptDetect() (int, error) {
  47. return detectIptables()
  48. }
  49. // nftDetect returns the number of nftables rules in the current namespace.
  50. func (l linuxFWDetector) nftDetect() (int, error) {
  51. return detectNetfilter()
  52. }
  53. // pickFirewallModeFromInstalledRules returns the firewall mode to use based on
  54. // the environment and the system's capabilities.
  55. func pickFirewallModeFromInstalledRules(logf logger.Logf, det tableDetector) FirewallMode {
  56. if distro.Get() == distro.Gokrazy {
  57. // Reduce startup logging on gokrazy. There's no way to do iptables on
  58. // gokrazy anyway.
  59. return FirewallModeNfTables
  60. }
  61. iptAva, nftAva := true, true
  62. iptRuleCount, err := det.iptDetect()
  63. if err != nil {
  64. logf("detect iptables rule: %v", err)
  65. iptAva = false
  66. }
  67. nftRuleCount, err := det.nftDetect()
  68. if err != nil {
  69. logf("detect nftables rule: %v", err)
  70. nftAva = false
  71. }
  72. logf("nftables rule count: %d, iptables rule count: %d", nftRuleCount, iptRuleCount)
  73. switch {
  74. case nftRuleCount > 0 && iptRuleCount == 0:
  75. logf("nftables is currently in use")
  76. hostinfo.SetFirewallMode("nft-inuse")
  77. return FirewallModeNfTables
  78. case iptRuleCount > 0 && nftRuleCount == 0:
  79. logf("iptables is currently in use")
  80. hostinfo.SetFirewallMode("ipt-inuse")
  81. return FirewallModeIPTables
  82. case nftAva:
  83. // if both iptables and nftables are available but
  84. // neither/both are currently used, use nftables.
  85. logf("nftables is available")
  86. hostinfo.SetFirewallMode("nft")
  87. return FirewallModeNfTables
  88. case iptAva:
  89. logf("iptables is available")
  90. hostinfo.SetFirewallMode("ipt")
  91. return FirewallModeIPTables
  92. default:
  93. // if neither iptables nor nftables are available, use iptablesRunner as a dummy
  94. // runner which exists but won't do anything. Creating iptablesRunner errors only
  95. // if the iptables command is missing or doesn’t support "--version", as long as it
  96. // can determine a version then it’ll carry on.
  97. hostinfo.SetFirewallMode("ipt-fb")
  98. return FirewallModeIPTables
  99. }
  100. }