pkg/: FEATURE: support allowed IPs outside a cluster
Users can specify IPs with the annotation "allowed-location-ips". It makes no difference which node of a location is annotated. The IP should be routable from the particular location, e.g. a printer in the same LAN. This way these IPs become routable from other location. Signed-off-by: leonnicolas <leonloechner@gmx.de> Co-authored-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
|
||||
"github.com/squat/kilo/pkg/wireguard"
|
||||
@@ -28,6 +29,15 @@ func allowedIPs(ips ...string) string {
|
||||
return strings.Join(ips, ", ")
|
||||
}
|
||||
|
||||
func mustParseCIDR(s string) (r *net.IPNet) {
|
||||
if _, ip, err := net.ParseCIDR(s); err != nil {
|
||||
panic("failed to parse CIDR")
|
||||
} else {
|
||||
r = ip
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
||||
key := []byte("private")
|
||||
e1 := &net.IPNet{IP: net.ParseIP("10.1.0.1").To4(), Mask: net.CIDRMask(16, 32)}
|
||||
@@ -36,6 +46,7 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
||||
e4 := &net.IPNet{IP: net.ParseIP("10.1.0.4").To4(), Mask: net.CIDRMask(16, 32)}
|
||||
i1 := &net.IPNet{IP: net.ParseIP("192.168.0.1").To4(), Mask: net.CIDRMask(32, 32)}
|
||||
i2 := &net.IPNet{IP: net.ParseIP("192.168.0.2").To4(), Mask: net.CIDRMask(32, 32)}
|
||||
i3 := &net.IPNet{IP: net.ParseIP("192.168.178.3").To4(), Mask: net.CIDRMask(32, 32)}
|
||||
nodes := map[string]*Node{
|
||||
"a": {
|
||||
Name: "a",
|
||||
@@ -47,12 +58,13 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
|
||||
PersistentKeepalive: 25,
|
||||
},
|
||||
"b": {
|
||||
Name: "b",
|
||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
||||
InternalIP: i1,
|
||||
Location: "2",
|
||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
||||
Key: []byte("key2"),
|
||||
Name: "b",
|
||||
Endpoint: &wireguard.Endpoint{DNSOrIP: wireguard.DNSOrIP{IP: e2.IP}, Port: DefaultKiloPort},
|
||||
InternalIP: i1,
|
||||
Location: "2",
|
||||
Subnet: &net.IPNet{IP: net.ParseIP("10.2.2.0"), Mask: net.CIDRMask(24, 32)},
|
||||
Key: []byte("key2"),
|
||||
AllowedLocationIPs: []*net.IPNet{i3},
|
||||
},
|
||||
"c": {
|
||||
Name: "c",
|
||||
@@ -146,6 +158,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b", "c"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -159,7 +172,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w3,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -195,6 +209,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b", "c"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -208,7 +223,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w3,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -244,6 +260,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b", "c"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP, nodes["c"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["d"].Subnet, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -257,7 +274,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w3,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -293,6 +311,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -317,7 +336,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w4,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -353,6 +373,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -377,7 +398,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w4,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -413,6 +435,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -437,7 +460,8 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w4,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -473,6 +497,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostnames: []string{"b"},
|
||||
privateIPs: []net.IP{nodes["b"].InternalIP.IP},
|
||||
wireGuardIP: w2,
|
||||
allowedLocationIPs: nodes["b"].AllowedLocationIPs,
|
||||
},
|
||||
{
|
||||
allowedIPs: []*net.IPNet{nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w3, Mask: net.CIDRMask(32, 32)}},
|
||||
@@ -497,13 +522,14 @@ func TestNewTopology(t *testing.T) {
|
||||
wireGuardIP: w4,
|
||||
},
|
||||
},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
peers: []*Peer{peers["a"], peers["b"]},
|
||||
logger: log.NewNopLogger(),
|
||||
},
|
||||
},
|
||||
} {
|
||||
tc.result.key = key
|
||||
tc.result.port = port
|
||||
topo, err := NewTopology(nodes, peers, tc.granularity, tc.hostname, port, key, DefaultKiloSubnet, 0)
|
||||
topo, err := NewTopology(nodes, peers, tc.granularity, tc.hostname, port, key, DefaultKiloSubnet, 0, nil)
|
||||
if err != nil {
|
||||
t.Errorf("test case %q: failed to generate Topology: %v", tc.name, err)
|
||||
}
|
||||
@@ -514,7 +540,7 @@ func TestNewTopology(t *testing.T) {
|
||||
}
|
||||
|
||||
func mustTopo(t *testing.T, nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) *Topology {
|
||||
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive)
|
||||
topo, err := NewTopology(nodes, peers, granularity, hostname, port, key, subnet, persistentKeepalive, nil)
|
||||
if err != nil {
|
||||
t.Errorf("failed to generate Topology: %v", err)
|
||||
}
|
||||
@@ -538,7 +564,7 @@ ListenPort = 51820
|
||||
[Peer]
|
||||
PublicKey = key2
|
||||
Endpoint = 10.1.0.2:51820
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.2.3.0/24, 192.168.0.2/32, 10.4.0.2/32, 192.168.178.3/32
|
||||
PersistentKeepalive = 25
|
||||
|
||||
[Peer]
|
||||
@@ -623,7 +649,7 @@ PersistentKeepalive = 25
|
||||
[Peer]
|
||||
PublicKey = key2
|
||||
Endpoint = 10.1.0.2:51820
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
||||
PersistentKeepalive = 25
|
||||
|
||||
[Peer]
|
||||
@@ -697,7 +723,7 @@ PersistentKeepalive = 25
|
||||
[Peer]
|
||||
PublicKey = key2
|
||||
Endpoint = 10.1.0.2:51820
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32
|
||||
AllowedIPs = 10.2.2.0/24, 192.168.0.1/32, 10.4.0.2/32, 192.168.178.3/32
|
||||
|
||||
[Peer]
|
||||
PublicKey = key4
|
||||
@@ -953,3 +979,133 @@ func TestDeduplicatePeerIPs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterAllowedIPs(t *testing.T) {
|
||||
nodes, peers, key, port := setup(t)
|
||||
topo := mustTopo(t, nodes, peers, LogicalGranularity, nodes["a"].Name, port, key, DefaultKiloSubnet, nodes["a"].PersistentKeepalive)
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
allowedLocationIPs map[int][]*net.IPNet
|
||||
result map[int][]*net.IPNet
|
||||
}{
|
||||
{
|
||||
name: "nothing to filter",
|
||||
allowedLocationIPs: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.4/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
},
|
||||
},
|
||||
result: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.4/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intersections between segments",
|
||||
allowedLocationIPs: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.4/32"),
|
||||
mustParseCIDR("192.168.178.8/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
mustParseCIDR("192.168.178.4/32"),
|
||||
},
|
||||
},
|
||||
result: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.8/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
mustParseCIDR("192.168.178.4/32"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intersections with wireGuardCIDR",
|
||||
allowedLocationIPs: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("10.4.0.1/32"),
|
||||
mustParseCIDR("192.168.178.8/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
},
|
||||
},
|
||||
result: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.8/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.6/32"),
|
||||
mustParseCIDR("192.168.178.7/32"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "intersections with more than one allowedLocationIPs",
|
||||
allowedLocationIPs: map[int][]*net.IPNet{
|
||||
0: {
|
||||
mustParseCIDR("192.168.178.8/32"),
|
||||
},
|
||||
1: {
|
||||
mustParseCIDR("192.168.178.5/32"),
|
||||
},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.7/24"),
|
||||
},
|
||||
},
|
||||
result: map[int][]*net.IPNet{
|
||||
0: {},
|
||||
1: {},
|
||||
2: {
|
||||
mustParseCIDR("192.168.178.7/24"),
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
for k, v := range tc.allowedLocationIPs {
|
||||
topo.segments[k].allowedLocationIPs = v
|
||||
}
|
||||
for k, v := range topo.segments {
|
||||
f := topo.filterAllowedLocationIPs(v.allowedLocationIPs, v.location)
|
||||
// Overwrite the allowedLocationIPs to mimic the actual usage of the filterAllowedLocationIPs function.
|
||||
topo.segments[k].allowedLocationIPs = f
|
||||
if !ipNetSlicesEqual(f, tc.result[k]) {
|
||||
t.Errorf("test case %q:\n\texpected:\n\t%q\n\tgot:\n\t%q\n", tc.name, tc.result[k], f)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user