1
0

table.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. package firewall
  2. import (
  3. "net/netip"
  4. "github.com/gaissmai/bart"
  5. "github.com/slackhq/nebula/cert"
  6. "github.com/slackhq/nebula/iputil"
  7. )
  8. // Rules are the Tables built for each direction, with hashes of the rules they were built from
  9. type Rules struct {
  10. In, Out *Table
  11. // Hash is a sha256 of the rules, HashFNV is an FNV-1a of them for use as a metric value.
  12. // Rule sets with the same rules have the same hashes.
  13. Hash string
  14. HashFNV uint32
  15. }
  16. // Table holds the rules for one direction, the evaluation order is:
  17. // Proto AND port AND (CA SHA or CA name) AND local CIDR AND (group OR groups OR name OR remote CIDR)
  18. // A Table doesn't change once built, see RulesBuilder.
  19. type Table struct {
  20. // protos holds the rules for each IP protocol number, so a packet is only checked against one set of rules.
  21. // A protocol with rules of its own also holds a copy of every proto `any` rule, the rest share the proto `any`
  22. // rules, which are at protos[ProtoAny]. nil when a protocol has no rules at all.
  23. // ICMP and ICMPv6 share one set of rules.
  24. protos [256]portRules
  25. }
  26. // Even though ports are uint16, int32 maps are faster for lookup
  27. // Plus we can use `-1` for fragment rules
  28. type portRules map[int32]*caRules
  29. // caRules and the types below it only make their maps and tables once a rule needs them, a range of ports
  30. // gets a copy for every port so empty ones add up
  31. type caRules struct {
  32. Any *remoteRules
  33. CANames map[string]*remoteRules
  34. CAShas map[string]*remoteRules
  35. }
  36. type remoteRules struct {
  37. // Any makes Hosts, Groups, and CIDR irrelevant
  38. Any *localRules
  39. Hosts map[string]*localRules
  40. Groups []*groupsRule
  41. CIDR *bart.Table[*localRules]
  42. }
  43. type groupsRule struct {
  44. Groups []string
  45. LocalCIDR *localRules
  46. }
  47. type localRules struct {
  48. Any bool
  49. // LocalCIDR is always set when Any isn't. It's shared with other localRules, so it's never changed.
  50. LocalCIDR *bart.Lite
  51. }
  52. // Match reports whether a rule for the direction p is going allows it
  53. func (r *Rules) Match(p *Packet, incoming bool, c *cert.CachedCertificate, caPool *cert.CAPool) bool {
  54. t := r.Out
  55. if incoming {
  56. t = r.In
  57. }
  58. return t.Match(p, incoming, c, caPool)
  59. }
  60. // Match reports whether a rule allows p
  61. func (t *Table) Match(p *Packet, incoming bool, c *cert.CachedCertificate, caPool *cert.CAPool) bool {
  62. return t.protos[p.Protocol].match(p, incoming, c, caPool)
  63. }
  64. func (pr portRules) match(p *Packet, incoming bool, c *cert.CachedCertificate, caPool *cert.CAPool) bool {
  65. // We don't have any allowed ports, bail
  66. if pr == nil {
  67. return false
  68. }
  69. // ICMP has no ports, only the port `any` rules apply, including the ones copied in from proto `any` rules
  70. if p.Protocol == iputil.IPProtocolICMP || p.Protocol == iputil.IPProtocolICMPv6 {
  71. // port numbers are re-used for connection tracking of ICMP,
  72. // but we don't want to actually filter on them.
  73. return pr[PortAny].match(p, c, caPool)
  74. }
  75. var port int32
  76. if p.Fragment {
  77. port = PortFragment
  78. } else if incoming {
  79. port = int32(p.LocalPort)
  80. } else {
  81. port = int32(p.RemotePort)
  82. }
  83. // Packets without ports (gre, esp, etc) have port 0, which is PortAny, don't check those rules twice
  84. if port != PortAny && pr[port].match(p, c, caPool) {
  85. return true
  86. }
  87. return pr[PortAny].match(p, c, caPool)
  88. }
  89. func (cr *caRules) match(p *Packet, c *cert.CachedCertificate, caPool *cert.CAPool) bool {
  90. if cr == nil {
  91. return false
  92. }
  93. if cr.Any.match(p, c) {
  94. return true
  95. }
  96. if t, ok := cr.CAShas[c.Certificate.Issuer()]; ok {
  97. if t.match(p, c) {
  98. return true
  99. }
  100. }
  101. s, err := caPool.GetCAForCert(c.Certificate)
  102. if err != nil {
  103. return false
  104. }
  105. return cr.CANames[s.Certificate.Name()].match(p, c)
  106. }
  107. func (rr *remoteRules) match(p *Packet, c *cert.CachedCertificate) bool {
  108. if rr == nil {
  109. return false
  110. }
  111. // Shortcut path for if groups, hosts, or cidr contained an `any`
  112. if rr.Any.match(p, c) {
  113. return true
  114. }
  115. // Need any of group, host, or cidr to match
  116. for _, sg := range rr.Groups {
  117. found := false
  118. for _, g := range sg.Groups {
  119. if _, ok := c.InvertedGroups[g]; !ok {
  120. found = false
  121. break
  122. }
  123. found = true
  124. }
  125. if found && sg.LocalCIDR.match(p, c) {
  126. return true
  127. }
  128. }
  129. if rr.Hosts != nil {
  130. if lr, ok := rr.Hosts[c.Certificate.Name()]; ok {
  131. if lr.match(p, c) {
  132. return true
  133. }
  134. }
  135. }
  136. if rr.CIDR != nil {
  137. for _, v := range rr.CIDR.Supernets(netip.PrefixFrom(p.RemoteAddr, p.RemoteAddr.BitLen())) {
  138. if v.match(p, c) {
  139. return true
  140. }
  141. }
  142. }
  143. return false
  144. }
  145. func (lr *localRules) match(p *Packet, c *cert.CachedCertificate) bool {
  146. if lr == nil {
  147. return false
  148. }
  149. if lr.Any {
  150. return true
  151. }
  152. return lr.LocalCIDR.Contains(p.LocalAddr)
  153. }