Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cross mesh granularity #98

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/kg/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ var (
availableGranularities = strings.Join([]string{
string(mesh.LogicalGranularity),
string(mesh.FullGranularity),
string(mesh.CrossGranularity),
}, ", ")
availableLogLevels = strings.Join([]string{
logLevelAll,
Expand Down Expand Up @@ -157,6 +158,7 @@ func Main() error {
switch gr {
case mesh.LogicalGranularity:
case mesh.FullGranularity:
case mesh.CrossGranularity:
default:
return fmt.Errorf("mesh granularity %v unknown; possible values are: %s", *granularity, availableGranularities)
}
Expand Down
6 changes: 6 additions & 0 deletions docs/topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ kgctl graph | circo -Tsvg > cluster.svg

<img src="./graphs/full-mesh.svg" />

# Cross Mesh

In this topology all nodes within the same location are not encrypted. Traffic to any other node outside of current location is encrypted
with direct node-to-node encryption. To use this mesh specify `--mesh-granularity=cross`.


## Mixed

The `kilo.squat.ai/location` annotation can be used to create cluster mixing some fully meshed nodes and some nodes grouped by logical location.
Expand Down
3 changes: 3 additions & 0 deletions pkg/mesh/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const (
// FullGranularity indicates that the network should create
// a mesh between every node.
FullGranularity Granularity = "full"
// CrossGranularity indicates that network is encrypted only
// between nodes in different locations.
CrossGranularity Granularity = "cross"
)

// Node represents a node in the network.
Expand Down
2 changes: 1 addition & 1 deletion pkg/mesh/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func (t *Topology) Routes(kiloIfaceName string, kiloIface, privIface, tunlIface
}
for _, segment := range t.segments {
// Add routes for the current segment if local is true.
if segment.location == t.location {
if (segment.location == t.location) || (t.nodeLocation != "" && segment.nodeLocation == t.nodeLocation) {
if local {
for i := range segment.cidrs {
// Don't add routes for the local node.
Expand Down
74 changes: 51 additions & 23 deletions pkg/mesh/topology.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ type Topology struct {
// key is the private key of the node creating the topology.
key []byte
port uint32
// Location is the logical location of the local host.
// Location is the logical location of the local host of host name.
location string
// nodeLocation is the location annotation of the node. This is set only in cross location topology.
nodeLocation string

segments []*segment
peers []*Peer

Expand Down Expand Up @@ -56,7 +59,8 @@ type segment struct {
key []byte
// Location is the logical location of this segment.
location string

// nodeLocation is the node location annotation. This is set only for cross location topology.
nodeLocation string
// cidrs is a slice of subnets of all peers in the segment.
cidrs []*net.IPNet
// hostnames is a slice of the hostnames of the peers in the segment.
Expand All @@ -70,35 +74,58 @@ type segment struct {
wireGuardIP net.IP
}

// topoKey is used to group nodes into locations.
type topoKey struct {
location string
nodeLocation string
}

// NewTopology creates a new Topology struct from a given set of nodes and peers.
func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Granularity, hostname string, port uint32, key []byte, subnet *net.IPNet, persistentKeepalive int) (*Topology, error) {
topoMap := make(map[string][]*Node)
topoMap := make(map[topoKey][]*Node)
var localLocation, localNodeLocation string
switch granularity {
case LogicalGranularity:
localLocation = nodes[hostname].Location
case CrossGranularity:
localLocation = hostname
localNodeLocation = nodes[hostname].Location
case FullGranularity:
localLocation = hostname
}

for _, node := range nodes {
var location string
var location, nodeLocation string
switch granularity {
case LogicalGranularity:
location = node.Location
case CrossGranularity:
location = node.Name
nodeLocation = node.Location
case FullGranularity:
location = node.Name
}
topoMap[location] = append(topoMap[location], node)
}
var localLocation string
switch granularity {
case LogicalGranularity:
localLocation = nodes[hostname].Location
case FullGranularity:
localLocation = hostname
key := topoKey{location: location, nodeLocation: nodeLocation}
topoMap[key] = append(topoMap[key], node)
}

t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet}
t := Topology{
key: key,
port: port,
hostname: hostname,
location: localLocation,
nodeLocation: localNodeLocation,
persistentKeepalive: persistentKeepalive,
privateIP: nodes[hostname].InternalIP,
subnet: nodes[hostname].Subnet,
}
for location := range topoMap {
// Sort the location so the result is stable.
sort.Slice(topoMap[location], func(i, j int) bool {
return topoMap[location][i].Name < topoMap[location][j].Name
})
leader := findLeader(topoMap[location])
if location == localLocation && topoMap[location][leader].Name == hostname {
if location.nodeLocation != "" || (location.location == localLocation && topoMap[location][leader].Name == hostname) {
t.leader = true
}
var allowedIPs []*net.IPNet
Expand All @@ -116,14 +143,15 @@ func NewTopology(nodes map[string]*Node, peers map[string]*Peer, granularity Gra
privateIPs = append(privateIPs, node.InternalIP.IP)
}
t.segments = append(t.segments, &segment{
allowedIPs: allowedIPs,
endpoint: topoMap[location][leader].Endpoint,
key: topoMap[location][leader].Key,
location: location,
cidrs: cidrs,
hostnames: hostnames,
leader: leader,
privateIPs: privateIPs,
allowedIPs: allowedIPs,
endpoint: topoMap[location][leader].Endpoint,
key: topoMap[location][leader].Key,
location: location.location,
nodeLocation: location.nodeLocation,
cidrs: cidrs,
hostnames: hostnames,
leader: leader,
privateIPs: privateIPs,
})
}
// Sort the Topology segments so the result is stable.
Expand Down Expand Up @@ -167,7 +195,7 @@ func (t *Topology) Conf() *wireguard.Conf {
},
}
for _, s := range t.segments {
if s.location == t.location {
if (s.location == t.location) || (t.nodeLocation != "" && t.nodeLocation == s.nodeLocation) {
continue
}
peer := &wireguard.Peer{
Expand Down
Loading