Просмотр исходного кода

Define WireGuard PersistentKeepAlive via Annotation (#31)

* Add WireGuardPersistentKeepAlive to mesh.Node

* Connect to configuration

* Shorten keepalive key

* Fix casing on keepalive

* Add annotated keepalive value to peer functions
Francis Nguyen 6 лет назад
Родитель
Сommit
6de0f9805a
5 измененных файлов с 127 добавлено и 86 удалено
  1. 19 8
      pkg/k8s/backend.go
  2. 19 8
      pkg/k8s/backend_test.go
  3. 6 5
      pkg/mesh/mesh.go
  4. 18 11
      pkg/mesh/topology.go
  5. 65 54
      pkg/mesh/topology_test.go

+ 19 - 8
pkg/k8s/backend.go

@@ -56,6 +56,7 @@ const (
 	lastSeenAnnotationKey        = "kilo.squat.ai/last-seen"
 	leaderAnnotationKey          = "kilo.squat.ai/leader"
 	locationAnnotationKey        = "kilo.squat.ai/location"
+	persistentKeepaliveKey       = "kilo.squat.ai/persistent-keepalive"
 	wireGuardIPAnnotationKey     = "kilo.squat.ai/wireguard-ip"
 
 	regionLabelKey  = "topology.kubernetes.io/region"
@@ -262,6 +263,15 @@ func translateNode(node *v1.Node) *mesh.Node {
 	if !ok {
 		internalIP = node.ObjectMeta.Annotations[internalIPAnnotationKey]
 	}
+	// Set Wireguard PersistentKeepalive setting for the node.
+	var persistentKeepalive int64
+	if keepAlive, ok := node.ObjectMeta.Annotations[persistentKeepaliveKey]; !ok {
+		persistentKeepalive = 0
+	} else {
+		if persistentKeepalive, err = strconv.ParseInt(keepAlive, 10, 64); err != nil {
+			persistentKeepalive = 0
+		}
+	}
 	var lastSeen int64
 	if ls, ok := node.ObjectMeta.Annotations[lastSeenAnnotationKey]; !ok {
 		lastSeen = 0
@@ -275,14 +285,15 @@ func translateNode(node *v1.Node) *mesh.Node {
 		// remote node's agent has not yet set its IP address;
 		// in this case the IP will be nil and
 		// the mesh can wait for the node to be updated.
-		ExternalIP: normalizeIP(externalIP),
-		InternalIP: normalizeIP(internalIP),
-		Key:        []byte(node.ObjectMeta.Annotations[keyAnnotationKey]),
-		LastSeen:   lastSeen,
-		Leader:     leader,
-		Location:   location,
-		Name:       node.Name,
-		Subnet:     subnet,
+		ExternalIP:          normalizeIP(externalIP),
+		InternalIP:          normalizeIP(internalIP),
+		Key:                 []byte(node.ObjectMeta.Annotations[keyAnnotationKey]),
+		LastSeen:            lastSeen,
+		Leader:              leader,
+		Location:            location,
+		Name:                node.Name,
+		PersistentKeepalive: int(persistentKeepalive),
+		Subnet:              subnet,
 		// WireGuardIP can fail to parse if the node is not a leader or if
 		// the node's agent has not yet reconciled. In either case, the IP
 		// will parse as nil.

+ 19 - 8
pkg/k8s/backend_test.go

@@ -111,6 +111,15 @@ func TestTranslateNode(t *testing.T) {
 				ExternalIP: &net.IPNet{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
 			},
 		},
+		{
+			name: "wireguard persistent keepalive override",
+			annotations: map[string]string{
+				persistentKeepaliveKey: "25",
+			},
+			out: &mesh.Node{
+				PersistentKeepalive: 25,
+			},
+		},
 		{
 			name: "internal IP override",
 			annotations: map[string]string{
@@ -139,20 +148,22 @@ func TestTranslateNode(t *testing.T) {
 				lastSeenAnnotationKey:        "1000000000",
 				leaderAnnotationKey:          "",
 				locationAnnotationKey:        "b",
+				persistentKeepaliveKey:       "25",
 				wireGuardIPAnnotationKey:     "10.4.0.1/16",
 			},
 			labels: map[string]string{
 				regionLabelKey: "a",
 			},
 			out: &mesh.Node{
-				ExternalIP:  &net.IPNet{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
-				InternalIP:  &net.IPNet{IP: net.ParseIP("10.1.0.2"), Mask: net.CIDRMask(32, 32)},
-				Key:         []byte("foo"),
-				LastSeen:    1000000000,
-				Leader:      true,
-				Location:    "b",
-				Subnet:      &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
-				WireGuardIP: &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
+				ExternalIP:          &net.IPNet{IP: net.ParseIP("10.0.0.2"), Mask: net.CIDRMask(24, 32)},
+				InternalIP:          &net.IPNet{IP: net.ParseIP("10.1.0.2"), Mask: net.CIDRMask(32, 32)},
+				Key:                 []byte("foo"),
+				LastSeen:            1000000000,
+				Leader:              true,
+				Location:            "b",
+				PersistentKeepalive: 25,
+				Subnet:              &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
+				WireGuardIP:         &net.IPNet{IP: net.ParseIP("10.4.0.1"), Mask: net.CIDRMask(16, 32)},
 			},
 			subnet: "10.2.1.0/24",
 		},

+ 6 - 5
pkg/mesh/mesh.go

@@ -79,11 +79,12 @@ type Node struct {
 	LastSeen int64
 	// Leader is a suggestion to Kilo that
 	// the node wants to lead its segment.
-	Leader      bool
-	Location    string
-	Name        string
-	Subnet      *net.IPNet
-	WireGuardIP *net.IPNet
+	Leader              bool
+	Location            string
+	Name                string
+	PersistentKeepalive int
+	Subnet              *net.IPNet
+	WireGuardIP         *net.IPNet
 }
 
 // Ready indicates whether or not the node is ready.

+ 18 - 11
pkg/mesh/topology.go

@@ -64,6 +64,9 @@ type segment struct {
 	hostnames []string
 	// leader is the index of the leader of the segment.
 	leader int
+	// persistentKeepalive is the interval in seconds of the emission
+	// of keepalive packets to the peer.
+	persistentKeepalive int
 	// privateIPs is a slice of private IPs of all peers in the segment.
 	privateIPs []net.IP
 	// wireGuardIP is the allocated IP address of the WireGuard
@@ -117,14 +120,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].ExternalIP.IP,
-			key:        topoMap[location][leader].Key,
-			location:   location,
-			cidrs:      cidrs,
-			hostnames:  hostnames,
-			leader:     leader,
-			privateIPs: privateIPs,
+			allowedIPs:          allowedIPs,
+			endpoint:            topoMap[location][leader].ExternalIP.IP,
+			key:                 topoMap[location][leader].Key,
+			location:            location,
+			cidrs:               cidrs,
+			hostnames:           hostnames,
+			leader:              leader,
+			privateIPs:          privateIPs,
+			persistentKeepalive: topoMap[location][leader].PersistentKeepalive,
 		})
 	}
 	// Sort the Topology segments so the result is stable.
@@ -334,7 +338,8 @@ func (t *Topology) Conf() *wireguard.Conf {
 				IP:   s.endpoint,
 				Port: uint32(t.port),
 			},
-			PublicKey: s.key,
+			PublicKey:           s.key,
+			PersistentKeepalive: s.persistentKeepalive,
 		}
 		c.Peers = append(c.Peers, peer)
 	}
@@ -363,7 +368,8 @@ func (t *Topology) AsPeer() *wireguard.Peer {
 				IP:   s.endpoint,
 				Port: uint32(t.port),
 			},
-			PublicKey: s.key,
+			PersistentKeepalive: s.persistentKeepalive,
+			PublicKey:           s.key,
 		}
 	}
 	return nil
@@ -379,7 +385,8 @@ func (t *Topology) PeerConf(name string) *wireguard.Conf {
 				IP:   s.endpoint,
 				Port: uint32(t.port),
 			},
-			PublicKey: s.key,
+			PersistentKeepalive: s.persistentKeepalive,
+			PublicKey:           s.key,
 		}
 		c.Peers = append(c.Peers, peer)
 	}

+ 65 - 54
pkg/mesh/topology_test.go

@@ -39,12 +39,13 @@ func setup(t *testing.T) (map[string]*Node, map[string]*Peer, []byte, uint32) {
 	i2 := &net.IPNet{IP: net.ParseIP("192.168.0.2").To4(), Mask: net.CIDRMask(32, 32)}
 	nodes := map[string]*Node{
 		"a": {
-			Name:       "a",
-			ExternalIP: e1,
-			InternalIP: i1,
-			Location:   "1",
-			Subnet:     &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
-			Key:        []byte("key1"),
+			Name:                "a",
+			ExternalIP:          e1,
+			InternalIP:          i1,
+			Location:            "1",
+			Subnet:              &net.IPNet{IP: net.ParseIP("10.2.1.0"), Mask: net.CIDRMask(24, 32)},
+			Key:                 []byte("key1"),
+			PersistentKeepalive: 25,
 		},
 		"b": {
 			Name:       "b",
@@ -117,14 +118,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Location,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Location,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -153,14 +155,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Location,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Location,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -189,14 +192,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: nil,
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Location,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Location,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, nodes["c"].Subnet, nodes["c"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -225,14 +229,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: &net.IPNet{IP: w1, Mask: net.CIDRMask(16, 32)},
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Name,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Name,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -271,14 +276,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: &net.IPNet{IP: w2, Mask: net.CIDRMask(16, 32)},
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Name,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Name,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -317,14 +323,15 @@ func TestNewTopology(t *testing.T) {
 				wireGuardCIDR: &net.IPNet{IP: w3, Mask: net.CIDRMask(16, 32)},
 				segments: []*segment{
 					{
-						allowedIPs:  []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
-						endpoint:    nodes["a"].ExternalIP.IP,
-						key:         nodes["a"].Key,
-						location:    nodes["a"].Name,
-						cidrs:       []*net.IPNet{nodes["a"].Subnet},
-						hostnames:   []string{"a"},
-						privateIPs:  []net.IP{nodes["a"].InternalIP.IP},
-						wireGuardIP: w1,
+						allowedIPs:          []*net.IPNet{nodes["a"].Subnet, nodes["a"].InternalIP, {IP: w1, Mask: net.CIDRMask(32, 32)}},
+						endpoint:            nodes["a"].ExternalIP.IP,
+						key:                 nodes["a"].Key,
+						location:            nodes["a"].Name,
+						cidrs:               []*net.IPNet{nodes["a"].Subnet},
+						hostnames:           []string{"a"},
+						privateIPs:          []net.IP{nodes["a"].InternalIP.IP},
+						persistentKeepalive: nodes["a"].PersistentKeepalive,
+						wireGuardIP:         w1,
 					},
 					{
 						allowedIPs:  []*net.IPNet{nodes["b"].Subnet, nodes["b"].InternalIP, {IP: w2, Mask: net.CIDRMask(32, 32)}},
@@ -1027,6 +1034,7 @@ AllowedIPs = 10.5.0.3/24
 		[Peer]
 		PublicKey = key1
 		Endpoint = 10.1.0.1:51820
+		PersistentKeepalive = 25
 		AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
 
 		[Peer]
@@ -1051,6 +1059,7 @@ AllowedIPs = 10.5.0.3/24
 		[Peer]
 		PublicKey = key1
 		Endpoint = 10.1.0.1:51820
+		PersistentKeepalive = 25
 		AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
 
 		[Peer]
@@ -1104,6 +1113,7 @@ AllowedIPs = 10.5.0.3/24
 		[Peer]
 		PublicKey = key1
 		Endpoint = 10.1.0.1:51820
+		PersistentKeepalive = 25
 		AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
 
 		[Peer]
@@ -1133,6 +1143,7 @@ AllowedIPs = 10.5.0.3/24
 		[Peer]
 		PublicKey = key1
 		Endpoint = 10.1.0.1:51820
+		PersistentKeepalive = 25
 		AllowedIPs = 10.2.1.0/24, 192.168.0.1/32, 10.4.0.1/32
 
 		[Peer]