2
0

firewall_test.go 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382
  1. package nebula
  2. import (
  3. "bytes"
  4. "errors"
  5. "math"
  6. "net/netip"
  7. "testing"
  8. "time"
  9. "github.com/gaissmai/bart"
  10. "github.com/sirupsen/logrus"
  11. "github.com/slackhq/nebula/cert"
  12. "github.com/slackhq/nebula/config"
  13. "github.com/slackhq/nebula/firewall"
  14. "github.com/slackhq/nebula/test"
  15. "github.com/stretchr/testify/assert"
  16. "github.com/stretchr/testify/require"
  17. )
  18. func TestNewFirewall(t *testing.T) {
  19. l := test.NewLogger()
  20. c := &dummyCert{}
  21. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  22. conntrack := fw.Conntrack
  23. assert.NotNil(t, conntrack)
  24. assert.NotNil(t, conntrack.Conns)
  25. assert.NotNil(t, conntrack.TimerWheel)
  26. assert.NotNil(t, fw.InRules)
  27. assert.NotNil(t, fw.OutRules)
  28. assert.Equal(t, time.Second, fw.TCPTimeout)
  29. assert.Equal(t, time.Minute, fw.UDPTimeout)
  30. assert.Equal(t, time.Hour, fw.DefaultTimeout)
  31. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  32. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  33. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  34. fw = NewFirewall(l, time.Second, time.Hour, time.Minute, c)
  35. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  36. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  37. fw = NewFirewall(l, time.Hour, time.Second, time.Minute, c)
  38. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  39. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  40. fw = NewFirewall(l, time.Hour, time.Minute, time.Second, c)
  41. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  42. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  43. fw = NewFirewall(l, time.Minute, time.Hour, time.Second, c)
  44. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  45. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  46. fw = NewFirewall(l, time.Minute, time.Second, time.Hour, c)
  47. assert.Equal(t, time.Hour, conntrack.TimerWheel.wheelDuration)
  48. assert.Equal(t, 3602, conntrack.TimerWheel.wheelLen)
  49. }
  50. func TestFirewall_AddRule(t *testing.T) {
  51. l := test.NewLogger()
  52. ob := &bytes.Buffer{}
  53. l.SetOutput(ob)
  54. c := &dummyCert{}
  55. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  56. assert.NotNil(t, fw.InRules)
  57. assert.NotNil(t, fw.OutRules)
  58. ti, err := netip.ParsePrefix("1.2.3.4/32")
  59. require.NoError(t, err)
  60. ti6, err := netip.ParsePrefix("fd12::34/128")
  61. require.NoError(t, err)
  62. require.NoError(t, fw.AddRule(true, firewall.ProtoTCP, 1, 1, []string{}, "", "", "", "", ""))
  63. // An empty rule is any
  64. assert.True(t, fw.InRules.TCP[1].Any.Any.Any)
  65. assert.Empty(t, fw.InRules.TCP[1].Any.Groups)
  66. assert.Empty(t, fw.InRules.TCP[1].Any.Hosts)
  67. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  68. require.NoError(t, fw.AddRule(true, firewall.ProtoUDP, 1, 1, []string{"g1"}, "", "", "", "", ""))
  69. assert.Nil(t, fw.InRules.UDP[1].Any.Any)
  70. assert.Contains(t, fw.InRules.UDP[1].Any.Groups[0].Groups, "g1")
  71. assert.Empty(t, fw.InRules.UDP[1].Any.Hosts)
  72. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  73. require.NoError(t, fw.AddRule(true, firewall.ProtoICMP, 1, 1, []string{}, "h1", "", "", "", ""))
  74. assert.Nil(t, fw.InRules.ICMP[1].Any.Any)
  75. assert.Empty(t, fw.InRules.ICMP[1].Any.Groups)
  76. assert.Contains(t, fw.InRules.ICMP[1].Any.Hosts, "h1")
  77. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  78. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 1, 1, []string{}, "", ti.String(), "", "", ""))
  79. assert.Nil(t, fw.OutRules.AnyProto[1].Any.Any)
  80. _, ok := fw.OutRules.AnyProto[1].Any.CIDR.Get(ti)
  81. assert.True(t, ok)
  82. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  83. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 1, 1, []string{}, "", ti6.String(), "", "", ""))
  84. assert.Nil(t, fw.OutRules.AnyProto[1].Any.Any)
  85. _, ok = fw.OutRules.AnyProto[1].Any.CIDR.Get(ti6)
  86. assert.True(t, ok)
  87. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  88. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 1, 1, []string{}, "", "", ti.String(), "", ""))
  89. assert.NotNil(t, fw.OutRules.AnyProto[1].Any.Any)
  90. ok = fw.OutRules.AnyProto[1].Any.Any.LocalCIDR.Get(ti)
  91. assert.True(t, ok)
  92. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  93. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 1, 1, []string{}, "", "", ti6.String(), "", ""))
  94. assert.NotNil(t, fw.OutRules.AnyProto[1].Any.Any)
  95. ok = fw.OutRules.AnyProto[1].Any.Any.LocalCIDR.Get(ti6)
  96. assert.True(t, ok)
  97. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  98. require.NoError(t, fw.AddRule(true, firewall.ProtoUDP, 1, 1, []string{"g1"}, "", "", "", "ca-name", ""))
  99. assert.Contains(t, fw.InRules.UDP[1].CANames, "ca-name")
  100. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  101. require.NoError(t, fw.AddRule(true, firewall.ProtoUDP, 1, 1, []string{"g1"}, "", "", "", "", "ca-sha"))
  102. assert.Contains(t, fw.InRules.UDP[1].CAShas, "ca-sha")
  103. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  104. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "any", "", "", "", ""))
  105. assert.True(t, fw.OutRules.AnyProto[0].Any.Any.Any)
  106. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  107. anyIp, err := netip.ParsePrefix("0.0.0.0/0")
  108. require.NoError(t, err)
  109. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", anyIp.String(), "", "", ""))
  110. assert.Nil(t, fw.OutRules.AnyProto[0].Any.Any)
  111. table, ok := fw.OutRules.AnyProto[0].Any.CIDR.Lookup(netip.MustParseAddr("1.1.1.1"))
  112. assert.True(t, table.Any)
  113. table, ok = fw.OutRules.AnyProto[0].Any.CIDR.Lookup(netip.MustParseAddr("9::9"))
  114. assert.False(t, ok)
  115. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  116. anyIp6, err := netip.ParsePrefix("::/0")
  117. require.NoError(t, err)
  118. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", anyIp6.String(), "", "", ""))
  119. assert.Nil(t, fw.OutRules.AnyProto[0].Any.Any)
  120. table, ok = fw.OutRules.AnyProto[0].Any.CIDR.Lookup(netip.MustParseAddr("9::9"))
  121. assert.True(t, table.Any)
  122. table, ok = fw.OutRules.AnyProto[0].Any.CIDR.Lookup(netip.MustParseAddr("1.1.1.1"))
  123. assert.False(t, ok)
  124. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  125. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", "any", "", "", ""))
  126. assert.True(t, fw.OutRules.AnyProto[0].Any.Any.Any)
  127. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  128. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", "", anyIp.String(), "", ""))
  129. assert.False(t, fw.OutRules.AnyProto[0].Any.Any.Any)
  130. assert.True(t, fw.OutRules.AnyProto[0].Any.Any.LocalCIDR.Lookup(netip.MustParseAddr("1.1.1.1")))
  131. assert.False(t, fw.OutRules.AnyProto[0].Any.Any.LocalCIDR.Lookup(netip.MustParseAddr("9::9")))
  132. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  133. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", "", anyIp6.String(), "", ""))
  134. assert.False(t, fw.OutRules.AnyProto[0].Any.Any.Any)
  135. assert.True(t, fw.OutRules.AnyProto[0].Any.Any.LocalCIDR.Lookup(netip.MustParseAddr("9::9")))
  136. assert.False(t, fw.OutRules.AnyProto[0].Any.Any.LocalCIDR.Lookup(netip.MustParseAddr("1.1.1.1")))
  137. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  138. require.NoError(t, fw.AddRule(false, firewall.ProtoAny, 0, 0, []string{}, "", "", "any", "", ""))
  139. assert.True(t, fw.OutRules.AnyProto[0].Any.Any.Any)
  140. // Test error conditions
  141. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c)
  142. require.Error(t, fw.AddRule(true, math.MaxUint8, 0, 0, []string{}, "", "", "", "", ""))
  143. require.Error(t, fw.AddRule(true, firewall.ProtoAny, 10, 0, []string{}, "", "", "", "", ""))
  144. }
  145. func TestFirewall_Drop(t *testing.T) {
  146. l := test.NewLogger()
  147. ob := &bytes.Buffer{}
  148. l.SetOutput(ob)
  149. myVpnNetworksTable := new(bart.Lite)
  150. myVpnNetworksTable.Insert(netip.MustParsePrefix("1.1.1.1/8"))
  151. p := firewall.Packet{
  152. LocalAddr: netip.MustParseAddr("1.2.3.4"),
  153. RemoteAddr: netip.MustParseAddr("1.2.3.4"),
  154. LocalPort: 10,
  155. RemotePort: 90,
  156. Protocol: firewall.ProtoUDP,
  157. Fragment: false,
  158. }
  159. c := dummyCert{
  160. name: "host1",
  161. networks: []netip.Prefix{netip.MustParsePrefix("1.2.3.4/24")},
  162. groups: []string{"default-group"},
  163. issuer: "signer-shasum",
  164. }
  165. h := HostInfo{
  166. ConnectionState: &ConnectionState{
  167. peerCert: &cert.CachedCertificate{
  168. Certificate: &c,
  169. InvertedGroups: map[string]struct{}{"default-group": {}},
  170. },
  171. },
  172. vpnAddrs: []netip.Addr{netip.MustParseAddr("1.2.3.4")},
  173. }
  174. h.buildNetworks(myVpnNetworksTable, &c)
  175. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  176. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"any"}, "", "", "", "", ""))
  177. cp := cert.NewCAPool()
  178. // Drop outbound
  179. assert.Equal(t, ErrNoMatchingRule, fw.Drop(p, false, &h, cp, nil))
  180. // Allow inbound
  181. resetConntrack(fw)
  182. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  183. // Allow outbound because conntrack
  184. require.NoError(t, fw.Drop(p, false, &h, cp, nil))
  185. // test remote mismatch
  186. oldRemote := p.RemoteAddr
  187. p.RemoteAddr = netip.MustParseAddr("1.2.3.10")
  188. assert.Equal(t, fw.Drop(p, false, &h, cp, nil), ErrInvalidRemoteIP)
  189. p.RemoteAddr = oldRemote
  190. // ensure signer doesn't get in the way of group checks
  191. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  192. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "", "signer-shasum"))
  193. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "", "signer-shasum-bad"))
  194. assert.Equal(t, fw.Drop(p, true, &h, cp, nil), ErrNoMatchingRule)
  195. // test caSha doesn't drop on match
  196. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  197. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "", "signer-shasum-bad"))
  198. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "", "signer-shasum"))
  199. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  200. // ensure ca name doesn't get in the way of group checks
  201. cp.CAs["signer-shasum"] = &cert.CachedCertificate{Certificate: &dummyCert{name: "ca-good"}}
  202. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  203. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "ca-good", ""))
  204. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "ca-good-bad", ""))
  205. assert.Equal(t, fw.Drop(p, true, &h, cp, nil), ErrNoMatchingRule)
  206. // test caName doesn't drop on match
  207. cp.CAs["signer-shasum"] = &cert.CachedCertificate{Certificate: &dummyCert{name: "ca-good"}}
  208. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  209. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "ca-good-bad", ""))
  210. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "ca-good", ""))
  211. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  212. }
  213. func TestFirewall_DropV6(t *testing.T) {
  214. l := test.NewLogger()
  215. ob := &bytes.Buffer{}
  216. l.SetOutput(ob)
  217. myVpnNetworksTable := new(bart.Lite)
  218. myVpnNetworksTable.Insert(netip.MustParsePrefix("fd00::/7"))
  219. p := firewall.Packet{
  220. LocalAddr: netip.MustParseAddr("fd12::34"),
  221. RemoteAddr: netip.MustParseAddr("fd12::34"),
  222. LocalPort: 10,
  223. RemotePort: 90,
  224. Protocol: firewall.ProtoUDP,
  225. Fragment: false,
  226. }
  227. c := dummyCert{
  228. name: "host1",
  229. networks: []netip.Prefix{netip.MustParsePrefix("fd12::34/120")},
  230. groups: []string{"default-group"},
  231. issuer: "signer-shasum",
  232. }
  233. h := HostInfo{
  234. ConnectionState: &ConnectionState{
  235. peerCert: &cert.CachedCertificate{
  236. Certificate: &c,
  237. InvertedGroups: map[string]struct{}{"default-group": {}},
  238. },
  239. },
  240. vpnAddrs: []netip.Addr{netip.MustParseAddr("fd12::34")},
  241. }
  242. h.buildNetworks(myVpnNetworksTable, &c)
  243. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  244. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"any"}, "", "", "", "", ""))
  245. cp := cert.NewCAPool()
  246. // Drop outbound
  247. assert.Equal(t, ErrNoMatchingRule, fw.Drop(p, false, &h, cp, nil))
  248. // Allow inbound
  249. resetConntrack(fw)
  250. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  251. // Allow outbound because conntrack
  252. require.NoError(t, fw.Drop(p, false, &h, cp, nil))
  253. // test remote mismatch
  254. oldRemote := p.RemoteAddr
  255. p.RemoteAddr = netip.MustParseAddr("fd12::56")
  256. assert.Equal(t, fw.Drop(p, false, &h, cp, nil), ErrInvalidRemoteIP)
  257. p.RemoteAddr = oldRemote
  258. // ensure signer doesn't get in the way of group checks
  259. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  260. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "", "signer-shasum"))
  261. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "", "signer-shasum-bad"))
  262. assert.Equal(t, fw.Drop(p, true, &h, cp, nil), ErrNoMatchingRule)
  263. // test caSha doesn't drop on match
  264. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  265. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "", "signer-shasum-bad"))
  266. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "", "signer-shasum"))
  267. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  268. // ensure ca name doesn't get in the way of group checks
  269. cp.CAs["signer-shasum"] = &cert.CachedCertificate{Certificate: &dummyCert{name: "ca-good"}}
  270. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  271. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "ca-good", ""))
  272. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "ca-good-bad", ""))
  273. assert.Equal(t, fw.Drop(p, true, &h, cp, nil), ErrNoMatchingRule)
  274. // test caName doesn't drop on match
  275. cp.CAs["signer-shasum"] = &cert.CachedCertificate{Certificate: &dummyCert{name: "ca-good"}}
  276. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  277. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"nope"}, "", "", "", "ca-good-bad", ""))
  278. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group"}, "", "", "", "ca-good", ""))
  279. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  280. }
  281. func BenchmarkFirewallTable_match(b *testing.B) {
  282. f := &Firewall{}
  283. ft := FirewallTable{
  284. TCP: firewallPort{},
  285. }
  286. pfix := netip.MustParsePrefix("172.1.1.1/32")
  287. _ = ft.TCP.addRule(f, 10, 10, []string{"good-group"}, "good-host", pfix.String(), "", "", "")
  288. _ = ft.TCP.addRule(f, 100, 100, []string{"good-group"}, "good-host", "", pfix.String(), "", "")
  289. pfix6 := netip.MustParsePrefix("fd11::11/128")
  290. _ = ft.TCP.addRule(f, 10, 10, []string{"good-group"}, "good-host", pfix6.String(), "", "", "")
  291. _ = ft.TCP.addRule(f, 100, 100, []string{"good-group"}, "good-host", "", pfix6.String(), "", "")
  292. cp := cert.NewCAPool()
  293. b.Run("fail on proto", func(b *testing.B) {
  294. // This benchmark is showing us the cost of failing to match the protocol
  295. c := &cert.CachedCertificate{
  296. Certificate: &dummyCert{},
  297. }
  298. for n := 0; n < b.N; n++ {
  299. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoUDP}, true, c, cp))
  300. }
  301. })
  302. b.Run("pass proto, fail on port", func(b *testing.B) {
  303. // This benchmark is showing us the cost of matching a specific protocol but failing to match the port
  304. c := &cert.CachedCertificate{
  305. Certificate: &dummyCert{},
  306. }
  307. for n := 0; n < b.N; n++ {
  308. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 1}, true, c, cp))
  309. }
  310. })
  311. b.Run("pass proto, port, fail on local CIDR", func(b *testing.B) {
  312. c := &cert.CachedCertificate{
  313. Certificate: &dummyCert{},
  314. }
  315. ip := netip.MustParsePrefix("9.254.254.254/32")
  316. for n := 0; n < b.N; n++ {
  317. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: ip.Addr()}, true, c, cp))
  318. }
  319. })
  320. b.Run("pass proto, port, fail on local CIDRv6", func(b *testing.B) {
  321. c := &cert.CachedCertificate{
  322. Certificate: &dummyCert{},
  323. }
  324. ip := netip.MustParsePrefix("fd99::99/128")
  325. for n := 0; n < b.N; n++ {
  326. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: ip.Addr()}, true, c, cp))
  327. }
  328. })
  329. b.Run("pass proto, port, any local CIDR, fail all group, name, and cidr", func(b *testing.B) {
  330. c := &cert.CachedCertificate{
  331. Certificate: &dummyCert{
  332. name: "nope",
  333. networks: []netip.Prefix{netip.MustParsePrefix("9.254.254.245/32")},
  334. },
  335. InvertedGroups: map[string]struct{}{"nope": {}},
  336. }
  337. for n := 0; n < b.N; n++ {
  338. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 10}, true, c, cp))
  339. }
  340. })
  341. b.Run("pass proto, port, any local CIDRv6, fail all group, name, and cidr", func(b *testing.B) {
  342. c := &cert.CachedCertificate{
  343. Certificate: &dummyCert{
  344. name: "nope",
  345. networks: []netip.Prefix{netip.MustParsePrefix("fd99::99/128")},
  346. },
  347. InvertedGroups: map[string]struct{}{"nope": {}},
  348. }
  349. for n := 0; n < b.N; n++ {
  350. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 10}, true, c, cp))
  351. }
  352. })
  353. b.Run("pass proto, port, specific local CIDR, fail all group, name, and cidr", func(b *testing.B) {
  354. c := &cert.CachedCertificate{
  355. Certificate: &dummyCert{
  356. name: "nope",
  357. networks: []netip.Prefix{netip.MustParsePrefix("9.254.254.245/32")},
  358. },
  359. InvertedGroups: map[string]struct{}{"nope": {}},
  360. }
  361. for n := 0; n < b.N; n++ {
  362. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: pfix.Addr()}, true, c, cp))
  363. }
  364. })
  365. b.Run("pass proto, port, specific local CIDRv6, fail all group, name, and cidr", func(b *testing.B) {
  366. c := &cert.CachedCertificate{
  367. Certificate: &dummyCert{
  368. name: "nope",
  369. networks: []netip.Prefix{netip.MustParsePrefix("fd99::99/128")},
  370. },
  371. InvertedGroups: map[string]struct{}{"nope": {}},
  372. }
  373. for n := 0; n < b.N; n++ {
  374. assert.False(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: pfix6.Addr()}, true, c, cp))
  375. }
  376. })
  377. b.Run("pass on group on any local cidr", func(b *testing.B) {
  378. c := &cert.CachedCertificate{
  379. Certificate: &dummyCert{
  380. name: "nope",
  381. },
  382. InvertedGroups: map[string]struct{}{"good-group": {}},
  383. }
  384. for n := 0; n < b.N; n++ {
  385. assert.True(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 10}, true, c, cp))
  386. }
  387. })
  388. b.Run("pass on group on specific local cidr", func(b *testing.B) {
  389. c := &cert.CachedCertificate{
  390. Certificate: &dummyCert{
  391. name: "nope",
  392. },
  393. InvertedGroups: map[string]struct{}{"good-group": {}},
  394. }
  395. for n := 0; n < b.N; n++ {
  396. assert.True(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: pfix.Addr()}, true, c, cp))
  397. }
  398. })
  399. b.Run("pass on group on specific local cidr6", func(b *testing.B) {
  400. c := &cert.CachedCertificate{
  401. Certificate: &dummyCert{
  402. name: "nope",
  403. },
  404. InvertedGroups: map[string]struct{}{"good-group": {}},
  405. }
  406. for n := 0; n < b.N; n++ {
  407. assert.True(b, ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 100, LocalAddr: pfix6.Addr()}, true, c, cp))
  408. }
  409. })
  410. b.Run("pass on name", func(b *testing.B) {
  411. c := &cert.CachedCertificate{
  412. Certificate: &dummyCert{
  413. name: "good-host",
  414. },
  415. InvertedGroups: map[string]struct{}{"nope": {}},
  416. }
  417. for n := 0; n < b.N; n++ {
  418. ft.match(firewall.Packet{Protocol: firewall.ProtoTCP, LocalPort: 10}, true, c, cp)
  419. }
  420. })
  421. }
  422. func TestFirewall_Drop2(t *testing.T) {
  423. l := test.NewLogger()
  424. ob := &bytes.Buffer{}
  425. l.SetOutput(ob)
  426. myVpnNetworksTable := new(bart.Lite)
  427. myVpnNetworksTable.Insert(netip.MustParsePrefix("1.1.1.1/8"))
  428. p := firewall.Packet{
  429. LocalAddr: netip.MustParseAddr("1.2.3.4"),
  430. RemoteAddr: netip.MustParseAddr("1.2.3.4"),
  431. LocalPort: 10,
  432. RemotePort: 90,
  433. Protocol: firewall.ProtoUDP,
  434. Fragment: false,
  435. }
  436. network := netip.MustParsePrefix("1.2.3.4/24")
  437. c := cert.CachedCertificate{
  438. Certificate: &dummyCert{
  439. name: "host1",
  440. networks: []netip.Prefix{network},
  441. },
  442. InvertedGroups: map[string]struct{}{"default-group": {}, "test-group": {}},
  443. }
  444. h := HostInfo{
  445. ConnectionState: &ConnectionState{
  446. peerCert: &c,
  447. },
  448. vpnAddrs: []netip.Addr{network.Addr()},
  449. }
  450. h.buildNetworks(myVpnNetworksTable, c.Certificate)
  451. c1 := cert.CachedCertificate{
  452. Certificate: &dummyCert{
  453. name: "host1",
  454. networks: []netip.Prefix{network},
  455. },
  456. InvertedGroups: map[string]struct{}{"default-group": {}, "test-group-not": {}},
  457. }
  458. h1 := HostInfo{
  459. vpnAddrs: []netip.Addr{network.Addr()},
  460. ConnectionState: &ConnectionState{
  461. peerCert: &c1,
  462. },
  463. }
  464. h1.buildNetworks(myVpnNetworksTable, c1.Certificate)
  465. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  466. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"default-group", "test-group"}, "", "", "", "", ""))
  467. cp := cert.NewCAPool()
  468. // h1/c1 lacks the proper groups
  469. require.ErrorIs(t, fw.Drop(p, true, &h1, cp, nil), ErrNoMatchingRule)
  470. // c has the proper groups
  471. resetConntrack(fw)
  472. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  473. }
  474. func TestFirewall_Drop3(t *testing.T) {
  475. l := test.NewLogger()
  476. ob := &bytes.Buffer{}
  477. l.SetOutput(ob)
  478. myVpnNetworksTable := new(bart.Lite)
  479. myVpnNetworksTable.Insert(netip.MustParsePrefix("1.1.1.1/8"))
  480. p := firewall.Packet{
  481. LocalAddr: netip.MustParseAddr("1.2.3.4"),
  482. RemoteAddr: netip.MustParseAddr("1.2.3.4"),
  483. LocalPort: 1,
  484. RemotePort: 1,
  485. Protocol: firewall.ProtoUDP,
  486. Fragment: false,
  487. }
  488. network := netip.MustParsePrefix("1.2.3.4/24")
  489. c := cert.CachedCertificate{
  490. Certificate: &dummyCert{
  491. name: "host-owner",
  492. networks: []netip.Prefix{network},
  493. },
  494. }
  495. c1 := cert.CachedCertificate{
  496. Certificate: &dummyCert{
  497. name: "host1",
  498. networks: []netip.Prefix{network},
  499. issuer: "signer-sha-bad",
  500. },
  501. }
  502. h1 := HostInfo{
  503. ConnectionState: &ConnectionState{
  504. peerCert: &c1,
  505. },
  506. vpnAddrs: []netip.Addr{network.Addr()},
  507. }
  508. h1.buildNetworks(myVpnNetworksTable, c1.Certificate)
  509. c2 := cert.CachedCertificate{
  510. Certificate: &dummyCert{
  511. name: "host2",
  512. networks: []netip.Prefix{network},
  513. issuer: "signer-sha",
  514. },
  515. }
  516. h2 := HostInfo{
  517. ConnectionState: &ConnectionState{
  518. peerCert: &c2,
  519. },
  520. vpnAddrs: []netip.Addr{network.Addr()},
  521. }
  522. h2.buildNetworks(myVpnNetworksTable, c2.Certificate)
  523. c3 := cert.CachedCertificate{
  524. Certificate: &dummyCert{
  525. name: "host3",
  526. networks: []netip.Prefix{network},
  527. issuer: "signer-sha-bad",
  528. },
  529. }
  530. h3 := HostInfo{
  531. ConnectionState: &ConnectionState{
  532. peerCert: &c3,
  533. },
  534. vpnAddrs: []netip.Addr{network.Addr()},
  535. }
  536. h3.buildNetworks(myVpnNetworksTable, c3.Certificate)
  537. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  538. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 1, 1, []string{}, "host1", "", "", "", ""))
  539. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 1, 1, []string{}, "", "", "", "", "signer-sha"))
  540. cp := cert.NewCAPool()
  541. // c1 should pass because host match
  542. require.NoError(t, fw.Drop(p, true, &h1, cp, nil))
  543. // c2 should pass because ca sha match
  544. resetConntrack(fw)
  545. require.NoError(t, fw.Drop(p, true, &h2, cp, nil))
  546. // c3 should fail because no match
  547. resetConntrack(fw)
  548. assert.Equal(t, fw.Drop(p, true, &h3, cp, nil), ErrNoMatchingRule)
  549. // Test a remote address match
  550. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  551. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 1, 1, []string{}, "", "1.2.3.4/24", "", "", ""))
  552. require.NoError(t, fw.Drop(p, true, &h1, cp, nil))
  553. }
  554. func TestFirewall_Drop3V6(t *testing.T) {
  555. l := test.NewLogger()
  556. ob := &bytes.Buffer{}
  557. l.SetOutput(ob)
  558. myVpnNetworksTable := new(bart.Lite)
  559. myVpnNetworksTable.Insert(netip.MustParsePrefix("fd00::/7"))
  560. p := firewall.Packet{
  561. LocalAddr: netip.MustParseAddr("fd12::34"),
  562. RemoteAddr: netip.MustParseAddr("fd12::34"),
  563. LocalPort: 1,
  564. RemotePort: 1,
  565. Protocol: firewall.ProtoUDP,
  566. Fragment: false,
  567. }
  568. network := netip.MustParsePrefix("fd12::34/120")
  569. c := cert.CachedCertificate{
  570. Certificate: &dummyCert{
  571. name: "host-owner",
  572. networks: []netip.Prefix{network},
  573. },
  574. }
  575. h := HostInfo{
  576. ConnectionState: &ConnectionState{
  577. peerCert: &c,
  578. },
  579. vpnAddrs: []netip.Addr{network.Addr()},
  580. }
  581. h.buildNetworks(myVpnNetworksTable, c.Certificate)
  582. // Test a remote address match
  583. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  584. cp := cert.NewCAPool()
  585. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 1, 1, []string{}, "", "fd12::34/120", "", "", ""))
  586. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  587. }
  588. func TestFirewall_DropConntrackReload(t *testing.T) {
  589. l := test.NewLogger()
  590. ob := &bytes.Buffer{}
  591. l.SetOutput(ob)
  592. myVpnNetworksTable := new(bart.Lite)
  593. myVpnNetworksTable.Insert(netip.MustParsePrefix("1.1.1.1/8"))
  594. p := firewall.Packet{
  595. LocalAddr: netip.MustParseAddr("1.2.3.4"),
  596. RemoteAddr: netip.MustParseAddr("1.2.3.4"),
  597. LocalPort: 10,
  598. RemotePort: 90,
  599. Protocol: firewall.ProtoUDP,
  600. Fragment: false,
  601. }
  602. network := netip.MustParsePrefix("1.2.3.4/24")
  603. c := cert.CachedCertificate{
  604. Certificate: &dummyCert{
  605. name: "host1",
  606. networks: []netip.Prefix{network},
  607. groups: []string{"default-group"},
  608. issuer: "signer-shasum",
  609. },
  610. InvertedGroups: map[string]struct{}{"default-group": {}},
  611. }
  612. h := HostInfo{
  613. ConnectionState: &ConnectionState{
  614. peerCert: &c,
  615. },
  616. vpnAddrs: []netip.Addr{network.Addr()},
  617. }
  618. h.buildNetworks(myVpnNetworksTable, c.Certificate)
  619. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  620. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"any"}, "", "", "", "", ""))
  621. cp := cert.NewCAPool()
  622. // Drop outbound
  623. assert.Equal(t, fw.Drop(p, false, &h, cp, nil), ErrNoMatchingRule)
  624. // Allow inbound
  625. resetConntrack(fw)
  626. require.NoError(t, fw.Drop(p, true, &h, cp, nil))
  627. // Allow outbound because conntrack
  628. require.NoError(t, fw.Drop(p, false, &h, cp, nil))
  629. oldFw := fw
  630. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  631. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 10, 10, []string{"any"}, "", "", "", "", ""))
  632. fw.Conntrack = oldFw.Conntrack
  633. fw.rulesVersion = oldFw.rulesVersion + 1
  634. // Allow outbound because conntrack and new rules allow port 10
  635. require.NoError(t, fw.Drop(p, false, &h, cp, nil))
  636. oldFw = fw
  637. fw = NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  638. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 11, 11, []string{"any"}, "", "", "", "", ""))
  639. fw.Conntrack = oldFw.Conntrack
  640. fw.rulesVersion = oldFw.rulesVersion + 1
  641. // Drop outbound because conntrack doesn't match new ruleset
  642. assert.Equal(t, fw.Drop(p, false, &h, cp, nil), ErrNoMatchingRule)
  643. }
  644. func TestFirewall_DropIPSpoofing(t *testing.T) {
  645. l := test.NewLogger()
  646. ob := &bytes.Buffer{}
  647. l.SetOutput(ob)
  648. myVpnNetworksTable := new(bart.Lite)
  649. myVpnNetworksTable.Insert(netip.MustParsePrefix("192.0.2.1/24"))
  650. c := cert.CachedCertificate{
  651. Certificate: &dummyCert{
  652. name: "host-owner",
  653. networks: []netip.Prefix{netip.MustParsePrefix("192.0.2.1/24")},
  654. },
  655. }
  656. c1 := cert.CachedCertificate{
  657. Certificate: &dummyCert{
  658. name: "host",
  659. networks: []netip.Prefix{netip.MustParsePrefix("192.0.2.2/24")},
  660. unsafeNetworks: []netip.Prefix{netip.MustParsePrefix("198.51.100.0/24")},
  661. },
  662. }
  663. h1 := HostInfo{
  664. ConnectionState: &ConnectionState{
  665. peerCert: &c1,
  666. },
  667. vpnAddrs: []netip.Addr{c1.Certificate.Networks()[0].Addr()},
  668. }
  669. h1.buildNetworks(myVpnNetworksTable, c1.Certificate)
  670. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, c.Certificate)
  671. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 1, 1, []string{}, "", "", "", "", ""))
  672. cp := cert.NewCAPool()
  673. // Packet spoofed by `c1`. Note that the remote addr is not a valid one.
  674. p := firewall.Packet{
  675. LocalAddr: netip.MustParseAddr("192.0.2.1"),
  676. RemoteAddr: netip.MustParseAddr("192.0.2.3"),
  677. LocalPort: 1,
  678. RemotePort: 1,
  679. Protocol: firewall.ProtoUDP,
  680. Fragment: false,
  681. }
  682. assert.Equal(t, fw.Drop(p, true, &h1, cp, nil), ErrInvalidRemoteIP)
  683. }
  684. func BenchmarkLookup(b *testing.B) {
  685. ml := func(m map[string]struct{}, a [][]string) {
  686. for n := 0; n < b.N; n++ {
  687. for _, sg := range a {
  688. found := false
  689. for _, g := range sg {
  690. if _, ok := m[g]; !ok {
  691. found = false
  692. break
  693. }
  694. found = true
  695. }
  696. if found {
  697. return
  698. }
  699. }
  700. }
  701. }
  702. b.Run("array to map best", func(b *testing.B) {
  703. m := map[string]struct{}{
  704. "1ne": {},
  705. "2wo": {},
  706. "3hr": {},
  707. "4ou": {},
  708. "5iv": {},
  709. "6ix": {},
  710. }
  711. a := [][]string{
  712. {"1ne", "2wo", "3hr", "4ou", "5iv", "6ix"},
  713. {"one", "2wo", "3hr", "4ou", "5iv", "6ix"},
  714. {"one", "two", "3hr", "4ou", "5iv", "6ix"},
  715. {"one", "two", "thr", "4ou", "5iv", "6ix"},
  716. {"one", "two", "thr", "fou", "5iv", "6ix"},
  717. {"one", "two", "thr", "fou", "fiv", "6ix"},
  718. {"one", "two", "thr", "fou", "fiv", "six"},
  719. }
  720. for n := 0; n < b.N; n++ {
  721. ml(m, a)
  722. }
  723. })
  724. b.Run("array to map worst", func(b *testing.B) {
  725. m := map[string]struct{}{
  726. "one": {},
  727. "two": {},
  728. "thr": {},
  729. "fou": {},
  730. "fiv": {},
  731. "six": {},
  732. }
  733. a := [][]string{
  734. {"1ne", "2wo", "3hr", "4ou", "5iv", "6ix"},
  735. {"one", "2wo", "3hr", "4ou", "5iv", "6ix"},
  736. {"one", "two", "3hr", "4ou", "5iv", "6ix"},
  737. {"one", "two", "thr", "4ou", "5iv", "6ix"},
  738. {"one", "two", "thr", "fou", "5iv", "6ix"},
  739. {"one", "two", "thr", "fou", "fiv", "6ix"},
  740. {"one", "two", "thr", "fou", "fiv", "six"},
  741. }
  742. for n := 0; n < b.N; n++ {
  743. ml(m, a)
  744. }
  745. })
  746. }
  747. func Test_parsePort(t *testing.T) {
  748. _, _, err := parsePort("")
  749. require.EqualError(t, err, "was not a number; ``")
  750. _, _, err = parsePort(" ")
  751. require.EqualError(t, err, "was not a number; ` `")
  752. _, _, err = parsePort("-")
  753. require.EqualError(t, err, "appears to be a range but could not be parsed; `-`")
  754. _, _, err = parsePort(" - ")
  755. require.EqualError(t, err, "appears to be a range but could not be parsed; ` - `")
  756. _, _, err = parsePort("a-b")
  757. require.EqualError(t, err, "beginning range was not a number; `a`")
  758. _, _, err = parsePort("1-b")
  759. require.EqualError(t, err, "ending range was not a number; `b`")
  760. s, e, err := parsePort(" 1 - 2 ")
  761. assert.Equal(t, int32(1), s)
  762. assert.Equal(t, int32(2), e)
  763. require.NoError(t, err)
  764. s, e, err = parsePort("0-1")
  765. assert.Equal(t, int32(0), s)
  766. assert.Equal(t, int32(0), e)
  767. require.NoError(t, err)
  768. s, e, err = parsePort("9919")
  769. assert.Equal(t, int32(9919), s)
  770. assert.Equal(t, int32(9919), e)
  771. require.NoError(t, err)
  772. s, e, err = parsePort("any")
  773. assert.Equal(t, int32(0), s)
  774. assert.Equal(t, int32(0), e)
  775. require.NoError(t, err)
  776. }
  777. func TestNewFirewallFromConfig(t *testing.T) {
  778. l := test.NewLogger()
  779. // Test a bad rule definition
  780. c := &dummyCert{}
  781. cs, err := newCertState(cert.Version2, nil, c, false, cert.Curve_CURVE25519, nil)
  782. require.NoError(t, err)
  783. conf := config.NewC(l)
  784. conf.Settings["firewall"] = map[string]any{"outbound": "asdf"}
  785. _, err = NewFirewallFromConfig(l, cs, conf)
  786. require.EqualError(t, err, "firewall.outbound failed to parse, should be an array of rules")
  787. // Test both port and code
  788. conf = config.NewC(l)
  789. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "code": "2"}}}
  790. _, err = NewFirewallFromConfig(l, cs, conf)
  791. require.EqualError(t, err, "firewall.outbound rule #0; only one of port or code should be provided")
  792. // Test missing host, group, cidr, ca_name and ca_sha
  793. conf = config.NewC(l)
  794. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{}}}
  795. _, err = NewFirewallFromConfig(l, cs, conf)
  796. require.EqualError(t, err, "firewall.outbound rule #0; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided")
  797. // Test code/port error
  798. conf = config.NewC(l)
  799. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "a", "host": "testh"}}}
  800. _, err = NewFirewallFromConfig(l, cs, conf)
  801. require.EqualError(t, err, "firewall.outbound rule #0; code was not a number; `a`")
  802. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "a", "host": "testh"}}}
  803. _, err = NewFirewallFromConfig(l, cs, conf)
  804. require.EqualError(t, err, "firewall.outbound rule #0; port was not a number; `a`")
  805. // Test proto error
  806. conf = config.NewC(l)
  807. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "host": "testh"}}}
  808. _, err = NewFirewallFromConfig(l, cs, conf)
  809. require.EqualError(t, err, "firewall.outbound rule #0; proto was not understood; ``")
  810. // Test cidr parse error
  811. conf = config.NewC(l)
  812. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "cidr": "testh", "proto": "any"}}}
  813. _, err = NewFirewallFromConfig(l, cs, conf)
  814. require.EqualError(t, err, "firewall.outbound rule #0; cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
  815. // Test local_cidr parse error
  816. conf = config.NewC(l)
  817. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "local_cidr": "testh", "proto": "any"}}}
  818. _, err = NewFirewallFromConfig(l, cs, conf)
  819. require.EqualError(t, err, "firewall.outbound rule #0; local_cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
  820. // Test both group and groups
  821. conf = config.NewC(l)
  822. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "group": "a", "groups": []string{"b", "c"}}}}
  823. _, err = NewFirewallFromConfig(l, cs, conf)
  824. require.EqualError(t, err, "firewall.inbound rule #0; only one of group or groups should be defined, both provided")
  825. }
  826. func TestAddFirewallRulesFromConfig(t *testing.T) {
  827. l := test.NewLogger()
  828. // Test adding tcp rule
  829. conf := config.NewC(l)
  830. mf := &mockFirewall{}
  831. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "tcp", "host": "a"}}}
  832. require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
  833. assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoTCP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: "", localIp: ""}, mf.lastCall)
  834. // Test adding udp rule
  835. conf = config.NewC(l)
  836. mf = &mockFirewall{}
  837. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "udp", "host": "a"}}}
  838. require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
  839. assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoUDP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: "", localIp: ""}, mf.lastCall)
  840. // Test adding icmp rule
  841. conf = config.NewC(l)
  842. mf = &mockFirewall{}
  843. conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "icmp", "host": "a"}}}
  844. require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
  845. assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoICMP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: "", localIp: ""}, mf.lastCall)
  846. // Test adding any rule
  847. conf = config.NewC(l)
  848. mf = &mockFirewall{}
  849. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "host": "a"}}}
  850. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  851. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, host: "a", ip: "", localIp: ""}, mf.lastCall)
  852. // Test adding rule with cidr
  853. cidr := netip.MustParsePrefix("10.0.0.0/8")
  854. conf = config.NewC(l)
  855. mf = &mockFirewall{}
  856. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "cidr": cidr.String()}}}
  857. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  858. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: cidr.String(), localIp: ""}, mf.lastCall)
  859. // Test adding rule with local_cidr
  860. conf = config.NewC(l)
  861. mf = &mockFirewall{}
  862. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "local_cidr": cidr.String()}}}
  863. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  864. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: "", localIp: cidr.String()}, mf.lastCall)
  865. // Test adding rule with cidr ipv6
  866. cidr6 := netip.MustParsePrefix("fd00::/8")
  867. conf = config.NewC(l)
  868. mf = &mockFirewall{}
  869. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "cidr": cidr6.String()}}}
  870. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  871. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: cidr6.String(), localIp: ""}, mf.lastCall)
  872. // Test adding rule with any cidr
  873. conf = config.NewC(l)
  874. mf = &mockFirewall{}
  875. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "cidr": "any"}}}
  876. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  877. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: "any", localIp: ""}, mf.lastCall)
  878. // Test adding rule with junk cidr
  879. conf = config.NewC(l)
  880. mf = &mockFirewall{}
  881. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "cidr": "junk/junk"}}}
  882. require.EqualError(t, AddFirewallRulesFromConfig(l, true, conf, mf), "firewall.inbound rule #0; cidr did not parse; netip.ParsePrefix(\"junk/junk\"): ParseAddr(\"junk\"): unable to parse IP")
  883. // Test adding rule with local_cidr ipv6
  884. conf = config.NewC(l)
  885. mf = &mockFirewall{}
  886. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "local_cidr": cidr6.String()}}}
  887. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  888. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: "", localIp: cidr6.String()}, mf.lastCall)
  889. // Test adding rule with any local_cidr
  890. conf = config.NewC(l)
  891. mf = &mockFirewall{}
  892. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "local_cidr": "any"}}}
  893. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  894. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, localIp: "any"}, mf.lastCall)
  895. // Test adding rule with junk local_cidr
  896. conf = config.NewC(l)
  897. mf = &mockFirewall{}
  898. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "local_cidr": "junk/junk"}}}
  899. require.EqualError(t, AddFirewallRulesFromConfig(l, true, conf, mf), "firewall.inbound rule #0; local_cidr did not parse; netip.ParsePrefix(\"junk/junk\"): ParseAddr(\"junk\"): unable to parse IP")
  900. // Test adding rule with ca_sha
  901. conf = config.NewC(l)
  902. mf = &mockFirewall{}
  903. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "ca_sha": "12312313123"}}}
  904. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  905. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: "", localIp: "", caSha: "12312313123"}, mf.lastCall)
  906. // Test adding rule with ca_name
  907. conf = config.NewC(l)
  908. mf = &mockFirewall{}
  909. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "ca_name": "root01"}}}
  910. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  911. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: "", localIp: "", caName: "root01"}, mf.lastCall)
  912. // Test single group
  913. conf = config.NewC(l)
  914. mf = &mockFirewall{}
  915. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "group": "a"}}}
  916. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  917. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: "", localIp: ""}, mf.lastCall)
  918. // Test single groups
  919. conf = config.NewC(l)
  920. mf = &mockFirewall{}
  921. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "groups": "a"}}}
  922. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  923. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: "", localIp: ""}, mf.lastCall)
  924. // Test multiple AND groups
  925. conf = config.NewC(l)
  926. mf = &mockFirewall{}
  927. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "groups": []string{"a", "b"}}}}
  928. require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
  929. assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a", "b"}, ip: "", localIp: ""}, mf.lastCall)
  930. // Test Add error
  931. conf = config.NewC(l)
  932. mf = &mockFirewall{}
  933. mf.nextCallReturn = errors.New("test error")
  934. conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "host": "a"}}}
  935. require.EqualError(t, AddFirewallRulesFromConfig(l, true, conf, mf), "firewall.inbound rule #0; `test error`")
  936. }
  937. func TestFirewall_convertRule(t *testing.T) {
  938. l := test.NewLogger()
  939. ob := &bytes.Buffer{}
  940. l.SetOutput(ob)
  941. // Ensure group array of 1 is converted and a warning is printed
  942. c := map[string]any{
  943. "group": []any{"group1"},
  944. }
  945. r, err := convertRule(l, c, "test", 1)
  946. assert.Contains(t, ob.String(), "test rule #1; group was an array with a single value, converting to simple value")
  947. require.NoError(t, err)
  948. assert.Equal(t, []string{"group1"}, r.Groups)
  949. // Ensure group array of > 1 is errord
  950. ob.Reset()
  951. c = map[string]any{
  952. "group": []any{"group1", "group2"},
  953. }
  954. r, err = convertRule(l, c, "test", 1)
  955. assert.Empty(t, ob.String())
  956. require.Error(t, err, "group should contain a single value, an array with more than one entry was provided")
  957. // Make sure a well formed group is alright
  958. ob.Reset()
  959. c = map[string]any{
  960. "group": "group1",
  961. }
  962. r, err = convertRule(l, c, "test", 1)
  963. require.NoError(t, err)
  964. assert.Equal(t, []string{"group1"}, r.Groups)
  965. }
  966. func TestFirewall_convertRuleSanity(t *testing.T) {
  967. l := test.NewLogger()
  968. ob := &bytes.Buffer{}
  969. l.SetOutput(ob)
  970. noWarningPlease := []map[string]any{
  971. {"group": "group1"},
  972. {"groups": []any{"group2"}},
  973. {"host": "bob"},
  974. {"cidr": "1.1.1.1/1"},
  975. {"groups": []any{"group2"}, "host": "bob"},
  976. {"cidr": "1.1.1.1/1", "host": "bob"},
  977. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1"},
  978. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1", "host": "bob"},
  979. }
  980. for _, c := range noWarningPlease {
  981. r, err := convertRule(l, c, "test", 1)
  982. require.NoError(t, err)
  983. require.NoError(t, r.sanity(), "should not generate a sanity warning, %+v", c)
  984. }
  985. yesWarningPlease := []map[string]any{
  986. {"group": "group1"},
  987. {"groups": []any{"group2"}},
  988. {"cidr": "1.1.1.1/1"},
  989. {"groups": []any{"group2"}, "host": "bob"},
  990. {"cidr": "1.1.1.1/1", "host": "bob"},
  991. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1"},
  992. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1", "host": "bob"},
  993. }
  994. for _, c := range yesWarningPlease {
  995. c["host"] = "any"
  996. r, err := convertRule(l, c, "test", 1)
  997. require.NoError(t, err)
  998. err = r.sanity()
  999. require.Error(t, err, "I wanted a warning: %+v", c)
  1000. }
  1001. //reset the list
  1002. yesWarningPlease = []map[string]any{
  1003. {"group": "group1"},
  1004. {"groups": []any{"group2"}},
  1005. {"cidr": "1.1.1.1/1"},
  1006. {"groups": []any{"group2"}, "host": "bob"},
  1007. {"cidr": "1.1.1.1/1", "host": "bob"},
  1008. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1"},
  1009. {"groups": []any{"group2"}, "cidr": "1.1.1.1/1", "host": "bob"},
  1010. }
  1011. for _, c := range yesWarningPlease {
  1012. r, err := convertRule(l, c, "test", 1)
  1013. require.NoError(t, err)
  1014. r.Groups = append(r.Groups, "any")
  1015. err = r.sanity()
  1016. require.Error(t, err, "I wanted a warning: %+v", c)
  1017. }
  1018. }
  1019. type testcase struct {
  1020. h *HostInfo
  1021. p firewall.Packet
  1022. c cert.Certificate
  1023. err error
  1024. }
  1025. func (c *testcase) Test(t *testing.T, fw *Firewall) {
  1026. t.Helper()
  1027. cp := cert.NewCAPool()
  1028. resetConntrack(fw)
  1029. err := fw.Drop(c.p, true, c.h, cp, nil)
  1030. if c.err == nil {
  1031. require.NoError(t, err, "failed to not drop remote address %s", c.p.RemoteAddr)
  1032. } else {
  1033. require.ErrorIs(t, c.err, err, "failed to drop remote address %s", c.p.RemoteAddr)
  1034. }
  1035. }
  1036. func buildTestCase(setup testsetup, err error, theirPrefixes ...netip.Prefix) testcase {
  1037. c1 := dummyCert{
  1038. name: "host1",
  1039. networks: theirPrefixes,
  1040. groups: []string{"default-group"},
  1041. issuer: "signer-shasum",
  1042. }
  1043. h := HostInfo{
  1044. ConnectionState: &ConnectionState{
  1045. peerCert: &cert.CachedCertificate{
  1046. Certificate: &c1,
  1047. InvertedGroups: map[string]struct{}{"default-group": {}},
  1048. },
  1049. },
  1050. vpnAddrs: make([]netip.Addr, len(theirPrefixes)),
  1051. }
  1052. for i := range theirPrefixes {
  1053. h.vpnAddrs[i] = theirPrefixes[i].Addr()
  1054. }
  1055. h.buildNetworks(setup.myVpnNetworksTable, &c1)
  1056. p := firewall.Packet{
  1057. LocalAddr: setup.c.Networks()[0].Addr(), //todo?
  1058. RemoteAddr: theirPrefixes[0].Addr(),
  1059. LocalPort: 10,
  1060. RemotePort: 90,
  1061. Protocol: firewall.ProtoUDP,
  1062. Fragment: false,
  1063. }
  1064. return testcase{
  1065. h: &h,
  1066. p: p,
  1067. c: &c1,
  1068. err: err,
  1069. }
  1070. }
  1071. type testsetup struct {
  1072. c dummyCert
  1073. myVpnNetworksTable *bart.Lite
  1074. fw *Firewall
  1075. }
  1076. func newSetup(t *testing.T, l *logrus.Logger, myPrefixes ...netip.Prefix) testsetup {
  1077. c := dummyCert{
  1078. name: "me",
  1079. networks: myPrefixes,
  1080. groups: []string{"default-group"},
  1081. issuer: "signer-shasum",
  1082. }
  1083. return newSetupFromCert(t, l, c)
  1084. }
  1085. func newSetupFromCert(t *testing.T, l *logrus.Logger, c dummyCert) testsetup {
  1086. myVpnNetworksTable := new(bart.Lite)
  1087. for _, prefix := range c.Networks() {
  1088. myVpnNetworksTable.Insert(prefix)
  1089. }
  1090. fw := NewFirewall(l, time.Second, time.Minute, time.Hour, &c)
  1091. require.NoError(t, fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"any"}, "", "", "", "", ""))
  1092. return testsetup{
  1093. c: c,
  1094. fw: fw,
  1095. myVpnNetworksTable: myVpnNetworksTable,
  1096. }
  1097. }
  1098. func TestFirewall_Drop_EnforceIPMatch(t *testing.T) {
  1099. t.Parallel()
  1100. l := test.NewLogger()
  1101. ob := &bytes.Buffer{}
  1102. l.SetOutput(ob)
  1103. myPrefix := netip.MustParsePrefix("1.1.1.1/8")
  1104. // for now, it's okay that these are all "incoming", the logic this test tries to check doesn't care about in/out
  1105. t.Run("allow inbound all matching", func(t *testing.T) {
  1106. t.Parallel()
  1107. setup := newSetup(t, l, myPrefix)
  1108. tc := buildTestCase(setup, nil, netip.MustParsePrefix("1.2.3.4/24"))
  1109. tc.Test(t, setup.fw)
  1110. })
  1111. t.Run("allow inbound local matching", func(t *testing.T) {
  1112. t.Parallel()
  1113. setup := newSetup(t, l, myPrefix)
  1114. tc := buildTestCase(setup, ErrInvalidLocalIP, netip.MustParsePrefix("1.2.3.4/24"))
  1115. tc.p.LocalAddr = netip.MustParseAddr("1.2.3.8")
  1116. tc.Test(t, setup.fw)
  1117. })
  1118. t.Run("block inbound remote mismatched", func(t *testing.T) {
  1119. t.Parallel()
  1120. setup := newSetup(t, l, myPrefix)
  1121. tc := buildTestCase(setup, ErrInvalidRemoteIP, netip.MustParsePrefix("1.2.3.4/24"))
  1122. tc.p.RemoteAddr = netip.MustParseAddr("9.9.9.9")
  1123. tc.Test(t, setup.fw)
  1124. })
  1125. t.Run("Block a vpn peer packet", func(t *testing.T) {
  1126. t.Parallel()
  1127. setup := newSetup(t, l, myPrefix)
  1128. tc := buildTestCase(setup, ErrPeerRejected, netip.MustParsePrefix("2.2.2.2/24"))
  1129. tc.Test(t, setup.fw)
  1130. })
  1131. twoPrefixes := []netip.Prefix{
  1132. netip.MustParsePrefix("1.2.3.4/24"), netip.MustParsePrefix("2.2.2.2/24"),
  1133. }
  1134. t.Run("allow inbound one matching", func(t *testing.T) {
  1135. t.Parallel()
  1136. setup := newSetup(t, l, myPrefix)
  1137. tc := buildTestCase(setup, nil, twoPrefixes...)
  1138. tc.Test(t, setup.fw)
  1139. })
  1140. t.Run("block inbound multimismatch", func(t *testing.T) {
  1141. t.Parallel()
  1142. setup := newSetup(t, l, myPrefix)
  1143. tc := buildTestCase(setup, ErrInvalidRemoteIP, twoPrefixes...)
  1144. tc.p.RemoteAddr = netip.MustParseAddr("9.9.9.9")
  1145. tc.Test(t, setup.fw)
  1146. })
  1147. t.Run("allow inbound 2nd one matching", func(t *testing.T) {
  1148. t.Parallel()
  1149. setup2 := newSetup(t, l, netip.MustParsePrefix("2.2.2.1/24"))
  1150. tc := buildTestCase(setup2, nil, twoPrefixes...)
  1151. tc.p.RemoteAddr = twoPrefixes[1].Addr()
  1152. tc.Test(t, setup2.fw)
  1153. })
  1154. t.Run("allow inbound unsafe route", func(t *testing.T) {
  1155. t.Parallel()
  1156. unsafePrefix := netip.MustParsePrefix("192.168.0.0/24")
  1157. c := dummyCert{
  1158. name: "me",
  1159. networks: []netip.Prefix{myPrefix},
  1160. unsafeNetworks: []netip.Prefix{unsafePrefix},
  1161. groups: []string{"default-group"},
  1162. issuer: "signer-shasum",
  1163. }
  1164. unsafeSetup := newSetupFromCert(t, l, c)
  1165. tc := buildTestCase(unsafeSetup, nil, twoPrefixes...)
  1166. tc.p.LocalAddr = netip.MustParseAddr("192.168.0.3")
  1167. tc.err = ErrNoMatchingRule
  1168. tc.Test(t, unsafeSetup.fw) //should hit firewall and bounce off
  1169. require.NoError(t, unsafeSetup.fw.AddRule(true, firewall.ProtoAny, 0, 0, []string{"any"}, "", "", unsafePrefix.String(), "", ""))
  1170. tc.err = nil
  1171. tc.Test(t, unsafeSetup.fw) //should pass
  1172. })
  1173. }
  1174. type addRuleCall struct {
  1175. incoming bool
  1176. proto uint8
  1177. startPort int32
  1178. endPort int32
  1179. groups []string
  1180. host string
  1181. ip string
  1182. localIp string
  1183. caName string
  1184. caSha string
  1185. }
  1186. type mockFirewall struct {
  1187. lastCall addRuleCall
  1188. nextCallReturn error
  1189. }
  1190. func (mf *mockFirewall) AddRule(incoming bool, proto uint8, startPort int32, endPort int32, groups []string, host string, ip, localIp, caName string, caSha string) error {
  1191. mf.lastCall = addRuleCall{
  1192. incoming: incoming,
  1193. proto: proto,
  1194. startPort: startPort,
  1195. endPort: endPort,
  1196. groups: groups,
  1197. host: host,
  1198. ip: ip,
  1199. localIp: localIp,
  1200. caName: caName,
  1201. caSha: caSha,
  1202. }
  1203. err := mf.nextCallReturn
  1204. mf.nextCallReturn = nil
  1205. return err
  1206. }
  1207. func resetConntrack(fw *Firewall) {
  1208. fw.Conntrack.Lock()
  1209. fw.Conntrack.Conns = map[firewall.Packet]*conn{}
  1210. fw.Conntrack.Unlock()
  1211. }