pkg/route,pkg/mesh: replace NAT with ip rules
This commit entirely replaces NAT in Kilo with a few iproute2 rules. Previously, Kilo would source-NAT the majority of packets in order to avoid problems with strict source checks in cloud providers causing packets to be considered martians. This source-NAT-ing made it difficult to correctly apply Kuberenetes NetworkPolicies based on source IPs. This rewrite instead relies on a handful of iproute2 rules to ensure that packets get encapsulated in certain scenarios based on the source network and/or source interface. This has the benefit of avoiding extra iptables bloat as well as enabling better compatibility with NetworkPolicies. Signed-off-by: Lucas Servén Marín <lserven@gmail.com>
This commit is contained in:
@@ -113,7 +113,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["a"].Name,
|
||||
leader: true,
|
||||
location: nodes["a"].Location,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["a"].Subnet,
|
||||
privateIP: nodes["a"].InternalIP,
|
||||
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
||||
segments: []*segment{
|
||||
@@ -150,7 +150,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["b"].Name,
|
||||
leader: true,
|
||||
location: nodes["b"].Location,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["b"].Subnet,
|
||||
privateIP: nodes["b"].InternalIP,
|
||||
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
||||
segments: []*segment{
|
||||
@@ -187,7 +187,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["c"].Name,
|
||||
leader: false,
|
||||
location: nodes["b"].Location,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["c"].Subnet,
|
||||
privateIP: nodes["c"].InternalIP,
|
||||
wireGuardCIDR: nil,
|
||||
segments: []*segment{
|
||||
@@ -224,7 +224,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["a"].Name,
|
||||
leader: true,
|
||||
location: nodes["a"].Name,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["a"].Subnet,
|
||||
privateIP: nodes["a"].InternalIP,
|
||||
wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
|
||||
segments: []*segment{
|
||||
@@ -271,7 +271,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["b"].Name,
|
||||
leader: true,
|
||||
location: nodes["b"].Name,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["b"].Subnet,
|
||||
privateIP: nodes["b"].InternalIP,
|
||||
wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
|
||||
segments: []*segment{
|
||||
@@ -318,7 +318,7 @@ func TestNewTopology(t *testing.T) {
|
||||
hostname: nodes["c"].Name,
|
||||
leader: true,
|
||||
location: nodes["c"].Name,
|
||||
subnet: DefaultKiloSubnet,
|
||||
subnet: nodes["c"].Subnet,
|
||||
privateIP: nodes["c"].InternalIP,
|
||||
wireGuardCIDR: &net.IPNet{IP: w3, Mask: net.CIDRMask(16, 32)},
|
||||
segments: []*segment{
|
||||
@@ -382,7 +382,7 @@ func TestRoutes(t *testing.T) {
|
||||
nodes, peers, key, port := setup(t)
|
||||
kiloIface := 0
|
||||
privIface := 1
|
||||
pubIface := 2
|
||||
tunlIface := 2
|
||||
mustTopoForGranularityAndHost := func(granularity Granularity, hostname string) *Topology {
|
||||
return mustTopo(t, nodes, peers, granularity, hostname, port, key, DefaultKiloSubnet)
|
||||
}
|
||||
@@ -391,12 +391,15 @@ func TestRoutes(t *testing.T) {
|
||||
name string
|
||||
local bool
|
||||
topology *Topology
|
||||
result []*netlink.Route
|
||||
strategy encapsulation.Strategy
|
||||
routes []*netlink.Route
|
||||
rules []*netlink.Rule
|
||||
}{
|
||||
{
|
||||
name: "logical from a",
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].cidrs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -445,7 +448,8 @@ func TestRoutes(t *testing.T) {
|
||||
{
|
||||
name: "logical from b",
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name).segments[0].cidrs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -480,7 +484,8 @@ func TestRoutes(t *testing.T) {
|
||||
{
|
||||
name: "logical from c",
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: oneAddressCIDR(mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name).segments[0].wireGuardIP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -535,7 +540,8 @@ func TestRoutes(t *testing.T) {
|
||||
{
|
||||
name: "full from a",
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name).segments[1].cidrs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -584,7 +590,8 @@ func TestRoutes(t *testing.T) {
|
||||
{
|
||||
name: "full from b",
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["b"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["b"].Name).segments[0].cidrs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -633,7 +640,8 @@ func TestRoutes(t *testing.T) {
|
||||
{
|
||||
name: "full from c",
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name).segments[0].cidrs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -683,7 +691,59 @@ func TestRoutes(t *testing.T) {
|
||||
name: "logical from a local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["b"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(nodes["b"].InternalIP.IP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: nodes["c"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(nodes["c"].InternalIP.IP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name).segments[1].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[0],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[1],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["b"].AllowedIPs[0],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logical from a local always",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["a"].Name),
|
||||
strategy: encapsulation.Always,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["b"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -733,7 +793,8 @@ func TestRoutes(t *testing.T) {
|
||||
name: "logical from b local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["a"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -772,11 +833,76 @@ func TestRoutes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logical from b local always",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name),
|
||||
strategy: encapsulation.Always,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["a"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name).segments[0].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(nodes["a"].InternalIP.IP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: mustTopoForGranularityAndHost(LogicalGranularity, nodes["b"].Name).segments[0].wireGuardIP,
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: nodes["c"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["c"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(nodes["c"].InternalIP.IP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["c"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
Table: kiloTableIndex,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[0],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[1],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["b"].AllowedIPs[0],
|
||||
LinkIndex: kiloIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
},
|
||||
rules: []*netlink.Rule{
|
||||
defaultRule(&netlink.Rule{
|
||||
Src: nodes["b"].Subnet,
|
||||
Dst: nodes["c"].InternalIP,
|
||||
Table: kiloTableIndex,
|
||||
}),
|
||||
defaultRule(&netlink.Rule{
|
||||
Dst: nodes["c"].InternalIP,
|
||||
IifName: DefaultKiloInterface,
|
||||
Table: kiloTableIndex,
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logical from c local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: oneAddressCIDR(mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name).segments[0].wireGuardIP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -835,11 +961,91 @@ func TestRoutes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logical from c local always",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name),
|
||||
strategy: encapsulation.Always,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: oneAddressCIDR(mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name).segments[0].wireGuardIP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: nodes["a"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(nodes["a"].InternalIP.IP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: oneAddressCIDR(mustTopoForGranularityAndHost(LogicalGranularity, nodes["c"].Name).segments[1].wireGuardIP),
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: nodes["b"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: nodes["b"].InternalIP,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
Table: kiloTableIndex,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["a"].AllowedIPs[1],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
{
|
||||
Dst: peers["b"].AllowedIPs[0],
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
Gw: nodes["b"].InternalIP.IP,
|
||||
LinkIndex: tunlIface,
|
||||
Protocol: unix.RTPROT_STATIC,
|
||||
},
|
||||
},
|
||||
rules: []*netlink.Rule{
|
||||
defaultRule(&netlink.Rule{
|
||||
Src: nodes["c"].Subnet,
|
||||
Dst: nodes["b"].InternalIP,
|
||||
Table: kiloTableIndex,
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "full from a local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["a"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["b"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -889,7 +1095,8 @@ func TestRoutes(t *testing.T) {
|
||||
name: "full from b local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["b"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["a"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -939,7 +1146,8 @@ func TestRoutes(t *testing.T) {
|
||||
name: "full from c local",
|
||||
local: true,
|
||||
topology: mustTopoForGranularityAndHost(FullGranularity, nodes["c"].Name),
|
||||
result: []*netlink.Route{
|
||||
strategy: encapsulation.Never,
|
||||
routes: []*netlink.Route{
|
||||
{
|
||||
Dst: nodes["a"].Subnet,
|
||||
Flags: int(netlink.FLAG_ONLINK),
|
||||
@@ -986,8 +1194,11 @@ func TestRoutes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
} {
|
||||
routes := tc.topology.Routes(kiloIface, privIface, pubIface, tc.local, encapsulation.NewIPIP(encapsulation.Never))
|
||||
if diff := pretty.Compare(routes, tc.result); diff != "" {
|
||||
routes, rules := tc.topology.Routes(DefaultKiloInterface, kiloIface, privIface, tunlIface, tc.local, encapsulation.NewIPIP(tc.strategy))
|
||||
if diff := pretty.Compare(routes, tc.routes); diff != "" {
|
||||
t.Errorf("test case %q: got diff: %v", tc.name, diff)
|
||||
}
|
||||
if diff := pretty.Compare(rules, tc.rules); diff != "" {
|
||||
t.Errorf("test case %q: got diff: %v", tc.name, diff)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user