remote_allow_ranges: allow inside CIDR specific remote_allow_lists (#540)

This allows you to configure remote allow lists specific to different
subnets of the inside CIDR. Example:

    remote_allow_ranges:
      10.42.42.0/24:
        192.168.0.0/16: true

This would only allow hosts with a VPN IP in the 10.42.42.0/24 range to
have private IPs (and thus don't connect over public IPs).

The PR also refactors AllowList into RemoteAllowList and LocalAllowList to make it clearer which methods are allowed on which allow list.
This commit is contained in:
Wade Simmons 2021-10-19 10:54:30 -04:00 committed by GitHub
parent ae5505bc74
commit ea2c186a77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 223 additions and 70 deletions

View File

@ -9,6 +9,18 @@ import (
type AllowList struct { type AllowList struct {
// The values of this cidrTree are `bool`, signifying allow/deny // The values of this cidrTree are `bool`, signifying allow/deny
cidrTree *CIDR6Tree cidrTree *CIDR6Tree
}
type RemoteAllowList struct {
AllowList *AllowList
// Inside Range Specific, keys of this tree are inside CIDRs and values
// are *AllowList
insideAllowLists *CIDR6Tree
}
type LocalAllowList struct {
AllowList *AllowList
// To avoid ambiguity, all rules must be true, or all rules must be false. // To avoid ambiguity, all rules must be true, or all rules must be false.
nameRules []AllowListNameRule nameRules []AllowListNameRule
@ -61,7 +73,14 @@ func (al *AllowList) AllowIpV6(hi, lo uint64) bool {
} }
} }
func (al *AllowList) AllowName(name string) bool { 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 { if al == nil || len(al.nameRules) == 0 {
return true return true
} }
@ -75,3 +94,47 @@ func (al *AllowList) AllowName(name string) bool {
// If no rules match, return the default, which is the inverse of the rules // If no rules match, return the default, which is the inverse of the rules
return !al.nameRules[0].Allow 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 uint32, ip net.IP) bool {
if !al.getInsideAllowList(vpnIp).Allow(ip) {
return false
}
return al.AllowList.Allow(ip)
}
func (al *RemoteAllowList) AllowIpV4(vpnIp uint32, ip uint32) bool {
if al == nil {
return true
}
if !al.getInsideAllowList(vpnIp).AllowIpV4(ip) {
return false
}
return al.AllowList.AllowIpV4(ip)
}
func (al *RemoteAllowList) AllowIpV6(vpnIp uint32, 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 uint32) *AllowList {
if al.insideAllowLists != nil {
inside := al.insideAllowLists.MostSpecificContainsIpV4(vpnIp)
if inside != nil {
return inside.(*AllowList)
}
}
return nil
}

View File

@ -31,14 +31,14 @@ func TestAllowList_Allow(t *testing.T) {
assert.Equal(t, false, al.Allow(net.ParseIP("::2"))) assert.Equal(t, false, al.Allow(net.ParseIP("::2")))
} }
func TestAllowList_AllowName(t *testing.T) { func TestLocalAllowList_AllowName(t *testing.T) {
assert.Equal(t, true, ((*AllowList)(nil)).AllowName("docker0")) assert.Equal(t, true, ((*LocalAllowList)(nil)).AllowName("docker0"))
rules := []AllowListNameRule{ rules := []AllowListNameRule{
{Name: regexp.MustCompile("^docker.*$"), Allow: false}, {Name: regexp.MustCompile("^docker.*$"), Allow: false},
{Name: regexp.MustCompile("^tun.*$"), Allow: false}, {Name: regexp.MustCompile("^tun.*$"), Allow: false},
} }
al := &AllowList{nameRules: rules} al := &LocalAllowList{nameRules: rules}
assert.Equal(t, false, al.AllowName("docker0")) assert.Equal(t, false, al.AllowName("docker0"))
assert.Equal(t, false, al.AllowName("tun0")) assert.Equal(t, false, al.AllowName("tun0"))
@ -48,7 +48,7 @@ func TestAllowList_AllowName(t *testing.T) {
{Name: regexp.MustCompile("^eth.*$"), Allow: true}, {Name: regexp.MustCompile("^eth.*$"), Allow: true},
{Name: regexp.MustCompile("^ens.*$"), Allow: true}, {Name: regexp.MustCompile("^ens.*$"), Allow: true},
} }
al = &AllowList{nameRules: rules} al = &LocalAllowList{nameRules: rules}
assert.Equal(t, false, al.AllowName("docker0")) assert.Equal(t, false, al.AllowName("docker0"))
assert.Equal(t, true, al.AllowName("eth0")) assert.Equal(t, true, al.AllowName("eth0"))

View File

@ -226,19 +226,94 @@ func (c *Config) GetDuration(k string, d time.Duration) time.Duration {
return v return v
} }
func (c *Config) GetAllowList(k string, allowInterfaces bool) (*AllowList, error) { func (c *Config) GetLocalAllowList(k string) (*LocalAllowList, error) {
var nameRules []AllowListNameRule
handleKey := func(key string, value interface{}) (bool, error) {
if key == "interfaces" {
var err error
nameRules, err = c.getAllowListInterfaces(k, value)
if err != nil {
return false, err
}
return true, nil
}
return false, nil
}
al, err := c.GetAllowList(k, handleKey)
if err != nil {
return nil, err
}
return &LocalAllowList{AllowList: al, nameRules: nameRules}, nil
}
func (c *Config) GetRemoteAllowList(k, rangesKey string) (*RemoteAllowList, error) {
al, err := c.GetAllowList(k, nil)
if err != nil {
return nil, err
}
remoteAllowRanges, err := c.getRemoteAllowRanges(rangesKey)
if err != nil {
return nil, err
}
return &RemoteAllowList{AllowList: al, insideAllowLists: remoteAllowRanges}, nil
}
func (c *Config) getRemoteAllowRanges(k string) (*CIDR6Tree, error) {
value := c.Get(k)
if value == nil {
return nil, nil
}
remoteAllowRanges := NewCIDR6Tree()
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 := c.getAllowList(fmt.Sprintf("%s.%s", k, rawCIDR), rawValue, nil)
if err != nil {
return nil, err
}
_, cidr, err := net.ParseCIDR(rawCIDR)
if err != nil {
return nil, fmt.Errorf("config `%s` has invalid CIDR: %s", k, rawCIDR)
}
remoteAllowRanges.AddCIDR(cidr, allowList)
}
return 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 (c *Config) GetAllowList(k string, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
r := c.Get(k) r := c.Get(k)
if r == nil { if r == nil {
return nil, nil return nil, nil
} }
rawMap, ok := r.(map[interface{}]interface{}) return c.getAllowList(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 (c *Config) getAllowList(k string, raw interface{}, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
rawMap, ok := raw.(map[interface{}]interface{})
if !ok { if !ok {
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, r) return nil, fmt.Errorf("config `%s` has invalid type: %T", k, raw)
} }
tree := NewCIDR6Tree() tree := NewCIDR6Tree()
var nameRules []AllowListNameRule
// Keep track of the rules we have added for both ipv4 and ipv6 // Keep track of the rules we have added for both ipv4 and ipv6
type allowListRules struct { type allowListRules struct {
@ -256,18 +331,14 @@ func (c *Config) GetAllowList(k string, allowInterfaces bool) (*AllowList, error
return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey) return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
} }
// Special rule for interface names if handleKey != nil {
if rawCIDR == "interfaces" { handled, err := handleKey(rawCIDR, rawValue)
if !allowInterfaces {
return nil, fmt.Errorf("config `%s` does not support `interfaces`", k)
}
var err error
nameRules, err = c.getAllowListInterfaces(k, rawValue)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if handled {
continue continue
}
} }
value, ok := rawValue.(bool) value, ok := rawValue.(bool)
@ -325,7 +396,7 @@ func (c *Config) GetAllowList(k string, allowInterfaces bool) (*AllowList, error
} }
} }
return &AllowList{cidrTree: tree, nameRules: nameRules}, nil return &AllowList{cidrTree: tree}, nil
} }
func (c *Config) getAllowListInterfaces(k string, v interface{}) ([]AllowListNameRule, error) { func (c *Config) getAllowListInterfaces(k string, v interface{}) ([]AllowListNameRule, error) {

View File

@ -97,21 +97,21 @@ func TestConfig_GetAllowList(t *testing.T) {
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
"192.168.0.0": true, "192.168.0.0": true,
} }
r, err := c.GetAllowList("allowlist", false) r, err := c.GetAllowList("allowlist", nil)
assert.EqualError(t, err, "config `allowlist` has invalid CIDR: 192.168.0.0") assert.EqualError(t, err, "config `allowlist` has invalid CIDR: 192.168.0.0")
assert.Nil(t, r) assert.Nil(t, r)
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
"192.168.0.0/16": "abc", "192.168.0.0/16": "abc",
} }
r, err = c.GetAllowList("allowlist", false) r, err = c.GetAllowList("allowlist", nil)
assert.EqualError(t, err, "config `allowlist` has invalid value (type string): abc") assert.EqualError(t, err, "config `allowlist` has invalid value (type string): abc")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
"192.168.0.0/16": true, "192.168.0.0/16": true,
"10.0.0.0/8": false, "10.0.0.0/8": false,
} }
r, err = c.GetAllowList("allowlist", false) r, err = c.GetAllowList("allowlist", nil)
assert.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for 0.0.0.0/0") assert.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for 0.0.0.0/0")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
@ -121,7 +121,7 @@ func TestConfig_GetAllowList(t *testing.T) {
"fd00::/8": true, "fd00::/8": true,
"fd00:fd00::/16": false, "fd00:fd00::/16": false,
} }
r, err = c.GetAllowList("allowlist", false) r, err = c.GetAllowList("allowlist", nil)
assert.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for ::/0") assert.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for ::/0")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
@ -129,7 +129,7 @@ func TestConfig_GetAllowList(t *testing.T) {
"10.0.0.0/8": false, "10.0.0.0/8": false,
"10.42.42.0/24": true, "10.42.42.0/24": true,
} }
r, err = c.GetAllowList("allowlist", false) r, err = c.GetAllowList("allowlist", nil)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.NotNil(t, r) assert.NotNil(t, r)
} }
@ -142,27 +142,19 @@ func TestConfig_GetAllowList(t *testing.T) {
"fd00::/8": true, "fd00::/8": true,
"fd00:fd00::/16": false, "fd00:fd00::/16": false,
} }
r, err = c.GetAllowList("allowlist", false) r, err = c.GetAllowList("allowlist", nil)
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.NotNil(t, r) assert.NotNil(t, r)
} }
// Test interface names // Test interface names
c.Settings["allowlist"] = map[interface{}]interface{}{
"interfaces": map[interface{}]interface{}{
`docker.*`: false,
},
}
r, err = c.GetAllowList("allowlist", false)
assert.EqualError(t, err, "config `allowlist` does not support `interfaces`")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
"interfaces": map[interface{}]interface{}{ "interfaces": map[interface{}]interface{}{
`docker.*`: "foo", `docker.*`: "foo",
}, },
} }
r, err = c.GetAllowList("allowlist", true) lr, err := c.GetLocalAllowList("allowlist")
assert.EqualError(t, err, "config `allowlist.interfaces` has invalid value (type string): foo") assert.EqualError(t, err, "config `allowlist.interfaces` has invalid value (type string): foo")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
@ -171,7 +163,7 @@ func TestConfig_GetAllowList(t *testing.T) {
`eth.*`: true, `eth.*`: true,
}, },
} }
r, err = c.GetAllowList("allowlist", true) lr, err = c.GetLocalAllowList("allowlist")
assert.EqualError(t, err, "config `allowlist.interfaces` values must all be the same true/false value") assert.EqualError(t, err, "config `allowlist.interfaces` values must all be the same true/false value")
c.Settings["allowlist"] = map[interface{}]interface{}{ c.Settings["allowlist"] = map[interface{}]interface{}{
@ -179,9 +171,9 @@ func TestConfig_GetAllowList(t *testing.T) {
`docker.*`: false, `docker.*`: false,
}, },
} }
r, err = c.GetAllowList("allowlist", true) lr, err = c.GetLocalAllowList("allowlist")
if assert.NoError(t, err) { if assert.NoError(t, err) {
assert.NotNil(t, r) assert.NotNil(t, lr)
} }
} }

View File

@ -56,6 +56,14 @@ lighthouse:
#"10.0.0.0/8": false #"10.0.0.0/8": false
#"10.42.42.0/24": true #"10.42.42.0/24": true
# EXPERIMENTAL: This option my change or disappear in the future.
# Optionally allows the definition of remote_allow_list blocks
# specific to an inside VPN IP CIDR.
#remote_allow_ranges:
# This rule would only allow only private IPs for this VPN range
#"10.42.42.0/24":
#"192.168.0.0/16": true
# local_allow_list allows you to filter which local IP addresses we advertise # local_allow_list allows you to filter which local IP addresses we advertise
# to the lighthouses. This uses the same logic as `remote_allow_list`, but # to the lighthouses. This uses the same logic as `remote_allow_list`, but
# additionally, you can specify an `interfaces` map of regular expressions # additionally, you can specify an `interfaces` map of regular expressions

View File

@ -6,7 +6,8 @@ const (
) )
func HandleIncomingHandshake(f *Interface, addr *udpAddr, packet []byte, h *Header, hostinfo *HostInfo) { func HandleIncomingHandshake(f *Interface, addr *udpAddr, packet []byte, h *Header, hostinfo *HostInfo) {
if !f.lightHouse.remoteAllowList.Allow(addr.IP) { // First remote allow list check before we know the vpnIp
if !f.lightHouse.remoteAllowList.AllowUnknownVpnIp(addr.IP) {
f.l.WithField("udpAddr", addr).Debug("lighthouse.remote_allow_list denied incoming handshake") f.l.WithField("udpAddr", addr).Debug("lighthouse.remote_allow_list denied incoming handshake")
return return
} }

View File

@ -111,6 +111,11 @@ func ixHandshakeStage1(f *Interface, addr *udpAddr, packet []byte, h *Header) {
return return
} }
if !f.lightHouse.remoteAllowList.Allow(vpnIP, addr.IP) {
f.l.WithField("vpnIp", IntIp(vpnIP)).WithField("udpAddr", addr).Debug("lighthouse.remote_allow_list denied incoming handshake")
return
}
myIndex, err := generateIndex(f.l) myIndex, err := generateIndex(f.l)
if err != nil { if err != nil {
f.l.WithError(err).WithField("vpnIp", IntIp(vpnIP)).WithField("udpAddr", addr). f.l.WithError(err).WithField("vpnIp", IntIp(vpnIP)).WithField("udpAddr", addr).
@ -313,6 +318,11 @@ func ixHandshakeStage2(f *Interface, addr *udpAddr, hostinfo *HostInfo, packet [
hostinfo.Lock() hostinfo.Lock()
defer hostinfo.Unlock() defer hostinfo.Unlock()
if !f.lightHouse.remoteAllowList.Allow(hostinfo.hostId, addr.IP) {
f.l.WithField("vpnIp", IntIp(hostinfo.hostId)).WithField("udpAddr", addr).Debug("lighthouse.remote_allow_list denied incoming handshake")
return false
}
ci := hostinfo.ConnectionState ci := hostinfo.ConnectionState
if ci.ready { if ci.ready {
f.l.WithField("vpnIp", IntIp(hostinfo.hostId)).WithField("udpAddr", addr). f.l.WithField("vpnIp", IntIp(hostinfo.hostId)).WithField("udpAddr", addr).

View File

@ -622,7 +622,7 @@ func (i *HostInfo) Probes() []*Probe {
// Utility functions // Utility functions
func localIps(l *logrus.Logger, allowList *AllowList) *[]net.IP { func localIps(l *logrus.Logger, allowList *LocalAllowList) *[]net.IP {
//FIXME: This function is pretty garbage //FIXME: This function is pretty garbage
var ips []net.IP var ips []net.IP
ifaces, _ := net.Interfaces() ifaces, _ := net.Interfaces()

View File

@ -35,10 +35,10 @@ type LightHouse struct {
// respond with. // respond with.
// - When we are not a lighthouse, this filters which addresses we accept // - When we are not a lighthouse, this filters which addresses we accept
// from lighthouses. // from lighthouses.
remoteAllowList *AllowList remoteAllowList *RemoteAllowList
// filters local addresses that we advertise to lighthouses // filters local addresses that we advertise to lighthouses
localAllowList *AllowList localAllowList *LocalAllowList
// used to trigger the HandshakeManager when we receive HostQueryReply // used to trigger the HandshakeManager when we receive HostQueryReply
handshakeTrigger chan<- uint32 handshakeTrigger chan<- uint32
@ -93,14 +93,14 @@ func NewLightHouse(l *logrus.Logger, amLighthouse bool, myVpnIpNet *net.IPNet, i
return &h return &h
} }
func (lh *LightHouse) SetRemoteAllowList(allowList *AllowList) { func (lh *LightHouse) SetRemoteAllowList(allowList *RemoteAllowList) {
lh.Lock() lh.Lock()
defer lh.Unlock() defer lh.Unlock()
lh.remoteAllowList = allowList lh.remoteAllowList = allowList
} }
func (lh *LightHouse) SetLocalAllowList(allowList *AllowList) { func (lh *LightHouse) SetLocalAllowList(allowList *LocalAllowList) {
lh.Lock() lh.Lock()
defer lh.Unlock() defer lh.Unlock()
@ -223,14 +223,14 @@ func (lh *LightHouse) AddStaticRemote(vpnIp uint32, toAddr *udpAddr) {
if ipv4 := toAddr.IP.To4(); ipv4 != nil { if ipv4 := toAddr.IP.To4(); ipv4 != nil {
to := NewIp4AndPort(ipv4, uint32(toAddr.Port)) to := NewIp4AndPort(ipv4, uint32(toAddr.Port))
if !lh.unlockedShouldAddV4(to) { if !lh.unlockedShouldAddV4(vpnIp, to) {
return return
} }
am.unlockedPrependV4(lh.myVpnIp, to) am.unlockedPrependV4(lh.myVpnIp, to)
} else { } else {
to := NewIp6AndPort(toAddr.IP, uint32(toAddr.Port)) to := NewIp6AndPort(toAddr.IP, uint32(toAddr.Port))
if !lh.unlockedShouldAddV6(to) { if !lh.unlockedShouldAddV6(vpnIp, to) {
return return
} }
am.unlockedPrependV6(lh.myVpnIp, to) am.unlockedPrependV6(lh.myVpnIp, to)
@ -251,8 +251,8 @@ func (lh *LightHouse) unlockedGetRemoteList(vpnIP uint32) *RemoteList {
} }
// unlockedShouldAddV4 checks if to is allowed by our allow list // unlockedShouldAddV4 checks if to is allowed by our allow list
func (lh *LightHouse) unlockedShouldAddV4(to *Ip4AndPort) bool { func (lh *LightHouse) unlockedShouldAddV4(vpnIp uint32, to *Ip4AndPort) bool {
allow := lh.remoteAllowList.AllowIpV4(to.Ip) allow := lh.remoteAllowList.AllowIpV4(vpnIp, to.Ip)
if lh.l.Level >= logrus.TraceLevel { if lh.l.Level >= logrus.TraceLevel {
lh.l.WithField("remoteIp", IntIp(to.Ip)).WithField("allow", allow).Trace("remoteAllowList.Allow") lh.l.WithField("remoteIp", IntIp(to.Ip)).WithField("allow", allow).Trace("remoteAllowList.Allow")
} }
@ -265,8 +265,8 @@ func (lh *LightHouse) unlockedShouldAddV4(to *Ip4AndPort) bool {
} }
// unlockedShouldAddV6 checks if to is allowed by our allow list // unlockedShouldAddV6 checks if to is allowed by our allow list
func (lh *LightHouse) unlockedShouldAddV6(to *Ip6AndPort) bool { func (lh *LightHouse) unlockedShouldAddV6(vpnIp uint32, to *Ip6AndPort) bool {
allow := lh.remoteAllowList.AllowIpV6(to.Hi, to.Lo) allow := lh.remoteAllowList.AllowIpV6(vpnIp, to.Hi, to.Lo)
if lh.l.Level >= logrus.TraceLevel { if lh.l.Level >= logrus.TraceLevel {
lh.l.WithField("remoteIp", lhIp6ToIp(to)).WithField("allow", allow).Trace("remoteAllowList.Allow") lh.l.WithField("remoteIp", lhIp6ToIp(to)).WithField("allow", allow).Trace("remoteAllowList.Allow")
} }
@ -549,8 +549,8 @@ func (lhh *LightHouseHandler) handleHostQueryReply(n *NebulaMeta, vpnIp uint32)
am.Lock() am.Lock()
lhh.lh.Unlock() lhh.lh.Unlock()
am.unlockedSetV4(vpnIp, n.Details.Ip4AndPorts, lhh.lh.unlockedShouldAddV4) am.unlockedSetV4(vpnIp, n.Details.VpnIp, n.Details.Ip4AndPorts, lhh.lh.unlockedShouldAddV4)
am.unlockedSetV6(vpnIp, n.Details.Ip6AndPorts, lhh.lh.unlockedShouldAddV6) am.unlockedSetV6(vpnIp, n.Details.VpnIp, n.Details.Ip6AndPorts, lhh.lh.unlockedShouldAddV6)
am.Unlock() am.Unlock()
// Non-blocking attempt to trigger, skip if it would block // Non-blocking attempt to trigger, skip if it would block
@ -581,8 +581,8 @@ func (lhh *LightHouseHandler) handleHostUpdateNotification(n *NebulaMeta, vpnIp
am.Lock() am.Lock()
lhh.lh.Unlock() lhh.lh.Unlock()
am.unlockedSetV4(vpnIp, n.Details.Ip4AndPorts, lhh.lh.unlockedShouldAddV4) am.unlockedSetV4(vpnIp, n.Details.VpnIp, n.Details.Ip4AndPorts, lhh.lh.unlockedShouldAddV4)
am.unlockedSetV6(vpnIp, n.Details.Ip6AndPorts, lhh.lh.unlockedShouldAddV6) am.unlockedSetV6(vpnIp, n.Details.VpnIp, n.Details.Ip6AndPorts, lhh.lh.unlockedShouldAddV6)
am.Unlock() am.Unlock()
} }

View File

@ -75,24 +75,26 @@ func BenchmarkLighthouseHandleRequest(b *testing.B) {
hAddr2 := NewUDPAddrFromString("4.5.6.7:12346") hAddr2 := NewUDPAddrFromString("4.5.6.7:12346")
lh.addrMap[3] = NewRemoteList() lh.addrMap[3] = NewRemoteList()
lh.addrMap[3].unlockedSetV4( lh.addrMap[3].unlockedSetV4(
3,
3, 3,
[]*Ip4AndPort{ []*Ip4AndPort{
NewIp4AndPort(hAddr.IP, uint32(hAddr.Port)), NewIp4AndPort(hAddr.IP, uint32(hAddr.Port)),
NewIp4AndPort(hAddr2.IP, uint32(hAddr2.Port)), NewIp4AndPort(hAddr2.IP, uint32(hAddr2.Port)),
}, },
func(*Ip4AndPort) bool { return true }, func(uint32, *Ip4AndPort) bool { return true },
) )
rAddr := NewUDPAddrFromString("1.2.2.3:12345") rAddr := NewUDPAddrFromString("1.2.2.3:12345")
rAddr2 := NewUDPAddrFromString("1.2.2.3:12346") rAddr2 := NewUDPAddrFromString("1.2.2.3:12346")
lh.addrMap[2] = NewRemoteList() lh.addrMap[2] = NewRemoteList()
lh.addrMap[2].unlockedSetV4( lh.addrMap[2].unlockedSetV4(
3,
3, 3,
[]*Ip4AndPort{ []*Ip4AndPort{
NewIp4AndPort(rAddr.IP, uint32(rAddr.Port)), NewIp4AndPort(rAddr.IP, uint32(rAddr.Port)),
NewIp4AndPort(rAddr2.IP, uint32(rAddr2.Port)), NewIp4AndPort(rAddr2.IP, uint32(rAddr2.Port)),
}, },
func(*Ip4AndPort) bool { return true }, func(uint32, *Ip4AndPort) bool { return true },
) )
mw := &mockEncWriter{} mw := &mockEncWriter{}

View File

@ -278,13 +278,13 @@ func Main(config *Config, configTest bool, buildVersion string, logger *logrus.L
config.GetBool("stats.lighthouse_metrics", false), config.GetBool("stats.lighthouse_metrics", false),
) )
remoteAllowList, err := config.GetAllowList("lighthouse.remote_allow_list", false) remoteAllowList, err := config.GetRemoteAllowList("lighthouse.remote_allow_list", "lighthouse.remote_allow_ranges")
if err != nil { if err != nil {
return nil, NewContextualError("Invalid lighthouse.remote_allow_list", nil, err) return nil, NewContextualError("Invalid lighthouse.remote_allow_list", nil, err)
} }
lightHouse.SetRemoteAllowList(remoteAllowList) lightHouse.SetRemoteAllowList(remoteAllowList)
localAllowList, err := config.GetAllowList("lighthouse.local_allow_list", true) localAllowList, err := config.GetLocalAllowList("lighthouse.local_allow_list")
if err != nil { if err != nil {
return nil, NewContextualError("Invalid lighthouse.local_allow_list", nil, err) return nil, NewContextualError("Invalid lighthouse.local_allow_list", nil, err)
} }

View File

@ -153,7 +153,7 @@ func (f *Interface) sendCloseTunnel(h *HostInfo) {
func (f *Interface) handleHostRoaming(hostinfo *HostInfo, addr *udpAddr) { func (f *Interface) handleHostRoaming(hostinfo *HostInfo, addr *udpAddr) {
if hostDidRoam(hostinfo.remote, addr) { if hostDidRoam(hostinfo.remote, addr) {
if !f.lightHouse.remoteAllowList.Allow(addr.IP) { if !f.lightHouse.remoteAllowList.Allow(hostinfo.hostId, addr.IP) {
hostinfo.logger(f.l).WithField("newAddr", addr).Debug("lighthouse.remote_allow_list denied roaming") hostinfo.logger(f.l).WithField("newAddr", addr).Debug("lighthouse.remote_allow_list denied roaming")
return return
} }

View File

@ -11,8 +11,8 @@ import (
type forEachFunc func(addr *udpAddr, preferred bool) type forEachFunc func(addr *udpAddr, preferred bool)
// The checkFuncs here are to simplify bulk importing LH query response logic into a single function (reset slice and iterate) // The checkFuncs here are to simplify bulk importing LH query response logic into a single function (reset slice and iterate)
type checkFuncV4 func(to *Ip4AndPort) bool type checkFuncV4 func(vpnIp uint32, to *Ip4AndPort) bool
type checkFuncV6 func(to *Ip6AndPort) bool type checkFuncV6 func(vpnIp uint32, to *Ip6AndPort) bool
// CacheMap is a struct that better represents the lighthouse cache for humans // CacheMap is a struct that better represents the lighthouse cache for humans
// The string key is the owners vpnIp // The string key is the owners vpnIp
@ -246,7 +246,7 @@ func (r *RemoteList) unlockedSetLearnedV4(ownerVpnIp uint32, to *Ip4AndPort) {
// unlockedSetV4 assumes you have the write lock and resets the reported list of ips for this owner to the list provided // unlockedSetV4 assumes you have the write lock and resets the reported list of ips for this owner to the list provided
// and marks the deduplicated address list as dirty // and marks the deduplicated address list as dirty
func (r *RemoteList) unlockedSetV4(ownerVpnIp uint32, to []*Ip4AndPort, check checkFuncV4) { func (r *RemoteList) unlockedSetV4(ownerVpnIp uint32, vpnIp uint32, to []*Ip4AndPort, check checkFuncV4) {
r.shouldRebuild = true r.shouldRebuild = true
c := r.unlockedGetOrMakeV4(ownerVpnIp) c := r.unlockedGetOrMakeV4(ownerVpnIp)
@ -255,7 +255,7 @@ func (r *RemoteList) unlockedSetV4(ownerVpnIp uint32, to []*Ip4AndPort, check ch
// We can't take their array but we can take their pointers // We can't take their array but we can take their pointers
for _, v := range to[:minInt(len(to), MaxRemotes)] { for _, v := range to[:minInt(len(to), MaxRemotes)] {
if check(v) { if check(vpnIp, v) {
c.reported = append(c.reported, v) c.reported = append(c.reported, v)
} }
} }
@ -283,7 +283,7 @@ func (r *RemoteList) unlockedSetLearnedV6(ownerVpnIp uint32, to *Ip6AndPort) {
// unlockedSetV6 assumes you have the write lock and resets the reported list of ips for this owner to the list provided // unlockedSetV6 assumes you have the write lock and resets the reported list of ips for this owner to the list provided
// and marks the deduplicated address list as dirty // and marks the deduplicated address list as dirty
func (r *RemoteList) unlockedSetV6(ownerVpnIp uint32, to []*Ip6AndPort, check checkFuncV6) { func (r *RemoteList) unlockedSetV6(ownerVpnIp uint32, vpnIp uint32, to []*Ip6AndPort, check checkFuncV6) {
r.shouldRebuild = true r.shouldRebuild = true
c := r.unlockedGetOrMakeV6(ownerVpnIp) c := r.unlockedGetOrMakeV6(ownerVpnIp)
@ -292,7 +292,7 @@ func (r *RemoteList) unlockedSetV6(ownerVpnIp uint32, to []*Ip6AndPort, check ch
// We can't take their array but we can take their pointers // We can't take their array but we can take their pointers
for _, v := range to[:minInt(len(to), MaxRemotes)] { for _, v := range to[:minInt(len(to), MaxRemotes)] {
if check(v) { if check(vpnIp, v) {
c.reported = append(c.reported, v) c.reported = append(c.reported, v)
} }
} }

View File

@ -10,6 +10,7 @@ import (
func TestRemoteList_Rebuild(t *testing.T) { func TestRemoteList_Rebuild(t *testing.T) {
rl := NewRemoteList() rl := NewRemoteList()
rl.unlockedSetV4( rl.unlockedSetV4(
0,
0, 0,
[]*Ip4AndPort{ []*Ip4AndPort{
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, // this is duped {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, // this is duped
@ -23,10 +24,11 @@ func TestRemoteList_Rebuild(t *testing.T) {
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // almost dupe of 0 with a diff port {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // almost dupe of 0 with a diff port
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, // this is a dupe {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, // this is a dupe
}, },
func(*Ip4AndPort) bool { return true }, func(uint32, *Ip4AndPort) bool { return true },
) )
rl.unlockedSetV6( rl.unlockedSetV6(
1,
1, 1,
[]*Ip6AndPort{ []*Ip6AndPort{
NewIp6AndPort(net.ParseIP("1::1"), 1), // this is duped NewIp6AndPort(net.ParseIP("1::1"), 1), // this is duped
@ -35,7 +37,7 @@ func TestRemoteList_Rebuild(t *testing.T) {
NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe
NewIp6AndPort(net.ParseIP("1::1"), 2), // this is a dupe NewIp6AndPort(net.ParseIP("1::1"), 2), // this is a dupe
}, },
func(*Ip6AndPort) bool { return true }, func(uint32, *Ip6AndPort) bool { return true },
) )
rl.Rebuild([]*net.IPNet{}) rl.Rebuild([]*net.IPNet{})
@ -101,6 +103,7 @@ func TestRemoteList_Rebuild(t *testing.T) {
func BenchmarkFullRebuild(b *testing.B) { func BenchmarkFullRebuild(b *testing.B) {
rl := NewRemoteList() rl := NewRemoteList()
rl.unlockedSetV4( rl.unlockedSetV4(
0,
0, 0,
[]*Ip4AndPort{ []*Ip4AndPort{
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475},
@ -112,10 +115,11 @@ func BenchmarkFullRebuild(b *testing.B) {
{Ip: ip2int(net.ParseIP("172.17.1.1")), Port: 10101}, // this is a dupe {Ip: ip2int(net.ParseIP("172.17.1.1")), Port: 10101}, // this is a dupe
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // dupe of 0 with a diff port {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // dupe of 0 with a diff port
}, },
func(*Ip4AndPort) bool { return true }, func(uint32, *Ip4AndPort) bool { return true },
) )
rl.unlockedSetV6( rl.unlockedSetV6(
0,
0, 0,
[]*Ip6AndPort{ []*Ip6AndPort{
NewIp6AndPort(net.ParseIP("1::1"), 1), NewIp6AndPort(net.ParseIP("1::1"), 1),
@ -123,7 +127,7 @@ func BenchmarkFullRebuild(b *testing.B) {
NewIp6AndPort(net.ParseIP("1:100::1"), 1), NewIp6AndPort(net.ParseIP("1:100::1"), 1),
NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe
}, },
func(*Ip6AndPort) bool { return true }, func(uint32, *Ip6AndPort) bool { return true },
) )
b.Run("no preferred", func(b *testing.B) { b.Run("no preferred", func(b *testing.B) {
@ -164,6 +168,7 @@ func BenchmarkFullRebuild(b *testing.B) {
func BenchmarkSortRebuild(b *testing.B) { func BenchmarkSortRebuild(b *testing.B) {
rl := NewRemoteList() rl := NewRemoteList()
rl.unlockedSetV4( rl.unlockedSetV4(
0,
0, 0,
[]*Ip4AndPort{ []*Ip4AndPort{
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475}, {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1475},
@ -175,10 +180,11 @@ func BenchmarkSortRebuild(b *testing.B) {
{Ip: ip2int(net.ParseIP("172.17.1.1")), Port: 10101}, // this is a dupe {Ip: ip2int(net.ParseIP("172.17.1.1")), Port: 10101}, // this is a dupe
{Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // dupe of 0 with a diff port {Ip: ip2int(net.ParseIP("70.199.182.92")), Port: 1476}, // dupe of 0 with a diff port
}, },
func(*Ip4AndPort) bool { return true }, func(uint32, *Ip4AndPort) bool { return true },
) )
rl.unlockedSetV6( rl.unlockedSetV6(
0,
0, 0,
[]*Ip6AndPort{ []*Ip6AndPort{
NewIp6AndPort(net.ParseIP("1::1"), 1), NewIp6AndPort(net.ParseIP("1::1"), 1),
@ -186,7 +192,7 @@ func BenchmarkSortRebuild(b *testing.B) {
NewIp6AndPort(net.ParseIP("1:100::1"), 1), NewIp6AndPort(net.ParseIP("1:100::1"), 1),
NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe NewIp6AndPort(net.ParseIP("1::1"), 1), // this is a dupe
}, },
func(*Ip6AndPort) bool { return true }, func(uint32, *Ip6AndPort) bool { return true },
) )
b.Run("no preferred", func(b *testing.B) { b.Run("no preferred", func(b *testing.B) {