topology.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2019 the Kilo authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package mesh
  15. import (
  16. "errors"
  17. "net"
  18. "sort"
  19. "github.com/squat/kilo/pkg/wireguard"
  20. )
  21. const (
  22. logicalLocationPrefix = "location:"
  23. nodeLocationPrefix = "node:"
  24. )
  25. // Topology represents the logical structure of the overlay network.
  26. type Topology struct {
  27. // key is the private key of the node creating the topology.
  28. key []byte
  29. port uint32
  30. // Location is the logical location of the local host.
  31. location string
  32. segments []*segment
  33. peers []*Peer
  34. // hostname is the hostname of the local host.
  35. hostname string
  36. // leader represents whether or not the local host
  37. // is the segment leader.
  38. leader bool
  39. // persistentKeepalive is the interval in seconds of the emission
  40. // of keepalive packets by the local node to its peers.
  41. persistentKeepalive int
  42. // privateIP is the private IP address of the local node.
  43. privateIP *net.IPNet
  44. // subnet is the Pod subnet of the local node.
  45. subnet *net.IPNet
  46. // wireGuardCIDR is the allocated CIDR of the WireGuard
  47. // interface of the local node within the Kilo subnet.
  48. // If the local node is not the leader of a location, then
  49. // the IP is the 0th address in the subnet, i.e. the CIDR
  50. // is equal to the Kilo subnet.
  51. wireGuardCIDR *net.IPNet
  52. // discoveredEndpoints is the updated map of valid discovered Endpoints
  53. discoveredEndpoints map[string]*wireguard.Endpoint
  54. }
  55. type segment struct {
  56. allowedIPs []*net.IPNet
  57. endpoint *wireguard.Endpoint
  58. key []byte
  59. persistentKeepalive int
  60. // Location is the logical location of this segment.
  61. location string
  62. // cidrs is a slice of subnets of all peers in the segment.
  63. cidrs []*net.IPNet
  64. // hostnames is a slice of the hostnames of the peers in the segment.
  65. hostnames []string
  66. // leader is the index of the leader of the segment.
  67. leader int
  68. // privateIPs is a slice of private IPs of all peers in the segment.
  69. privateIPs []net.IP
  70. // wireGuardIP is the allocated IP address of the WireGuard
  71. // interface on the leader of the segment.
  72. wireGuardIP net.IP
  73. }
  74. // NewTopology creates a new Topology struct from a given set of nodes and peers.
  75. 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) {
  76. topoMap := make(map[string][]*Node)
  77. for _, node := range nodes {
  78. var location string
  79. switch granularity {
  80. case LogicalGranularity:
  81. location = logicalLocationPrefix + node.Location
  82. // Put node in a different location, if no private
  83. // IP was found.
  84. if node.InternalIP == nil {
  85. location = nodeLocationPrefix + node.Name
  86. }
  87. case FullGranularity:
  88. location = nodeLocationPrefix + node.Name
  89. }
  90. topoMap[location] = append(topoMap[location], node)
  91. }
  92. var localLocation string
  93. switch granularity {
  94. case LogicalGranularity:
  95. localLocation = logicalLocationPrefix + nodes[hostname].Location
  96. if nodes[hostname].InternalIP == nil {
  97. localLocation = nodeLocationPrefix + hostname
  98. }
  99. case FullGranularity:
  100. localLocation = nodeLocationPrefix + hostname
  101. }
  102. t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet, wireGuardCIDR: subnet, discoveredEndpoints: make(map[string]*wireguard.Endpoint)}
  103. for location := range topoMap {
  104. // Sort the location so the result is stable.
  105. sort.Slice(topoMap[location], func(i, j int) bool {
  106. return topoMap[location][i].Name < topoMap[location][j].Name
  107. })
  108. leader := findLeader(topoMap[location])
  109. if location == localLocation && topoMap[location][leader].Name == hostname {
  110. t.leader = true
  111. }
  112. var allowedIPs []*net.IPNet
  113. var cidrs []*net.IPNet
  114. var hostnames []string
  115. var privateIPs []net.IP
  116. for _, node := range topoMap[location] {
  117. // Allowed IPs should include:
  118. // - the node's allocated subnet
  119. // - the node's WireGuard IP
  120. // - the node's internal IP
  121. allowedIPs = append(allowedIPs, node.Subnet)
  122. if node.InternalIP != nil {
  123. allowedIPs = append(allowedIPs, oneAddressCIDR(node.InternalIP.IP))
  124. privateIPs = append(privateIPs, node.InternalIP.IP)
  125. }
  126. cidrs = append(cidrs, node.Subnet)
  127. hostnames = append(hostnames, node.Name)
  128. }
  129. t.segments = append(t.segments, &segment{
  130. allowedIPs: allowedIPs,
  131. endpoint: topoMap[location][leader].Endpoint,
  132. key: topoMap[location][leader].Key,
  133. persistentKeepalive: topoMap[location][leader].PersistentKeepalive,
  134. location: location,
  135. cidrs: cidrs,
  136. hostnames: hostnames,
  137. leader: leader,
  138. privateIPs: privateIPs,
  139. })
  140. }
  141. // Sort the Topology segments so the result is stable.
  142. sort.Slice(t.segments, func(i, j int) bool {
  143. return t.segments[i].location < t.segments[j].location
  144. })
  145. for _, peer := range peers {
  146. t.peers = append(t.peers, peer)
  147. }
  148. // Sort the Topology peers so the result is stable.
  149. sort.Slice(t.peers, func(i, j int) bool {
  150. return t.peers[i].Name < t.peers[j].Name
  151. })
  152. // We need to defensively deduplicate peer allowed IPs. If two peers claim the same IP,
  153. // the WireGuard configuration could flap, causing the interface to churn.
  154. t.peers = deduplicatePeerIPs(t.peers)
  155. // Copy the host node DiscoveredEndpoints in the topology as a starting point.
  156. for key := range nodes[hostname].DiscoveredEndpoints {
  157. t.discoveredEndpoints[key] = nodes[hostname].DiscoveredEndpoints[key]
  158. }
  159. // Allocate IPs to the segment leaders in a stable, coordination-free manner.
  160. a := newAllocator(*subnet)
  161. for _, segment := range t.segments {
  162. ipNet := a.next()
  163. if ipNet == nil {
  164. return nil, errors.New("failed to allocate an IP address; ran out of IP addresses")
  165. }
  166. segment.wireGuardIP = ipNet.IP
  167. segment.allowedIPs = append(segment.allowedIPs, oneAddressCIDR(ipNet.IP))
  168. if t.leader && segment.location == t.location {
  169. t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
  170. }
  171. // Now that the topology is ordered, update the discoveredEndpoints map
  172. // add new ones by going through the ordered topology: segments, nodes
  173. for _, node := range topoMap[segment.location] {
  174. for key := range node.DiscoveredEndpoints {
  175. if _, ok := t.discoveredEndpoints[key]; !ok {
  176. t.discoveredEndpoints[key] = node.DiscoveredEndpoints[key]
  177. }
  178. }
  179. }
  180. }
  181. return &t, nil
  182. }
  183. func (t *Topology) updateEndpoint(endpoint *wireguard.Endpoint, key []byte, persistentKeepalive int) *wireguard.Endpoint {
  184. // Do not update non-nat peers
  185. if persistentKeepalive == 0 {
  186. return endpoint
  187. }
  188. e, ok := t.discoveredEndpoints[string(key)]
  189. if ok {
  190. return e
  191. }
  192. return endpoint
  193. }
  194. // Conf generates a WireGuard configuration file for a given Topology.
  195. func (t *Topology) Conf() *wireguard.Conf {
  196. c := &wireguard.Conf{
  197. Interface: &wireguard.Interface{
  198. PrivateKey: t.key,
  199. ListenPort: t.port,
  200. },
  201. }
  202. for _, s := range t.segments {
  203. if s.location == t.location {
  204. continue
  205. }
  206. peer := &wireguard.Peer{
  207. AllowedIPs: s.allowedIPs,
  208. Endpoint: t.updateEndpoint(s.endpoint, s.key, s.persistentKeepalive),
  209. PersistentKeepalive: t.persistentKeepalive,
  210. PublicKey: s.key,
  211. }
  212. c.Peers = append(c.Peers, peer)
  213. }
  214. for _, p := range t.peers {
  215. peer := &wireguard.Peer{
  216. AllowedIPs: p.AllowedIPs,
  217. Endpoint: t.updateEndpoint(p.Endpoint, p.PublicKey, p.PersistentKeepalive),
  218. PersistentKeepalive: t.persistentKeepalive,
  219. PresharedKey: p.PresharedKey,
  220. PublicKey: p.PublicKey,
  221. }
  222. c.Peers = append(c.Peers, peer)
  223. }
  224. return c
  225. }
  226. // AsPeer generates the WireGuard peer configuration for the local location of the given Topology.
  227. // This configuration can be used to configure this location as a peer of another WireGuard interface.
  228. func (t *Topology) AsPeer() *wireguard.Peer {
  229. for _, s := range t.segments {
  230. if s.location != t.location {
  231. continue
  232. }
  233. return &wireguard.Peer{
  234. AllowedIPs: s.allowedIPs,
  235. Endpoint: s.endpoint,
  236. PublicKey: s.key,
  237. }
  238. }
  239. return nil
  240. }
  241. // PeerConf generates a WireGuard configuration file for a given peer in a Topology.
  242. func (t *Topology) PeerConf(name string) *wireguard.Conf {
  243. var pka int
  244. var psk []byte
  245. for i := range t.peers {
  246. if t.peers[i].Name == name {
  247. pka = t.peers[i].PersistentKeepalive
  248. psk = t.peers[i].PresharedKey
  249. break
  250. }
  251. }
  252. c := &wireguard.Conf{}
  253. for _, s := range t.segments {
  254. peer := &wireguard.Peer{
  255. AllowedIPs: s.allowedIPs,
  256. Endpoint: s.endpoint,
  257. PersistentKeepalive: pka,
  258. PresharedKey: psk,
  259. PublicKey: s.key,
  260. }
  261. c.Peers = append(c.Peers, peer)
  262. }
  263. for i := range t.peers {
  264. if t.peers[i].Name == name {
  265. continue
  266. }
  267. peer := &wireguard.Peer{
  268. AllowedIPs: t.peers[i].AllowedIPs,
  269. PersistentKeepalive: pka,
  270. PublicKey: t.peers[i].PublicKey,
  271. Endpoint: t.peers[i].Endpoint,
  272. }
  273. c.Peers = append(c.Peers, peer)
  274. }
  275. return c
  276. }
  277. // oneAddressCIDR takes an IP address and returns a CIDR
  278. // that contains only that address.
  279. func oneAddressCIDR(ip net.IP) *net.IPNet {
  280. return &net.IPNet{IP: ip, Mask: net.CIDRMask(len(ip)*8, len(ip)*8)}
  281. }
  282. // findLeader selects a leader for the nodes in a segment;
  283. // it will select the first node that says it should lead
  284. // or the first node in the segment if none have volunteered,
  285. // always preferring those with a public external IP address,
  286. func findLeader(nodes []*Node) int {
  287. var leaders, public []int
  288. for i := range nodes {
  289. if nodes[i].Leader {
  290. if isPublic(nodes[i].Endpoint.IP) {
  291. return i
  292. }
  293. leaders = append(leaders, i)
  294. }
  295. if isPublic(nodes[i].Endpoint.IP) {
  296. public = append(public, i)
  297. }
  298. }
  299. if len(leaders) != 0 {
  300. return leaders[0]
  301. }
  302. if len(public) != 0 {
  303. return public[0]
  304. }
  305. return 0
  306. }
  307. func deduplicatePeerIPs(peers []*Peer) []*Peer {
  308. ps := make([]*Peer, len(peers))
  309. ips := make(map[string]struct{})
  310. for i, peer := range peers {
  311. p := Peer{
  312. Name: peer.Name,
  313. Peer: wireguard.Peer{
  314. Endpoint: peer.Endpoint,
  315. PersistentKeepalive: peer.PersistentKeepalive,
  316. PresharedKey: peer.PresharedKey,
  317. PublicKey: peer.PublicKey,
  318. },
  319. }
  320. for _, ip := range peer.AllowedIPs {
  321. if _, ok := ips[ip.String()]; ok {
  322. continue
  323. }
  324. p.AllowedIPs = append(p.AllowedIPs, ip)
  325. ips[ip.String()] = struct{}{}
  326. }
  327. ps[i] = &p
  328. }
  329. return ps
  330. }