iptables_runner.go 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build linux
  4. package linuxfw
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "log"
  10. "net/netip"
  11. "os"
  12. "os/exec"
  13. "slices"
  14. "strconv"
  15. "strings"
  16. "github.com/coreos/go-iptables/iptables"
  17. "tailscale.com/net/tsaddr"
  18. "tailscale.com/types/logger"
  19. "tailscale.com/util/multierr"
  20. "tailscale.com/version/distro"
  21. )
  22. // isNotExistError needs to be overridden in tests that rely on distinguishing
  23. // this error, because we don't have a good way how to create a new
  24. // iptables.Error of that type.
  25. var isNotExistError = func(err error) bool {
  26. var e *iptables.Error
  27. return errors.As(err, &e) && e.IsNotExist()
  28. }
  29. type iptablesInterface interface {
  30. // Adding this interface for testing purposes so we can mock out
  31. // the iptables library, in reality this is a wrapper to *iptables.IPTables.
  32. Insert(table, chain string, pos int, args ...string) error
  33. Append(table, chain string, args ...string) error
  34. Exists(table, chain string, args ...string) (bool, error)
  35. Delete(table, chain string, args ...string) error
  36. List(table, chain string) ([]string, error)
  37. ClearChain(table, chain string) error
  38. NewChain(table, chain string) error
  39. DeleteChain(table, chain string) error
  40. }
  41. type iptablesRunner struct {
  42. ipt4 iptablesInterface
  43. ipt6 iptablesInterface
  44. v6Available bool
  45. v6NATAvailable bool
  46. v6FilterAvailable bool
  47. }
  48. func checkIP6TablesExists() error {
  49. // Some distros ship ip6tables separately from iptables.
  50. if _, err := exec.LookPath("ip6tables"); err != nil {
  51. return fmt.Errorf("path not found: %w", err)
  52. }
  53. return nil
  54. }
  55. // newIPTablesRunner constructs a NetfilterRunner that programs iptables rules.
  56. // If the underlying iptables library fails to initialize, that error is
  57. // returned. The runner probes for IPv6 support once at initialization time and
  58. // if not found, no IPv6 rules will be modified for the lifetime of the runner.
  59. func newIPTablesRunner(logf logger.Logf) (*iptablesRunner, error) {
  60. ipt4, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
  61. if err != nil {
  62. return nil, err
  63. }
  64. supportsV6, supportsV6NAT, supportsV6Filter := false, false, false
  65. v6err := CheckIPv6(logf)
  66. ip6terr := checkIP6TablesExists()
  67. var ipt6 *iptables.IPTables
  68. switch {
  69. case v6err != nil:
  70. logf("disabling tunneled IPv6 due to system IPv6 config: %v", v6err)
  71. case ip6terr != nil:
  72. logf("disabling tunneled IPv6 due to missing ip6tables: %v", ip6terr)
  73. default:
  74. supportsV6 = true
  75. ipt6, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
  76. if err != nil {
  77. return nil, err
  78. }
  79. supportsV6Filter = checkSupportsV6Filter(ipt6, logf)
  80. supportsV6NAT = checkSupportsV6NAT(ipt6, logf)
  81. logf("netfilter running in iptables mode v6 = %v, v6filter = %v, v6nat = %v", supportsV6, supportsV6Filter, supportsV6NAT)
  82. }
  83. return &iptablesRunner{
  84. ipt4: ipt4,
  85. ipt6: ipt6,
  86. v6Available: supportsV6,
  87. v6NATAvailable: supportsV6NAT,
  88. v6FilterAvailable: supportsV6Filter}, nil
  89. }
  90. // checkSupportsV6Filter returns whether the system has a "filter" table in the
  91. // IPv6 tables. Some container environments such as GitHub codespaces have
  92. // limited local IPv6 support, and containers containing ip6tables, but do not
  93. // have kernel support for IPv6 filtering.
  94. // We will not set ip6tables rules in these instances.
  95. func checkSupportsV6Filter(ipt *iptables.IPTables, logf logger.Logf) bool {
  96. if ipt == nil {
  97. return false
  98. }
  99. _, filterListErr := ipt.ListChains("filter")
  100. if filterListErr == nil {
  101. return true
  102. }
  103. logf("ip6tables filtering is not supported on this host: %v", filterListErr)
  104. return false
  105. }
  106. // checkSupportsV6NAT returns whether the system has a "nat" table in the
  107. // IPv6 netfilter stack.
  108. //
  109. // The nat table was added after the initial release of ipv6
  110. // netfilter, so some older distros ship a kernel that can't NAT IPv6
  111. // traffic.
  112. // ipt must be initialized for IPv6.
  113. func checkSupportsV6NAT(ipt *iptables.IPTables, logf logger.Logf) bool {
  114. if ipt == nil || ipt.Proto() != iptables.ProtocolIPv6 {
  115. return false
  116. }
  117. _, natListErr := ipt.ListChains("nat")
  118. if natListErr == nil {
  119. return true
  120. }
  121. // TODO (irbekrm): the following two checks were added before the check
  122. // above that verifies that nat chains can be listed. It is a
  123. // container-friendly check (see
  124. // https://github.com/tailscale/tailscale/issues/11344), but also should
  125. // be good enough on its own in other environments. If we never observe
  126. // it falsely succeed, let's remove the other two checks.
  127. bs, err := os.ReadFile("/proc/net/ip6_tables_names")
  128. if err != nil {
  129. return false
  130. }
  131. if bytes.Contains(bs, []byte("nat\n")) {
  132. logf("[unexpected] listing nat chains failed, but /proc/net/ip6_tables_name reports a nat table existing")
  133. return true
  134. }
  135. if exec.Command("modprobe", "ip6table_nat").Run() == nil {
  136. logf("[unexpected] listing nat chains failed, but modprobe ip6table_nat succeeded")
  137. return true
  138. }
  139. return false
  140. }
  141. // HasIPV6 reports true if the system supports IPv6.
  142. func (i *iptablesRunner) HasIPV6() bool {
  143. return i.v6Available
  144. }
  145. // HasIPV6Filter reports true if the system supports ip6tables filter table.
  146. func (i *iptablesRunner) HasIPV6Filter() bool {
  147. return i.v6FilterAvailable
  148. }
  149. // HasIPV6NAT reports true if the system supports IPv6 NAT.
  150. func (i *iptablesRunner) HasIPV6NAT() bool {
  151. return i.v6NATAvailable
  152. }
  153. // getIPTByAddr returns the iptablesInterface with correct IP family
  154. // that we will be using for the given address.
  155. func (i *iptablesRunner) getIPTByAddr(addr netip.Addr) iptablesInterface {
  156. nf := i.ipt4
  157. if addr.Is6() {
  158. nf = i.ipt6
  159. }
  160. return nf
  161. }
  162. // AddLoopbackRule adds an iptables rule to permit loopback traffic to
  163. // a local Tailscale IP.
  164. func (i *iptablesRunner) AddLoopbackRule(addr netip.Addr) error {
  165. if err := i.getIPTByAddr(addr).Insert("filter", "ts-input", 1, "-i", "lo", "-s", addr.String(), "-j", "ACCEPT"); err != nil {
  166. return fmt.Errorf("adding loopback allow rule for %q: %w", addr, err)
  167. }
  168. return nil
  169. }
  170. // tsChain returns the name of the tailscale sub-chain corresponding
  171. // to the given "parent" chain (e.g. INPUT, FORWARD, ...).
  172. func tsChain(chain string) string {
  173. return "ts-" + strings.ToLower(chain)
  174. }
  175. // DelLoopbackRule removes the iptables rule permitting loopback
  176. // traffic to a Tailscale IP.
  177. func (i *iptablesRunner) DelLoopbackRule(addr netip.Addr) error {
  178. if err := i.getIPTByAddr(addr).Delete("filter", "ts-input", "-i", "lo", "-s", addr.String(), "-j", "ACCEPT"); err != nil {
  179. return fmt.Errorf("deleting loopback allow rule for %q: %w", addr, err)
  180. }
  181. return nil
  182. }
  183. // getTables gets the available iptablesInterface in iptables runner.
  184. func (i *iptablesRunner) getTables() []iptablesInterface {
  185. if i.HasIPV6Filter() {
  186. return []iptablesInterface{i.ipt4, i.ipt6}
  187. }
  188. return []iptablesInterface{i.ipt4}
  189. }
  190. // getNATTables gets the available iptablesInterface in iptables runner.
  191. // If the system does not support IPv6 NAT, only the IPv4 iptablesInterface
  192. // is returned.
  193. func (i *iptablesRunner) getNATTables() []iptablesInterface {
  194. if i.HasIPV6NAT() {
  195. return i.getTables()
  196. }
  197. return []iptablesInterface{i.ipt4}
  198. }
  199. // AddHooks inserts calls to tailscale's netfilter chains in
  200. // the relevant main netfilter chains. The tailscale chains must
  201. // already exist. If they do not, an error is returned.
  202. func (i *iptablesRunner) AddHooks() error {
  203. // divert inserts a jump to the tailscale chain in the given table/chain.
  204. // If the jump already exists, it is a no-op.
  205. divert := func(ipt iptablesInterface, table, chain string) error {
  206. tsChain := tsChain(chain)
  207. args := []string{"-j", tsChain}
  208. exists, err := ipt.Exists(table, chain, args...)
  209. if err != nil {
  210. return fmt.Errorf("checking for %v in %s/%s: %w", args, table, chain, err)
  211. }
  212. if exists {
  213. return nil
  214. }
  215. if err := ipt.Insert(table, chain, 1, args...); err != nil {
  216. return fmt.Errorf("adding %v in %s/%s: %w", args, table, chain, err)
  217. }
  218. return nil
  219. }
  220. for _, ipt := range i.getTables() {
  221. if err := divert(ipt, "filter", "INPUT"); err != nil {
  222. return err
  223. }
  224. if err := divert(ipt, "filter", "FORWARD"); err != nil {
  225. return err
  226. }
  227. }
  228. for _, ipt := range i.getNATTables() {
  229. if err := divert(ipt, "nat", "POSTROUTING"); err != nil {
  230. return err
  231. }
  232. }
  233. return nil
  234. }
  235. // AddChains creates custom Tailscale chains in netfilter via iptables
  236. // if the ts-chain doesn't already exist.
  237. func (i *iptablesRunner) AddChains() error {
  238. // create creates a chain in the given table if it doesn't already exist.
  239. // If the chain already exists, it is a no-op.
  240. create := func(ipt iptablesInterface, table, chain string) error {
  241. err := ipt.ClearChain(table, chain)
  242. if isNotExistError(err) {
  243. // nonexistent chain. let's create it!
  244. return ipt.NewChain(table, chain)
  245. }
  246. if err != nil {
  247. return fmt.Errorf("setting up %s/%s: %w", table, chain, err)
  248. }
  249. return nil
  250. }
  251. for _, ipt := range i.getTables() {
  252. if err := create(ipt, "filter", "ts-input"); err != nil {
  253. return err
  254. }
  255. if err := create(ipt, "filter", "ts-forward"); err != nil {
  256. return err
  257. }
  258. }
  259. for _, ipt := range i.getNATTables() {
  260. if err := create(ipt, "nat", "ts-postrouting"); err != nil {
  261. return err
  262. }
  263. }
  264. return nil
  265. }
  266. // AddBase adds some basic processing rules to be supplemented by
  267. // later calls to other helpers.
  268. func (i *iptablesRunner) AddBase(tunname string) error {
  269. if err := i.addBase4(tunname); err != nil {
  270. return err
  271. }
  272. if i.HasIPV6Filter() {
  273. if err := i.addBase6(tunname); err != nil {
  274. return err
  275. }
  276. }
  277. return nil
  278. }
  279. // addBase4 adds some basic IPv4 processing rules to be
  280. // supplemented by later calls to other helpers.
  281. func (i *iptablesRunner) addBase4(tunname string) error {
  282. // Only allow CGNAT range traffic to come from tailscale0. There
  283. // is an exception carved out for ranges used by ChromeOS, for
  284. // which we fall out of the Tailscale chain.
  285. //
  286. // Note, this will definitely break nodes that end up using the
  287. // CGNAT range for other purposes :(.
  288. args := []string{"!", "-i", tunname, "-s", tsaddr.ChromeOSVMRange().String(), "-j", "RETURN"}
  289. if err := i.ipt4.Append("filter", "ts-input", args...); err != nil {
  290. return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err)
  291. }
  292. args = []string{"!", "-i", tunname, "-s", tsaddr.CGNATRange().String(), "-j", "DROP"}
  293. if err := i.ipt4.Append("filter", "ts-input", args...); err != nil {
  294. return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err)
  295. }
  296. // Explicitly allow all other inbound traffic to the tun interface
  297. args = []string{"-i", tunname, "-j", "ACCEPT"}
  298. if err := i.ipt4.Append("filter", "ts-input", args...); err != nil {
  299. return fmt.Errorf("adding %v in v4/filter/ts-input: %w", args, err)
  300. }
  301. // Forward all traffic from the Tailscale interface, and drop
  302. // traffic to the tailscale interface by default. We use packet
  303. // marks here so both filter/FORWARD and nat/POSTROUTING can match
  304. // on these packets of interest.
  305. //
  306. // In particular, we only want to apply SNAT rules in
  307. // nat/POSTROUTING to packets that originated from the Tailscale
  308. // interface, but we can't match on the inbound interface in
  309. // POSTROUTING. So instead, we match on the inbound interface in
  310. // filter/FORWARD, and set a packet mark that nat/POSTROUTING can
  311. // use to effectively run that same test again.
  312. args = []string{"-i", tunname, "-j", "MARK", "--set-mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask}
  313. if err := i.ipt4.Append("filter", "ts-forward", args...); err != nil {
  314. return fmt.Errorf("adding %v in v4/filter/ts-forward: %w", args, err)
  315. }
  316. args = []string{"-m", "mark", "--mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask, "-j", "ACCEPT"}
  317. if err := i.ipt4.Append("filter", "ts-forward", args...); err != nil {
  318. return fmt.Errorf("adding %v in v4/filter/ts-forward: %w", args, err)
  319. }
  320. args = []string{"-o", tunname, "-s", tsaddr.CGNATRange().String(), "-j", "DROP"}
  321. if err := i.ipt4.Append("filter", "ts-forward", args...); err != nil {
  322. return fmt.Errorf("adding %v in v4/filter/ts-forward: %w", args, err)
  323. }
  324. args = []string{"-o", tunname, "-j", "ACCEPT"}
  325. if err := i.ipt4.Append("filter", "ts-forward", args...); err != nil {
  326. return fmt.Errorf("adding %v in v4/filter/ts-forward: %w", args, err)
  327. }
  328. return nil
  329. }
  330. func (i *iptablesRunner) AddDNATRule(origDst, dst netip.Addr) error {
  331. table := i.getIPTByAddr(dst)
  332. return table.Insert("nat", "PREROUTING", 1, "--destination", origDst.String(), "-j", "DNAT", "--to-destination", dst.String())
  333. }
  334. // EnsureSNATForDst sets up firewall to ensure that all traffic aimed for dst, has its source ip set to src:
  335. // - creates a SNAT rule if not already present
  336. // - ensures that any no longer valid SNAT rules for the same dst are removed
  337. func (i *iptablesRunner) EnsureSNATForDst(src, dst netip.Addr) error {
  338. table := i.getIPTByAddr(dst)
  339. rules, err := table.List("nat", "POSTROUTING")
  340. if err != nil {
  341. return fmt.Errorf("error listing rules: %v", err)
  342. }
  343. // iptables accept either address or a CIDR value for the --destination flag, but converts an address to /32
  344. // CIDR. Explicitly passing a /32 CIDR made it possible to test this rule.
  345. dstPrefix, err := dst.Prefix(32)
  346. if err != nil {
  347. return fmt.Errorf("error calculating prefix of dst %v: %v", dst, err)
  348. }
  349. // wantsArgsPrefix is the prefix of the SNAT rule for the provided destination.
  350. // We should only have one POSTROUTING rule with this prefix.
  351. wantsArgsPrefix := fmt.Sprintf("-d %s -j SNAT --to-source", dstPrefix.String())
  352. // wantsArgs is the actual SNAT rule that we want.
  353. wantsArgs := fmt.Sprintf("%s %s", wantsArgsPrefix, src.String())
  354. for _, r := range rules {
  355. args := argsFromPostRoutingRule(r)
  356. if strings.HasPrefix(args, wantsArgsPrefix) {
  357. if strings.HasPrefix(args, wantsArgs) {
  358. return nil
  359. }
  360. // SNAT rule matching the destination, but for a different source - delete.
  361. if err := table.Delete("nat", "POSTROUTING", strings.Split(args, " ")...); err != nil {
  362. // If we failed to delete don't crash the node- the proxy should still be functioning.
  363. log.Printf("[unexpected] error deleting rule %s: %v, please report it.", r, err)
  364. }
  365. break
  366. }
  367. }
  368. return table.Insert("nat", "POSTROUTING", 1, "-d", dstPrefix.String(), "-j", "SNAT", "--to-source", src.String())
  369. }
  370. func (i *iptablesRunner) DNATNonTailscaleTraffic(tun string, dst netip.Addr) error {
  371. table := i.getIPTByAddr(dst)
  372. return table.Insert("nat", "PREROUTING", 1, "!", "-i", tun, "-j", "DNAT", "--to-destination", dst.String())
  373. }
  374. // DNATWithLoadBalancer adds iptables rules to forward all traffic received for
  375. // originDst to the backend dsts. Traffic will be load balanced using round robin.
  376. func (i *iptablesRunner) DNATWithLoadBalancer(origDst netip.Addr, dsts []netip.Addr) error {
  377. table := i.getIPTByAddr(dsts[0])
  378. if err := table.ClearChain("nat", "PREROUTING"); err != nil && !isNotExistError(err) {
  379. // If clearing the PREROUTING chain fails, fail the whole operation. This
  380. // rule is currently only used in Kubernetes containers where a
  381. // failed container gets restarted which should hopefully fix things.
  382. return fmt.Errorf("error clearing nat PREROUTING chain: %w", err)
  383. }
  384. // If dsts contain more than one address, for n := n in range(len(dsts)..2) route packets for every nth connection to dsts[n].
  385. for i := len(dsts); i >= 2; i-- {
  386. dst := dsts[i-1] // the order in which rules for addrs are installed does not matter
  387. if err := table.Append("nat", "PREROUTING", "--destination", origDst.String(), "-m", "statistic", "--mode", "nth", "--every", fmt.Sprint(i), "--packet", "0", "-j", "DNAT", "--to-destination", dst.String()); err != nil {
  388. return fmt.Errorf("error adding DNAT rule for %s: %w", dst.String(), err)
  389. }
  390. }
  391. // If the packet falls through to this rule, we route to the first destination in the list unconditionally.
  392. return table.Append("nat", "PREROUTING", "--destination", origDst.String(), "-j", "DNAT", "--to-destination", dsts[0].String())
  393. }
  394. func (i *iptablesRunner) ClampMSSToPMTU(tun string, addr netip.Addr) error {
  395. table := i.getIPTByAddr(addr)
  396. return table.Append("mangle", "FORWARD", "-o", tun, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu")
  397. }
  398. // addBase6 adds some basic IPv6 processing rules to be
  399. // supplemented by later calls to other helpers.
  400. func (i *iptablesRunner) addBase6(tunname string) error {
  401. // TODO: only allow traffic from Tailscale's ULA range to come
  402. // from tailscale0.
  403. // Explicitly allow all other inbound traffic to the tun interface
  404. args := []string{"-i", tunname, "-j", "ACCEPT"}
  405. if err := i.ipt6.Append("filter", "ts-input", args...); err != nil {
  406. return fmt.Errorf("adding %v in v6/filter/ts-input: %w", args, err)
  407. }
  408. args = []string{"-i", tunname, "-j", "MARK", "--set-mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask}
  409. if err := i.ipt6.Append("filter", "ts-forward", args...); err != nil {
  410. return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err)
  411. }
  412. args = []string{"-m", "mark", "--mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask, "-j", "ACCEPT"}
  413. if err := i.ipt6.Append("filter", "ts-forward", args...); err != nil {
  414. return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err)
  415. }
  416. // TODO: drop forwarded traffic to tailscale0 from tailscale's ULA
  417. // (see corresponding IPv4 CGNAT rule).
  418. args = []string{"-o", tunname, "-j", "ACCEPT"}
  419. if err := i.ipt6.Append("filter", "ts-forward", args...); err != nil {
  420. return fmt.Errorf("adding %v in v6/filter/ts-forward: %w", args, err)
  421. }
  422. return nil
  423. }
  424. // DelChains removes the custom Tailscale chains from netfilter via iptables.
  425. func (i *iptablesRunner) DelChains() error {
  426. for _, ipt := range i.getTables() {
  427. if err := delChain(ipt, "filter", "ts-input"); err != nil {
  428. return err
  429. }
  430. if err := delChain(ipt, "filter", "ts-forward"); err != nil {
  431. return err
  432. }
  433. }
  434. for _, ipt := range i.getNATTables() {
  435. if err := delChain(ipt, "nat", "ts-postrouting"); err != nil {
  436. return err
  437. }
  438. }
  439. return nil
  440. }
  441. // DelBase empties but does not remove custom Tailscale chains from
  442. // netfilter via iptables.
  443. func (i *iptablesRunner) DelBase() error {
  444. del := func(ipt iptablesInterface, table, chain string) error {
  445. if err := ipt.ClearChain(table, chain); err != nil {
  446. if isNotExistError(err) {
  447. // nonexistent chain. That's fine, since it's
  448. // the desired state anyway.
  449. return nil
  450. }
  451. return fmt.Errorf("flushing %s/%s: %w", table, chain, err)
  452. }
  453. return nil
  454. }
  455. for _, ipt := range i.getTables() {
  456. if err := del(ipt, "filter", "ts-input"); err != nil {
  457. return err
  458. }
  459. if err := del(ipt, "filter", "ts-forward"); err != nil {
  460. return err
  461. }
  462. }
  463. for _, ipt := range i.getNATTables() {
  464. if err := del(ipt, "nat", "ts-postrouting"); err != nil {
  465. return err
  466. }
  467. }
  468. return nil
  469. }
  470. // DelHooks deletes the calls to tailscale's netfilter chains
  471. // in the relevant main netfilter chains.
  472. func (i *iptablesRunner) DelHooks(logf logger.Logf) error {
  473. for _, ipt := range i.getTables() {
  474. if err := delTSHook(ipt, "filter", "INPUT", logf); err != nil {
  475. return err
  476. }
  477. if err := delTSHook(ipt, "filter", "FORWARD", logf); err != nil {
  478. return err
  479. }
  480. }
  481. for _, ipt := range i.getNATTables() {
  482. if err := delTSHook(ipt, "nat", "POSTROUTING", logf); err != nil {
  483. return err
  484. }
  485. }
  486. return nil
  487. }
  488. // AddSNATRule adds a netfilter rule to SNAT traffic destined for
  489. // local subnets.
  490. func (i *iptablesRunner) AddSNATRule() error {
  491. args := []string{"-m", "mark", "--mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask, "-j", "MASQUERADE"}
  492. for _, ipt := range i.getNATTables() {
  493. if err := ipt.Append("nat", "ts-postrouting", args...); err != nil {
  494. return fmt.Errorf("adding %v in nat/ts-postrouting: %w", args, err)
  495. }
  496. }
  497. return nil
  498. }
  499. // DelSNATRule removes the netfilter rule to SNAT traffic destined for
  500. // local subnets. An error is returned if the rule does not exist.
  501. func (i *iptablesRunner) DelSNATRule() error {
  502. args := []string{"-m", "mark", "--mark", TailscaleSubnetRouteMark + "/" + TailscaleFwmarkMask, "-j", "MASQUERADE"}
  503. for _, ipt := range i.getNATTables() {
  504. if err := ipt.Delete("nat", "ts-postrouting", args...); err != nil {
  505. return fmt.Errorf("deleting %v in nat/ts-postrouting: %w", args, err)
  506. }
  507. }
  508. return nil
  509. }
  510. func statefulRuleArgs(tunname string) []string {
  511. return []string{"-o", tunname, "-m", "conntrack", "!", "--ctstate", "ESTABLISHED,RELATED", "-j", "DROP"}
  512. }
  513. // AddStatefulRule adds a netfilter rule for stateful packet filtering using
  514. // conntrack.
  515. func (i *iptablesRunner) AddStatefulRule(tunname string) error {
  516. // Drop packets that are destined for the tailscale interface if
  517. // they're a new connection, per conntrack, to prevent hosts on the
  518. // same subnet from being able to use this device as a way to forward
  519. // packets on to the Tailscale network.
  520. //
  521. // The conntrack states are:
  522. // NEW A packet which creates a new connection.
  523. // ESTABLISHED A packet which belongs to an existing connection
  524. // (i.e., a reply packet, or outgoing packet on a
  525. // connection which has seen replies).
  526. // RELATED A packet which is related to, but not part of, an
  527. // existing connection, such as an ICMP error.
  528. // INVALID A packet which could not be identified for some
  529. // reason: this includes running out of memory and ICMP
  530. // errors which don't correspond to any known
  531. // connection. Generally these packets should be
  532. // dropped.
  533. //
  534. // We drop NEW packets to prevent connections from coming "into"
  535. // Tailscale from other hosts on the same network segment; we drop
  536. // INVALID packets as well.
  537. args := statefulRuleArgs(tunname)
  538. for _, ipt := range i.getTables() {
  539. // First, find the final "accept" rule.
  540. rules, err := ipt.List("filter", "ts-forward")
  541. if err != nil {
  542. return fmt.Errorf("listing rules in filter/ts-forward: %w", err)
  543. }
  544. want := fmt.Sprintf("-A %s -o %s -j ACCEPT", "ts-forward", tunname)
  545. pos := slices.Index(rules, want)
  546. if pos < 0 {
  547. return fmt.Errorf("couldn't find final ACCEPT rule in filter/ts-forward")
  548. }
  549. if err := ipt.Insert("filter", "ts-forward", pos, args...); err != nil {
  550. return fmt.Errorf("adding %v in filter/ts-forward: %w", args, err)
  551. }
  552. }
  553. return nil
  554. }
  555. // DelStatefulRule removes the netfilter rule for stateful packet filtering
  556. // using conntrack.
  557. func (i *iptablesRunner) DelStatefulRule(tunname string) error {
  558. args := statefulRuleArgs(tunname)
  559. for _, ipt := range i.getTables() {
  560. if err := ipt.Delete("filter", "ts-forward", args...); err != nil {
  561. return fmt.Errorf("deleting %v in filter/ts-forward: %w", args, err)
  562. }
  563. }
  564. return nil
  565. }
  566. // buildMagicsockPortRule generates the string slice containing the arguments
  567. // to describe a rule accepting traffic on a particular port to iptables. It is
  568. // separated out here to avoid repetition in AddMagicsockPortRule and
  569. // RemoveMagicsockPortRule, since it is important that the same rule is passed
  570. // to Append() and Delete().
  571. func buildMagicsockPortRule(port uint16) []string {
  572. return []string{"-p", "udp", "--dport", strconv.FormatUint(uint64(port), 10), "-j", "ACCEPT"}
  573. }
  574. // AddMagicsockPortRule adds a rule to iptables to allow incoming traffic on
  575. // the specified UDP port, so magicsock can accept incoming connections.
  576. // network must be either "udp4" or "udp6" - this determines whether the rule
  577. // is added for IPv4 or IPv6.
  578. func (i *iptablesRunner) AddMagicsockPortRule(port uint16, network string) error {
  579. var ipt iptablesInterface
  580. switch network {
  581. case "udp4":
  582. ipt = i.ipt4
  583. case "udp6":
  584. ipt = i.ipt6
  585. default:
  586. return fmt.Errorf("unsupported network %s", network)
  587. }
  588. args := buildMagicsockPortRule(port)
  589. if err := ipt.Append("filter", "ts-input", args...); err != nil {
  590. return fmt.Errorf("adding %v in filter/ts-input: %w", args, err)
  591. }
  592. return nil
  593. }
  594. // DelMagicsockPortRule removes a rule added by AddMagicsockPortRule to accept
  595. // incoming traffic on a particular UDP port.
  596. // network must be either "udp4" or "udp6" - this determines whether the rule
  597. // is removed for IPv4 or IPv6.
  598. func (i *iptablesRunner) DelMagicsockPortRule(port uint16, network string) error {
  599. var ipt iptablesInterface
  600. switch network {
  601. case "udp4":
  602. ipt = i.ipt4
  603. case "udp6":
  604. ipt = i.ipt6
  605. default:
  606. return fmt.Errorf("unsupported network %s", network)
  607. }
  608. args := buildMagicsockPortRule(port)
  609. if err := ipt.Delete("filter", "ts-input", args...); err != nil {
  610. return fmt.Errorf("removing %v in filter/ts-input: %w", args, err)
  611. }
  612. return nil
  613. }
  614. // IPTablesCleanUp removes all Tailscale added iptables rules.
  615. // Any errors that occur are logged to the provided logf.
  616. func IPTablesCleanUp(logf logger.Logf) {
  617. if distro.Get() == distro.Gokrazy {
  618. // Gokrazy uses nftables and doesn't have the "iptables" command.
  619. // Avoid log spam on cleanup. (#12277)
  620. return
  621. }
  622. err := clearRules(iptables.ProtocolIPv4, logf)
  623. if err != nil {
  624. logf("linuxfw: clear iptables: %v", err)
  625. }
  626. err = clearRules(iptables.ProtocolIPv6, logf)
  627. if err != nil {
  628. logf("linuxfw: clear ip6tables: %v", err)
  629. }
  630. }
  631. // delTSHook deletes hook in a chain that jumps to a ts-chain. If the hook does not
  632. // exist, it's a no-op since the desired state is already achieved but we log the
  633. // error because error code from the iptables module resists unwrapping.
  634. func delTSHook(ipt iptablesInterface, table, chain string, logf logger.Logf) error {
  635. tsChain := tsChain(chain)
  636. args := []string{"-j", tsChain}
  637. if err := ipt.Delete(table, chain, args...); err != nil && !isNotExistError(err) {
  638. return fmt.Errorf("deleting %v in %s/%s: %v", args, table, chain, err)
  639. }
  640. return nil
  641. }
  642. // delChain flushes and deletes a chain. If the chain does not exist, it's a no-op
  643. // since the desired state is already achieved. otherwise, it returns an error.
  644. func delChain(ipt iptablesInterface, table, chain string) error {
  645. if err := ipt.ClearChain(table, chain); err != nil {
  646. if isNotExistError(err) {
  647. // nonexistent chain. nothing to do.
  648. return nil
  649. }
  650. return fmt.Errorf("flushing %s/%s: %w", table, chain, err)
  651. }
  652. if err := ipt.DeleteChain(table, chain); err != nil {
  653. return fmt.Errorf("deleting %s/%s: %w", table, chain, err)
  654. }
  655. return nil
  656. }
  657. // clearRules clears all the iptables rules created by Tailscale
  658. // for the given protocol. If error occurs, it's logged but not returned.
  659. func clearRules(proto iptables.Protocol, logf logger.Logf) error {
  660. ipt, err := iptables.NewWithProtocol(proto)
  661. if err != nil {
  662. return err
  663. }
  664. var errs []error
  665. if err := delTSHook(ipt, "filter", "INPUT", logf); err != nil {
  666. errs = append(errs, err)
  667. }
  668. if err := delTSHook(ipt, "filter", "FORWARD", logf); err != nil {
  669. errs = append(errs, err)
  670. }
  671. if err := delTSHook(ipt, "nat", "POSTROUTING", logf); err != nil {
  672. errs = append(errs, err)
  673. }
  674. if err := delChain(ipt, "filter", "ts-input"); err != nil {
  675. errs = append(errs, err)
  676. }
  677. if err := delChain(ipt, "filter", "ts-forward"); err != nil {
  678. errs = append(errs, err)
  679. }
  680. if err := delChain(ipt, "nat", "ts-postrouting"); err != nil {
  681. errs = append(errs, err)
  682. }
  683. return multierr.New(errs...)
  684. }
  685. // argsFromPostRoutingRule accepts a rule as returned by iptables.List and, if it is a rule from POSTROUTING chain,
  686. // returns the args part, else returns the original rule.
  687. func argsFromPostRoutingRule(r string) string {
  688. args, _ := strings.CutPrefix(r, "-A POSTROUTING ")
  689. return args
  690. }