topology.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. }
  53. type segment struct {
  54. allowedIPs []*net.IPNet
  55. endpoint *wireguard.Endpoint
  56. key []byte
  57. // Location is the logical location of this segment.
  58. location string
  59. // cidrs is a slice of subnets of all peers in the segment.
  60. cidrs []*net.IPNet
  61. // hostnames is a slice of the hostnames of the peers in the segment.
  62. hostnames []string
  63. // leader is the index of the leader of the segment.
  64. leader int
  65. // privateIPs is a slice of private IPs of all peers in the segment.
  66. privateIPs []net.IP
  67. // wireGuardIP is the allocated IP address of the WireGuard
  68. // interface on the leader of the segment.
  69. wireGuardIP net.IP
  70. }
  71. // NewTopology creates a new Topology struct from a given set of nodes and peers.
  72. 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) {
  73. topoMap := make(map[string][]*Node)
  74. for _, node := range nodes {
  75. var location string
  76. switch granularity {
  77. case LogicalGranularity:
  78. location = logicalLocationPrefix + node.Location
  79. // Put node in a different location, if no private
  80. // IP was found.
  81. if node.InternalIP == nil {
  82. location = nodeLocationPrefix + node.Name
  83. }
  84. case FullGranularity:
  85. location = nodeLocationPrefix + node.Name
  86. }
  87. topoMap[location] = append(topoMap[location], node)
  88. }
  89. var localLocation string
  90. switch granularity {
  91. case LogicalGranularity:
  92. localLocation = logicalLocationPrefix + nodes[hostname].Location
  93. if nodes[hostname].InternalIP == nil {
  94. localLocation = nodeLocationPrefix + hostname
  95. }
  96. case FullGranularity:
  97. localLocation = nodeLocationPrefix + hostname
  98. }
  99. t := Topology{key: key, port: port, hostname: hostname, location: localLocation, persistentKeepalive: persistentKeepalive, privateIP: nodes[hostname].InternalIP, subnet: nodes[hostname].Subnet, wireGuardCIDR: subnet}
  100. for location := range topoMap {
  101. // Sort the location so the result is stable.
  102. sort.Slice(topoMap[location], func(i, j int) bool {
  103. return topoMap[location][i].Name < topoMap[location][j].Name
  104. })
  105. leader := findLeader(topoMap[location])
  106. if location == localLocation && topoMap[location][leader].Name == hostname {
  107. t.leader = true
  108. }
  109. var allowedIPs []*net.IPNet
  110. var cidrs []*net.IPNet
  111. var hostnames []string
  112. var privateIPs []net.IP
  113. for _, node := range topoMap[location] {
  114. // Allowed IPs should include:
  115. // - the node's allocated subnet
  116. // - the node's WireGuard IP
  117. // - the node's internal IP
  118. allowedIPs = append(allowedIPs, node.Subnet)
  119. if node.InternalIP != nil {
  120. allowedIPs = append(allowedIPs, oneAddressCIDR(node.InternalIP.IP))
  121. privateIPs = append(privateIPs, node.InternalIP.IP)
  122. }
  123. cidrs = append(cidrs, node.Subnet)
  124. hostnames = append(hostnames, node.Name)
  125. }
  126. t.segments = append(t.segments, &segment{
  127. allowedIPs: allowedIPs,
  128. endpoint: topoMap[location][leader].Endpoint,
  129. key: topoMap[location][leader].Key,
  130. location: location,
  131. cidrs: cidrs,
  132. hostnames: hostnames,
  133. leader: leader,
  134. privateIPs: privateIPs,
  135. })
  136. }
  137. // Sort the Topology segments so the result is stable.
  138. sort.Slice(t.segments, func(i, j int) bool {
  139. return t.segments[i].location < t.segments[j].location
  140. })
  141. for _, peer := range peers {
  142. t.peers = append(t.peers, peer)
  143. }
  144. // Sort the Topology peers so the result is stable.
  145. sort.Slice(t.peers, func(i, j int) bool {
  146. return t.peers[i].Name < t.peers[j].Name
  147. })
  148. // We need to defensively deduplicate peer allowed IPs. If two peers claim the same IP,
  149. // the WireGuard configuration could flap, causing the interface to churn.
  150. t.peers = deduplicatePeerIPs(t.peers)
  151. // Allocate IPs to the segment leaders in a stable, coordination-free manner.
  152. a := newAllocator(*subnet)
  153. for _, segment := range t.segments {
  154. ipNet := a.next()
  155. if ipNet == nil {
  156. return nil, errors.New("failed to allocate an IP address; ran out of IP addresses")
  157. }
  158. segment.wireGuardIP = ipNet.IP
  159. segment.allowedIPs = append(segment.allowedIPs, oneAddressCIDR(ipNet.IP))
  160. if t.leader && segment.location == t.location {
  161. t.wireGuardCIDR = &net.IPNet{IP: ipNet.IP, Mask: subnet.Mask}
  162. }
  163. }
  164. return &t, nil
  165. }
  166. // Conf generates a WireGuard configuration file for a given Topology.
  167. func (t *Topology) Conf() *wireguard.Conf {
  168. c := &wireguard.Conf{
  169. Interface: &wireguard.Interface{
  170. PrivateKey: t.key,
  171. ListenPort: t.port,
  172. },
  173. }
  174. for _, s := range t.segments {
  175. if s.location == t.location {
  176. continue
  177. }
  178. peer := &wireguard.Peer{
  179. AllowedIPs: s.allowedIPs,
  180. Endpoint: s.endpoint,
  181. PersistentKeepalive: t.persistentKeepalive,
  182. PublicKey: s.key,
  183. }
  184. c.Peers = append(c.Peers, peer)
  185. }
  186. for _, p := range t.peers {
  187. peer := &wireguard.Peer{
  188. AllowedIPs: p.AllowedIPs,
  189. Endpoint: p.Endpoint,
  190. PersistentKeepalive: t.persistentKeepalive,
  191. PresharedKey: p.PresharedKey,
  192. PublicKey: p.PublicKey,
  193. }
  194. c.Peers = append(c.Peers, peer)
  195. }
  196. return c
  197. }
  198. // AsPeer generates the WireGuard peer configuration for the local location of the given Topology.
  199. // This configuration can be used to configure this location as a peer of another WireGuard interface.
  200. func (t *Topology) AsPeer() *wireguard.Peer {
  201. for _, s := range t.segments {
  202. if s.location != t.location {
  203. continue
  204. }
  205. return &wireguard.Peer{
  206. AllowedIPs: s.allowedIPs,
  207. Endpoint: s.endpoint,
  208. PublicKey: s.key,
  209. }
  210. }
  211. return nil
  212. }
  213. // PeerConf generates a WireGuard configuration file for a given peer in a Topology.
  214. func (t *Topology) PeerConf(name string) *wireguard.Conf {
  215. var pka int
  216. var psk []byte
  217. for i := range t.peers {
  218. if t.peers[i].Name == name {
  219. pka = t.peers[i].PersistentKeepalive
  220. psk = t.peers[i].PresharedKey
  221. break
  222. }
  223. }
  224. c := &wireguard.Conf{}
  225. for _, s := range t.segments {
  226. peer := &wireguard.Peer{
  227. AllowedIPs: s.allowedIPs,
  228. Endpoint: s.endpoint,
  229. PersistentKeepalive: pka,
  230. PresharedKey: psk,
  231. PublicKey: s.key,
  232. }
  233. c.Peers = append(c.Peers, peer)
  234. }
  235. for i := range t.peers {
  236. if t.peers[i].Name == name {
  237. continue
  238. }
  239. peer := &wireguard.Peer{
  240. AllowedIPs: t.peers[i].AllowedIPs,
  241. PersistentKeepalive: pka,
  242. PublicKey: t.peers[i].PublicKey,
  243. Endpoint: t.peers[i].Endpoint,
  244. }
  245. c.Peers = append(c.Peers, peer)
  246. }
  247. return c
  248. }
  249. // oneAddressCIDR takes an IP address and returns a CIDR
  250. // that contains only that address.
  251. func oneAddressCIDR(ip net.IP) *net.IPNet {
  252. return &net.IPNet{IP: ip, Mask: net.CIDRMask(len(ip)*8, len(ip)*8)}
  253. }
  254. // findLeader selects a leader for the nodes in a segment;
  255. // it will select the first node that says it should lead
  256. // or the first node in the segment if none have volunteered,
  257. // always preferring those with a public external IP address,
  258. func findLeader(nodes []*Node) int {
  259. var leaders, public []int
  260. for i := range nodes {
  261. if nodes[i].Leader {
  262. if isPublic(nodes[i].Endpoint.IP) {
  263. return i
  264. }
  265. leaders = append(leaders, i)
  266. }
  267. if isPublic(nodes[i].Endpoint.IP) {
  268. public = append(public, i)
  269. }
  270. }
  271. if len(leaders) != 0 {
  272. return leaders[0]
  273. }
  274. if len(public) != 0 {
  275. return public[0]
  276. }
  277. return 0
  278. }
  279. func deduplicatePeerIPs(peers []*Peer) []*Peer {
  280. ps := make([]*Peer, len(peers))
  281. ips := make(map[string]struct{})
  282. for i, peer := range peers {
  283. p := Peer{
  284. Name: peer.Name,
  285. Peer: wireguard.Peer{
  286. Endpoint: peer.Endpoint,
  287. PersistentKeepalive: peer.PersistentKeepalive,
  288. PresharedKey: peer.PresharedKey,
  289. PublicKey: peer.PublicKey,
  290. },
  291. }
  292. for _, ip := range peer.AllowedIPs {
  293. if _, ok := ips[ip.String()]; ok {
  294. continue
  295. }
  296. p.AllowedIPs = append(p.AllowedIPs, ip)
  297. ips[ip.String()] = struct{}{}
  298. }
  299. ps[i] = &p
  300. }
  301. return ps
  302. }