123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- package nebula
- import (
- "fmt"
- "net"
- "regexp"
- "github.com/slackhq/nebula/cidr"
- "github.com/slackhq/nebula/config"
- "github.com/slackhq/nebula/iputil"
- )
- type AllowList struct {
- // The values of this cidrTree are `bool`, signifying allow/deny
- cidrTree *cidr.Tree6
- }
- type RemoteAllowList struct {
- AllowList *AllowList
- // Inside Range Specific, keys of this tree are inside CIDRs and values
- // are *AllowList
- insideAllowLists *cidr.Tree6
- }
- type LocalAllowList struct {
- AllowList *AllowList
- // To avoid ambiguity, all rules must be true, or all rules must be false.
- nameRules []AllowListNameRule
- }
- type AllowListNameRule struct {
- Name *regexp.Regexp
- Allow bool
- }
- func NewLocalAllowListFromConfig(c *config.C, k string) (*LocalAllowList, error) {
- var nameRules []AllowListNameRule
- handleKey := func(key string, value interface{}) (bool, error) {
- if key == "interfaces" {
- var err error
- nameRules, err = getAllowListInterfaces(k, value)
- if err != nil {
- return false, err
- }
- return true, nil
- }
- return false, nil
- }
- al, err := newAllowListFromConfig(c, k, handleKey)
- if err != nil {
- return nil, err
- }
- return &LocalAllowList{AllowList: al, nameRules: nameRules}, nil
- }
- func NewRemoteAllowListFromConfig(c *config.C, k, rangesKey string) (*RemoteAllowList, error) {
- al, err := newAllowListFromConfig(c, k, nil)
- if err != nil {
- return nil, err
- }
- remoteAllowRanges, err := getRemoteAllowRanges(c, rangesKey)
- if err != nil {
- return nil, err
- }
- return &RemoteAllowList{AllowList: al, insideAllowLists: remoteAllowRanges}, nil
- }
- // If the handleKey func returns true, the rest of the parsing is skipped
- // for this key. This allows parsing of special values like `interfaces`.
- func newAllowListFromConfig(c *config.C, k string, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
- r := c.Get(k)
- if r == nil {
- return nil, nil
- }
- return newAllowList(k, r, handleKey)
- }
- // If the handleKey func returns true, the rest of the parsing is skipped
- // for this key. This allows parsing of special values like `interfaces`.
- func newAllowList(k string, raw interface{}, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
- rawMap, ok := raw.(map[interface{}]interface{})
- if !ok {
- return nil, fmt.Errorf("config `%s` has invalid type: %T", k, raw)
- }
- tree := cidr.NewTree6()
- // Keep track of the rules we have added for both ipv4 and ipv6
- type allowListRules struct {
- firstValue bool
- allValuesMatch bool
- defaultSet bool
- allValues bool
- }
- rules4 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
- rules6 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
- for rawKey, rawValue := range rawMap {
- rawCIDR, ok := rawKey.(string)
- if !ok {
- return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
- }
- if handleKey != nil {
- handled, err := handleKey(rawCIDR, rawValue)
- if err != nil {
- return nil, err
- }
- if handled {
- continue
- }
- }
- value, ok := rawValue.(bool)
- if !ok {
- return nil, fmt.Errorf("config `%s` has invalid value (type %T): %v", k, rawValue, rawValue)
- }
- _, ipNet, err := net.ParseCIDR(rawCIDR)
- if err != nil {
- return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR)
- }
- // TODO: should we error on duplicate CIDRs in the config?
- tree.AddCIDR(ipNet, value)
- maskBits, maskSize := ipNet.Mask.Size()
- var rules *allowListRules
- if maskSize == 32 {
- rules = &rules4
- } else {
- rules = &rules6
- }
- if rules.firstValue {
- rules.allValues = value
- rules.firstValue = false
- } else {
- if value != rules.allValues {
- rules.allValuesMatch = false
- }
- }
- // Check if this is 0.0.0.0/0 or ::/0
- if maskBits == 0 {
- rules.defaultSet = true
- }
- }
- if !rules4.defaultSet {
- if rules4.allValuesMatch {
- _, zeroCIDR, _ := net.ParseCIDR("0.0.0.0/0")
- tree.AddCIDR(zeroCIDR, !rules4.allValues)
- } else {
- return nil, fmt.Errorf("config `%s` contains both true and false rules, but no default set for 0.0.0.0/0", k)
- }
- }
- if !rules6.defaultSet {
- if rules6.allValuesMatch {
- _, zeroCIDR, _ := net.ParseCIDR("::/0")
- tree.AddCIDR(zeroCIDR, !rules6.allValues)
- } else {
- return nil, fmt.Errorf("config `%s` contains both true and false rules, but no default set for ::/0", k)
- }
- }
- return &AllowList{cidrTree: tree}, nil
- }
- func getAllowListInterfaces(k string, v interface{}) ([]AllowListNameRule, error) {
- var nameRules []AllowListNameRule
- rawRules, ok := v.(map[interface{}]interface{})
- if !ok {
- return nil, fmt.Errorf("config `%s.interfaces` is invalid (type %T): %v", k, v, v)
- }
- firstEntry := true
- var allValues bool
- for rawName, rawAllow := range rawRules {
- name, ok := rawName.(string)
- if !ok {
- return nil, fmt.Errorf("config `%s.interfaces` has invalid key (type %T): %v", k, rawName, rawName)
- }
- allow, ok := rawAllow.(bool)
- if !ok {
- return nil, fmt.Errorf("config `%s.interfaces` has invalid value (type %T): %v", k, rawAllow, rawAllow)
- }
- nameRE, err := regexp.Compile("^" + name + "$")
- if err != nil {
- return nil, fmt.Errorf("config `%s.interfaces` has invalid key: %s: %v", k, name, err)
- }
- nameRules = append(nameRules, AllowListNameRule{
- Name: nameRE,
- Allow: allow,
- })
- if firstEntry {
- allValues = allow
- firstEntry = false
- } else {
- if allow != allValues {
- return nil, fmt.Errorf("config `%s.interfaces` values must all be the same true/false value", k)
- }
- }
- }
- return nameRules, nil
- }
- func getRemoteAllowRanges(c *config.C, k string) (*cidr.Tree6, error) {
- value := c.Get(k)
- if value == nil {
- return nil, nil
- }
- remoteAllowRanges := cidr.NewTree6()
- rawMap, ok := value.(map[interface{}]interface{})
- if !ok {
- return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value)
- }
- for rawKey, rawValue := range rawMap {
- rawCIDR, ok := rawKey.(string)
- if !ok {
- return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
- }
- allowList, err := newAllowList(fmt.Sprintf("%s.%s", k, rawCIDR), rawValue, nil)
- if err != nil {
- return nil, err
- }
- _, ipNet, err := net.ParseCIDR(rawCIDR)
- if err != nil {
- return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR)
- }
- remoteAllowRanges.AddCIDR(ipNet, allowList)
- }
- return remoteAllowRanges, nil
- }
- func (al *AllowList) Allow(ip net.IP) bool {
- if al == nil {
- return true
- }
- result := al.cidrTree.MostSpecificContains(ip)
- switch v := result.(type) {
- case bool:
- return v
- default:
- panic(fmt.Errorf("invalid state, allowlist returned: %T %v", result, result))
- }
- }
- func (al *AllowList) AllowIpV4(ip iputil.VpnIp) bool {
- if al == nil {
- return true
- }
- result := al.cidrTree.MostSpecificContainsIpV4(ip)
- switch v := result.(type) {
- case bool:
- return v
- default:
- panic(fmt.Errorf("invalid state, allowlist returned: %T %v", result, result))
- }
- }
- func (al *AllowList) AllowIpV6(hi, lo uint64) bool {
- if al == nil {
- return true
- }
- result := al.cidrTree.MostSpecificContainsIpV6(hi, lo)
- switch v := result.(type) {
- case bool:
- return v
- default:
- panic(fmt.Errorf("invalid state, allowlist returned: %T %v", result, result))
- }
- }
- func (al *LocalAllowList) Allow(ip net.IP) bool {
- if al == nil {
- return true
- }
- return al.AllowList.Allow(ip)
- }
- func (al *LocalAllowList) AllowName(name string) bool {
- if al == nil || len(al.nameRules) == 0 {
- return true
- }
- for _, rule := range al.nameRules {
- if rule.Name.MatchString(name) {
- return rule.Allow
- }
- }
- // If no rules match, return the default, which is the inverse of the rules
- return !al.nameRules[0].Allow
- }
- func (al *RemoteAllowList) AllowUnknownVpnIp(ip net.IP) bool {
- if al == nil {
- return true
- }
- return al.AllowList.Allow(ip)
- }
- func (al *RemoteAllowList) Allow(vpnIp iputil.VpnIp, ip net.IP) bool {
- if !al.getInsideAllowList(vpnIp).Allow(ip) {
- return false
- }
- return al.AllowList.Allow(ip)
- }
- func (al *RemoteAllowList) AllowIpV4(vpnIp iputil.VpnIp, ip iputil.VpnIp) bool {
- if al == nil {
- return true
- }
- if !al.getInsideAllowList(vpnIp).AllowIpV4(ip) {
- return false
- }
- return al.AllowList.AllowIpV4(ip)
- }
- func (al *RemoteAllowList) AllowIpV6(vpnIp iputil.VpnIp, hi, lo uint64) bool {
- if al == nil {
- return true
- }
- if !al.getInsideAllowList(vpnIp).AllowIpV6(hi, lo) {
- return false
- }
- return al.AllowList.AllowIpV6(hi, lo)
- }
- func (al *RemoteAllowList) getInsideAllowList(vpnIp iputil.VpnIp) *AllowList {
- if al.insideAllowLists != nil {
- inside := al.insideAllowLists.MostSpecificContainsIpV4(vpnIp)
- if inside != nil {
- return inside.(*AllowList)
- }
- }
- return nil
- }
|