fw_linux.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package main
  4. import (
  5. "encoding/binary"
  6. "github.com/google/nftables"
  7. "github.com/google/nftables/expr"
  8. "tailscale.com/types/ptr"
  9. )
  10. func init() {
  11. addFirewall = addFirewallLinux
  12. }
  13. func addFirewallLinux() error {
  14. c, err := nftables.New()
  15. if err != nil {
  16. return err
  17. }
  18. // Create a new table
  19. table := &nftables.Table{
  20. Family: nftables.TableFamilyIPv4, // TableFamilyINet doesn't work (why?. oh well.)
  21. Name: "filter",
  22. }
  23. c.AddTable(table)
  24. // Create a new chain for incoming traffic
  25. inputChain := &nftables.Chain{
  26. Name: "input",
  27. Table: table,
  28. Type: nftables.ChainTypeFilter,
  29. Hooknum: nftables.ChainHookInput,
  30. Priority: nftables.ChainPriorityFilter,
  31. Policy: ptr.To(nftables.ChainPolicyDrop),
  32. }
  33. c.AddChain(inputChain)
  34. // Allow traffic from the loopback interface
  35. c.AddRule(&nftables.Rule{
  36. Table: table,
  37. Chain: inputChain,
  38. Exprs: []expr.Any{
  39. &expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
  40. &expr.Cmp{
  41. Op: expr.CmpOpEq,
  42. Register: 1,
  43. Data: []byte("lo"),
  44. },
  45. &expr.Verdict{
  46. Kind: expr.VerdictAccept,
  47. },
  48. },
  49. })
  50. // Accept established and related connections
  51. c.AddRule(&nftables.Rule{
  52. Table: table,
  53. Chain: inputChain,
  54. Exprs: []expr.Any{
  55. &expr.Ct{
  56. Register: 1,
  57. Key: expr.CtKeySTATE,
  58. },
  59. &expr.Bitwise{
  60. SourceRegister: 1,
  61. DestRegister: 1,
  62. Len: 4,
  63. Mask: binary.NativeEndian.AppendUint32(nil, 0x06), // CT_STATE_BIT_ESTABLISHED | CT_STATE_BIT_RELATED
  64. Xor: binary.NativeEndian.AppendUint32(nil, 0),
  65. },
  66. &expr.Cmp{
  67. Op: expr.CmpOpNeq,
  68. Register: 1,
  69. Data: binary.NativeEndian.AppendUint32(nil, 0x00),
  70. },
  71. &expr.Verdict{
  72. Kind: expr.VerdictAccept,
  73. },
  74. },
  75. })
  76. // Allow TCP packets in that don't have the SYN bit set, even if they're not
  77. // ESTABLISHED or RELATED. This is because the test suite gets TCP
  78. // connections up & idle (for HTTP) before it conditionally installs these
  79. // firewall rules. But because conntrack wasn't previously active, existing
  80. // TCP flows aren't ESTABLISHED and get dropped. So this rule allows
  81. // previously established TCP connections that predates the firewall rules
  82. // to continue working, as they don't have conntrack state.
  83. c.AddRule(&nftables.Rule{
  84. Table: table,
  85. Chain: inputChain,
  86. Exprs: []expr.Any{
  87. &expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 1},
  88. &expr.Cmp{
  89. Op: expr.CmpOpEq,
  90. Register: 1,
  91. Data: []byte{0x06}, // TCP
  92. },
  93. &expr.Payload{ // get TCP flags
  94. DestRegister: 1,
  95. Base: 2,
  96. Offset: 13, // flags
  97. Len: 1,
  98. },
  99. &expr.Bitwise{
  100. SourceRegister: 1,
  101. DestRegister: 1,
  102. Len: 1,
  103. Mask: []byte{2}, // TCP_SYN
  104. Xor: []byte{0},
  105. },
  106. &expr.Cmp{
  107. Op: expr.CmpOpNeq,
  108. Register: 1,
  109. Data: []byte{2}, // TCP_SYN
  110. },
  111. &expr.Verdict{
  112. Kind: expr.VerdictAccept,
  113. },
  114. },
  115. })
  116. return c.Flush()
  117. }