github.com/slackhq/nebula@v1.9.0/allow_list.go (about) 1 package nebula 2 3 import ( 4 "fmt" 5 "net" 6 "regexp" 7 8 "github.com/slackhq/nebula/cidr" 9 "github.com/slackhq/nebula/config" 10 "github.com/slackhq/nebula/iputil" 11 ) 12 13 type AllowList struct { 14 // The values of this cidrTree are `bool`, signifying allow/deny 15 cidrTree *cidr.Tree6[bool] 16 } 17 18 type RemoteAllowList struct { 19 AllowList *AllowList 20 21 // Inside Range Specific, keys of this tree are inside CIDRs and values 22 // are *AllowList 23 insideAllowLists *cidr.Tree6[*AllowList] 24 } 25 26 type LocalAllowList struct { 27 AllowList *AllowList 28 29 // To avoid ambiguity, all rules must be true, or all rules must be false. 30 nameRules []AllowListNameRule 31 } 32 33 type AllowListNameRule struct { 34 Name *regexp.Regexp 35 Allow bool 36 } 37 38 func NewLocalAllowListFromConfig(c *config.C, k string) (*LocalAllowList, error) { 39 var nameRules []AllowListNameRule 40 handleKey := func(key string, value interface{}) (bool, error) { 41 if key == "interfaces" { 42 var err error 43 nameRules, err = getAllowListInterfaces(k, value) 44 if err != nil { 45 return false, err 46 } 47 48 return true, nil 49 } 50 return false, nil 51 } 52 53 al, err := newAllowListFromConfig(c, k, handleKey) 54 if err != nil { 55 return nil, err 56 } 57 return &LocalAllowList{AllowList: al, nameRules: nameRules}, nil 58 } 59 60 func NewRemoteAllowListFromConfig(c *config.C, k, rangesKey string) (*RemoteAllowList, error) { 61 al, err := newAllowListFromConfig(c, k, nil) 62 if err != nil { 63 return nil, err 64 } 65 remoteAllowRanges, err := getRemoteAllowRanges(c, rangesKey) 66 if err != nil { 67 return nil, err 68 } 69 return &RemoteAllowList{AllowList: al, insideAllowLists: remoteAllowRanges}, nil 70 } 71 72 // If the handleKey func returns true, the rest of the parsing is skipped 73 // for this key. This allows parsing of special values like `interfaces`. 74 func newAllowListFromConfig(c *config.C, k string, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) { 75 r := c.Get(k) 76 if r == nil { 77 return nil, nil 78 } 79 80 return newAllowList(k, r, handleKey) 81 } 82 83 // If the handleKey func returns true, the rest of the parsing is skipped 84 // for this key. This allows parsing of special values like `interfaces`. 85 func newAllowList(k string, raw interface{}, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) { 86 rawMap, ok := raw.(map[interface{}]interface{}) 87 if !ok { 88 return nil, fmt.Errorf("config `%s` has invalid type: %T", k, raw) 89 } 90 91 tree := cidr.NewTree6[bool]() 92 93 // Keep track of the rules we have added for both ipv4 and ipv6 94 type allowListRules struct { 95 firstValue bool 96 allValuesMatch bool 97 defaultSet bool 98 allValues bool 99 } 100 101 rules4 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false} 102 rules6 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false} 103 104 for rawKey, rawValue := range rawMap { 105 rawCIDR, ok := rawKey.(string) 106 if !ok { 107 return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey) 108 } 109 110 if handleKey != nil { 111 handled, err := handleKey(rawCIDR, rawValue) 112 if err != nil { 113 return nil, err 114 } 115 if handled { 116 continue 117 } 118 } 119 120 value, ok := rawValue.(bool) 121 if !ok { 122 return nil, fmt.Errorf("config `%s` has invalid value (type %T): %v", k, rawValue, rawValue) 123 } 124 125 _, ipNet, err := net.ParseCIDR(rawCIDR) 126 if err != nil { 127 return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR) 128 } 129 130 // TODO: should we error on duplicate CIDRs in the config? 131 tree.AddCIDR(ipNet, value) 132 133 maskBits, maskSize := ipNet.Mask.Size() 134 135 var rules *allowListRules 136 if maskSize == 32 { 137 rules = &rules4 138 } else { 139 rules = &rules6 140 } 141 142 if rules.firstValue { 143 rules.allValues = value 144 rules.firstValue = false 145 } else { 146 if value != rules.allValues { 147 rules.allValuesMatch = false 148 } 149 } 150 151 // Check if this is 0.0.0.0/0 or ::/0 152 if maskBits == 0 { 153 rules.defaultSet = true 154 } 155 } 156 157 if !rules4.defaultSet { 158 if rules4.allValuesMatch { 159 _, zeroCIDR, _ := net.ParseCIDR("0.0.0.0/0") 160 tree.AddCIDR(zeroCIDR, !rules4.allValues) 161 } else { 162 return nil, fmt.Errorf("config `%s` contains both true and false rules, but no default set for 0.0.0.0/0", k) 163 } 164 } 165 166 if !rules6.defaultSet { 167 if rules6.allValuesMatch { 168 _, zeroCIDR, _ := net.ParseCIDR("::/0") 169 tree.AddCIDR(zeroCIDR, !rules6.allValues) 170 } else { 171 return nil, fmt.Errorf("config `%s` contains both true and false rules, but no default set for ::/0", k) 172 } 173 } 174 175 return &AllowList{cidrTree: tree}, nil 176 } 177 178 func getAllowListInterfaces(k string, v interface{}) ([]AllowListNameRule, error) { 179 var nameRules []AllowListNameRule 180 181 rawRules, ok := v.(map[interface{}]interface{}) 182 if !ok { 183 return nil, fmt.Errorf("config `%s.interfaces` is invalid (type %T): %v", k, v, v) 184 } 185 186 firstEntry := true 187 var allValues bool 188 for rawName, rawAllow := range rawRules { 189 name, ok := rawName.(string) 190 if !ok { 191 return nil, fmt.Errorf("config `%s.interfaces` has invalid key (type %T): %v", k, rawName, rawName) 192 } 193 allow, ok := rawAllow.(bool) 194 if !ok { 195 return nil, fmt.Errorf("config `%s.interfaces` has invalid value (type %T): %v", k, rawAllow, rawAllow) 196 } 197 198 nameRE, err := regexp.Compile("^" + name + "$") 199 if err != nil { 200 return nil, fmt.Errorf("config `%s.interfaces` has invalid key: %s: %v", k, name, err) 201 } 202 203 nameRules = append(nameRules, AllowListNameRule{ 204 Name: nameRE, 205 Allow: allow, 206 }) 207 208 if firstEntry { 209 allValues = allow 210 firstEntry = false 211 } else { 212 if allow != allValues { 213 return nil, fmt.Errorf("config `%s.interfaces` values must all be the same true/false value", k) 214 } 215 } 216 } 217 218 return nameRules, nil 219 } 220 221 func getRemoteAllowRanges(c *config.C, k string) (*cidr.Tree6[*AllowList], error) { 222 value := c.Get(k) 223 if value == nil { 224 return nil, nil 225 } 226 227 remoteAllowRanges := cidr.NewTree6[*AllowList]() 228 229 rawMap, ok := value.(map[interface{}]interface{}) 230 if !ok { 231 return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value) 232 } 233 for rawKey, rawValue := range rawMap { 234 rawCIDR, ok := rawKey.(string) 235 if !ok { 236 return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey) 237 } 238 239 allowList, err := newAllowList(fmt.Sprintf("%s.%s", k, rawCIDR), rawValue, nil) 240 if err != nil { 241 return nil, err 242 } 243 244 _, ipNet, err := net.ParseCIDR(rawCIDR) 245 if err != nil { 246 return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR) 247 } 248 249 remoteAllowRanges.AddCIDR(ipNet, allowList) 250 } 251 252 return remoteAllowRanges, nil 253 } 254 255 func (al *AllowList) Allow(ip net.IP) bool { 256 if al == nil { 257 return true 258 } 259 260 _, result := al.cidrTree.MostSpecificContains(ip) 261 return result 262 } 263 264 func (al *AllowList) AllowIpV4(ip iputil.VpnIp) bool { 265 if al == nil { 266 return true 267 } 268 269 _, result := al.cidrTree.MostSpecificContainsIpV4(ip) 270 return result 271 } 272 273 func (al *AllowList) AllowIpV6(hi, lo uint64) bool { 274 if al == nil { 275 return true 276 } 277 278 _, result := al.cidrTree.MostSpecificContainsIpV6(hi, lo) 279 return result 280 } 281 282 func (al *LocalAllowList) Allow(ip net.IP) bool { 283 if al == nil { 284 return true 285 } 286 return al.AllowList.Allow(ip) 287 } 288 289 func (al *LocalAllowList) AllowName(name string) bool { 290 if al == nil || len(al.nameRules) == 0 { 291 return true 292 } 293 294 for _, rule := range al.nameRules { 295 if rule.Name.MatchString(name) { 296 return rule.Allow 297 } 298 } 299 300 // If no rules match, return the default, which is the inverse of the rules 301 return !al.nameRules[0].Allow 302 } 303 304 func (al *RemoteAllowList) AllowUnknownVpnIp(ip net.IP) bool { 305 if al == nil { 306 return true 307 } 308 return al.AllowList.Allow(ip) 309 } 310 311 func (al *RemoteAllowList) Allow(vpnIp iputil.VpnIp, ip net.IP) bool { 312 if !al.getInsideAllowList(vpnIp).Allow(ip) { 313 return false 314 } 315 return al.AllowList.Allow(ip) 316 } 317 318 func (al *RemoteAllowList) AllowIpV4(vpnIp iputil.VpnIp, ip iputil.VpnIp) bool { 319 if al == nil { 320 return true 321 } 322 if !al.getInsideAllowList(vpnIp).AllowIpV4(ip) { 323 return false 324 } 325 return al.AllowList.AllowIpV4(ip) 326 } 327 328 func (al *RemoteAllowList) AllowIpV6(vpnIp iputil.VpnIp, hi, lo uint64) bool { 329 if al == nil { 330 return true 331 } 332 if !al.getInsideAllowList(vpnIp).AllowIpV6(hi, lo) { 333 return false 334 } 335 return al.AllowList.AllowIpV6(hi, lo) 336 } 337 338 func (al *RemoteAllowList) getInsideAllowList(vpnIp iputil.VpnIp) *AllowList { 339 if al.insideAllowLists != nil { 340 ok, inside := al.insideAllowLists.MostSpecificContainsIpV4(vpnIp) 341 if ok { 342 return inside 343 } 344 } 345 return nil 346 }