parent
3559fcf3b3
commit
f4e61df211
10
README.md
10
README.md
|
@ -125,13 +125,3 @@ different cloud providers) can lead to a split-brain scenario where each side th
|
|||
There is currently no clean solution for this problem, but one could work around it by designating edge nodes which
|
||||
periodically restart `wesher` with the `--join` option pointing to the other side.
|
||||
Future versions might include the notion of a "static" node to more cleanly avoid this.
|
||||
|
||||
### Broken connections on join/leave
|
||||
|
||||
Currently `wesher` uses wireguard's management commands (`wg` and `wg-quick`) to talk to wireguard. The current
|
||||
implementation is very naive and recreates the wireguard interface for every configuration change (e.g. nodes' joining
|
||||
and leavning). Since wireguard's underlying traffic is essentially stateless, this does not directly impact TCP
|
||||
connections on the overlay network. The connection should treat it as normal packet drops.
|
||||
However, if your application is sensitive to ICMP errors (e.g. "no route to host"), or if you are using UDP, you might
|
||||
experience connection resets and message loss, respectively.
|
||||
This behavior will be improved in future versions.
|
|
@ -107,7 +107,7 @@ func (c *cluster) LocalState(join bool) []byte { return nil }
|
|||
func (c *cluster) MergeRemoteState(buf []byte, join bool) {}
|
||||
|
||||
type nodeMeta struct {
|
||||
OverlayAddr net.IP
|
||||
OverlayAddr net.IPNet
|
||||
PubKey string
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ func (c *cluster) NodeMeta(limit int) []byte {
|
|||
buf := &bytes.Buffer{}
|
||||
if err := gob.NewEncoder(buf).Encode(nodeMeta{
|
||||
OverlayAddr: c.wg.OverlayAddr,
|
||||
PubKey: c.wg.PubKey,
|
||||
PubKey: c.wg.PubKey.String(),
|
||||
}); err != nil {
|
||||
logrus.Errorf("could not encode local state: %s", err)
|
||||
return nil
|
||||
|
@ -128,6 +128,8 @@ func (c *cluster) NodeMeta(limit int) []byte {
|
|||
}
|
||||
|
||||
func decodeNodeMeta(b []byte) (nodeMeta, error) {
|
||||
// TODO: we blindly trust the info we get from the peers; We should be more defensive to limit the damage a leaked
|
||||
// PSK can cause.
|
||||
nm := nodeMeta{}
|
||||
if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&nm); err != nil {
|
||||
return nm, errwrap.Wrapf("could not decode: {{err}}", err)
|
||||
|
|
5
go.mod
5
go.mod
|
@ -6,6 +6,11 @@ require (
|
|||
github.com/hashicorp/go-sockaddr v1.0.0
|
||||
github.com/hashicorp/memberlist v0.1.3
|
||||
github.com/mattn/go-isatty v0.0.7
|
||||
github.com/mdlayher/genetlink v0.0.0-20190617154021-985b2115c31a
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/sirupsen/logrus v1.3.0
|
||||
github.com/stevenroose/gonfig v0.1.4
|
||||
github.com/vishvananda/netlink v1.0.0
|
||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20190629151639-28f4e240be2d
|
||||
)
|
||||
|
|
43
go.sum
43
go.sum
|
@ -7,6 +7,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0=
|
||||
|
@ -23,16 +26,34 @@ github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCO
|
|||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190503083013-8a08cb3e375e/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a h1:84IpUNXj4mCR9CuCEvSiCArMbzr/TMbuPIadKDwypkI=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mdlayher/genetlink v0.0.0-20190513144241-4cdc5dab577c h1:Z7vEAfVdgfkjIzGSOF6vLt8BGu31+DuCJqXlTI7oj3o=
|
||||
github.com/mdlayher/genetlink v0.0.0-20190513144241-4cdc5dab577c/go.mod h1:Gxg/DEIMJtqdXDyq47mB98qcpBHmaLrvOAmKKNRE0Tg=
|
||||
github.com/mdlayher/genetlink v0.0.0-20190617154021-985b2115c31a h1:oL6319kSfjW4J90/GXLRgcL5vHj82AsWgYv95vP0n+E=
|
||||
github.com/mdlayher/genetlink v0.0.0-20190617154021-985b2115c31a/go.mod h1:JrQK8x2Z2oQUE84wv6QvW8/HgMzJIt8bN6vu28tNX1Q=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v0.0.0-20190513144208-ba284d510044/go.mod h1:gOrA34zDL0K3RsACQe54bDYLF/CeFspQ9m5DOycycQ8=
|
||||
github.com/mdlayher/netlink v0.0.0-20190614145538-d8264f87dbe3 h1:3IPcWjiboJFnnvHeXxT4pYw33BiPJn/DC5BKhcGEbGk=
|
||||
github.com/mdlayher/netlink v0.0.0-20190614145538-d8264f87dbe3/go.mod h1:ISujvOTprADlNr00kvJIu0d23q57wk2NSV/PT/TEk4E=
|
||||
github.com/mdlayher/netlink v0.0.0-20190617153422-f82a9b10b2bc h1:deLjDmcgzsCAO+7m2aeuyhQCqvn1LuCCSMLWUARnad8=
|
||||
github.com/microsoft/go-winio v0.4.12 h1:3vDRRsUnj2dKE7QKoedntu9hbuD8gzaVd2E2UZioqx4=
|
||||
github.com/microsoft/go-winio v0.4.12/go.mod h1:kcIxxtKZE55DEncT/EOvFiygPobhUWpSDqDb47poQOU=
|
||||
github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
|
@ -46,11 +67,23 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/vishvananda/netlink v1.0.0 h1:bqNY2lgheFIu1meHUFSH3d7vG93AFyqg3oGbJCOJgSM=
|
||||
github.com/vishvananda/netlink v1.0.0/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f h1:nBX3nTcmxEtHSERBJaIo1Qa26VwRaopnZmfDQUXsF4I=
|
||||
github.com/vishvananda/netns v0.0.0-20190625233234-7109fa855b0f/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190509222800-a4d6f7feada5/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -58,8 +91,18 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5 h1:x6r4Jo0KNzOOzYd8lbcRsqjuq
|
|||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339 h1:g/Jesu8+QLnA0CPzF3E1pURg0Byr7i6jLoX5sqjcAh0=
|
||||
golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190614084037-d442b75600c5 h1:tQrtnaPeNyfkuD2UMixVD6lAa7WngkIFvtWcdzNeq80=
|
||||
golang.org/x/sys v0.0.0-20190614084037-d442b75600c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20190629151639-28f4e240be2d h1:Y4Qa3H/pif1DkMBNwEwxdQ8H2+9Uq564oDIUOH4vGWE=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20190629151639-28f4e240be2d/go.mod h1:3bbJvD15soNWXiSdJMnTG8pIhM2gXvWIm56iXAkxi34=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
|
|
10
main.go
10
main.go
|
@ -53,13 +53,7 @@ func main() {
|
|||
for _, node := range nodes {
|
||||
logrus.Infof("\taddr: %s, overlay: %s, pubkey: %s", node.Addr, node.OverlayAddr, node.PubKey)
|
||||
}
|
||||
if err := wg.downInterface(); err != nil {
|
||||
logrus.Errorf("could not down interface: %s", err)
|
||||
}
|
||||
if err := wg.writeConf(nodes); err != nil {
|
||||
logrus.Errorf("could not write config: %s", err)
|
||||
}
|
||||
if err := wg.upInterface(); err != nil {
|
||||
if err := wg.setUpInterface(nodes); err != nil {
|
||||
logrus.Errorf("could not up interface: %s", err)
|
||||
}
|
||||
if !config.NoEtcHosts {
|
||||
|
@ -88,7 +82,7 @@ func main() {
|
|||
func writeToEtcHosts(nodes []node) error {
|
||||
hosts := make(map[string][]string, len(nodes))
|
||||
for _, n := range nodes {
|
||||
hosts[n.OverlayAddr.String()] = []string{n.Name}
|
||||
hosts[n.OverlayAddr.IP.String()] = []string{n.Name}
|
||||
}
|
||||
hostsFile := &etchosts.EtcHosts{
|
||||
Logger: logrus.StandardLogger(),
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import "github.com/vishvananda/netlink"
|
||||
|
||||
// this is only necessary while this PR is open:
|
||||
// https://github.com/vishvananda/netlink/pull/464
|
||||
|
||||
type wireguard struct {
|
||||
netlink.LinkAttrs
|
||||
}
|
||||
|
||||
func (wg *wireguard) Attrs() *netlink.LinkAttrs {
|
||||
return &wg.LinkAttrs
|
||||
}
|
||||
|
||||
func (wg *wireguard) Type() string {
|
||||
return "wireguard"
|
||||
}
|
18
tests/wg
18
tests/wg
|
@ -1,18 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "nothing to see here"
|
||||
;;
|
||||
genkey)
|
||||
echo "ILICZ3yBMCGAWNIq5Pn0bewBVimW3Q2yRVJ/Be+b1Uc="
|
||||
;;
|
||||
pubkey)
|
||||
read x
|
||||
echo "VceweY6x/QdGXEQ6frXrSd8CwUAInUmqIc6G/qi8FHo="
|
||||
;;
|
||||
*)
|
||||
echo "what?"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
case $1 in
|
||||
up)
|
||||
echo "up $2"
|
||||
;;
|
||||
pubkey)
|
||||
echo "down $2"
|
||||
;;
|
||||
*)
|
||||
echo "what?"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
153
wireguard.go
153
wireguard.go
|
@ -1,53 +1,40 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vishvananda/netlink"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
const wgConfPath = "/etc/wireguard/%s.conf"
|
||||
const wgConfTpl = `
|
||||
# this file was generated automatically by wesher - DO NOT MODIFY
|
||||
[Interface]
|
||||
PrivateKey = {{ .PrivKey }}
|
||||
Address = {{ .OverlayAddr }}
|
||||
ListenPort = {{ .Port }}
|
||||
|
||||
{{ range .Nodes }}
|
||||
[Peer]
|
||||
PublicKey = {{ .PubKey }}
|
||||
Endpoint = {{ .Addr }}:{{ $.Port }}
|
||||
AllowedIPs = {{ .OverlayAddr }}/32
|
||||
{{ end }}`
|
||||
|
||||
type wgState struct {
|
||||
iface string
|
||||
OverlayAddr net.IP
|
||||
client *wgctrl.Client
|
||||
OverlayAddr net.IPNet
|
||||
Port int
|
||||
PrivKey string
|
||||
PubKey string
|
||||
PrivKey wgtypes.Key
|
||||
PubKey wgtypes.Key
|
||||
}
|
||||
|
||||
var wgPath = "wg"
|
||||
var wgQuickPath = "wg-quick"
|
||||
|
||||
func newWGConfig(iface string, port int) (*wgState, error) {
|
||||
if err := exec.Command(wgPath).Run(); err != nil {
|
||||
return nil, fmt.Errorf("could not exec wireguard: %s", err)
|
||||
client, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not instantiate wireguard client")
|
||||
}
|
||||
|
||||
privKey, pubKey, err := wgKeyPair()
|
||||
privKey, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey := privKey.PublicKey()
|
||||
|
||||
wgState := wgState{
|
||||
iface: iface,
|
||||
client: client,
|
||||
Port: port,
|
||||
PrivKey: privKey,
|
||||
PubKey: pubKey,
|
||||
|
@ -69,51 +56,93 @@ func (wg *wgState) assignOverlayAddr(ipnet *net.IPNet, name string) {
|
|||
ip[len(ip)-i] = hb[len(hb)-i]
|
||||
}
|
||||
|
||||
wg.OverlayAddr = net.IP(ip)
|
||||
wg.OverlayAddr = net.IPNet{
|
||||
IP: net.IP(ip),
|
||||
Mask: net.CIDRMask(size, size), // either /32 or /128, depending if ipv4 or ipv6
|
||||
}
|
||||
|
||||
func (wg *wgState) writeConf(nodes []node) error {
|
||||
tpl := template.Must(template.New("wgconf").Parse(wgConfTpl))
|
||||
out, err := os.OpenFile(
|
||||
fmt.Sprintf(wgConfPath, wg.iface),
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
||||
0600,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return tpl.Execute(out, struct {
|
||||
*wgState
|
||||
Nodes []node
|
||||
}{wg, nodes})
|
||||
}
|
||||
|
||||
func (wg *wgState) downInterface() error {
|
||||
if err := exec.Command(wgPath, "show", wg.iface).Run(); err != nil {
|
||||
return nil // assume a failure means the interface is not there
|
||||
if _, err := wg.client.Device(wg.iface); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil // device already gone; noop
|
||||
}
|
||||
return exec.Command(wgQuickPath, "down", wg.iface).Run()
|
||||
return err
|
||||
}
|
||||
link, err := netlink.LinkByName(wg.iface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return netlink.LinkDel(link)
|
||||
}
|
||||
|
||||
func (wg *wgState) upInterface() error {
|
||||
return exec.Command(wgQuickPath, "up", wg.iface).Run()
|
||||
func (wg *wgState) setUpInterface(nodes []node) error {
|
||||
if err := wg.createWgInterface(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
func wgKeyPair() (string, string, error) {
|
||||
cmd := exec.Command(wgPath, "genkey")
|
||||
outPriv := strings.Builder{}
|
||||
cmd.Stdout = &outPriv
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", "", err
|
||||
peerCfgs, err := wg.nodesToPeerConfigs(nodes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error converting received node information to wireguard format")
|
||||
}
|
||||
wg.client.ConfigureDevice(wg.iface, wgtypes.Config{
|
||||
PrivateKey: &wg.PrivKey,
|
||||
ListenPort: &wg.Port,
|
||||
ReplacePeers: true,
|
||||
Peers: peerCfgs,
|
||||
})
|
||||
|
||||
link, err := netlink.LinkByName(wg.iface)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get link information for %s", wg.iface)
|
||||
}
|
||||
netlink.AddrReplace(link, &netlink.Addr{
|
||||
IPNet: &wg.OverlayAddr,
|
||||
})
|
||||
netlink.LinkSetMTU(link, 1420) // TODO: make MTU configurable?
|
||||
netlink.LinkSetUp(link)
|
||||
for _, node := range nodes {
|
||||
netlink.RouteAdd(&netlink.Route{
|
||||
LinkIndex: link.Attrs().Index,
|
||||
Dst: &node.OverlayAddr,
|
||||
Scope: netlink.SCOPE_LINK,
|
||||
})
|
||||
}
|
||||
|
||||
cmd = exec.Command(wgPath, "pubkey")
|
||||
outPub := strings.Builder{}
|
||||
cmd.Stdout = &outPub
|
||||
cmd.Stdin = strings.NewReader(outPriv.String())
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", "", err
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.TrimSpace(outPriv.String()), strings.TrimSpace(outPub.String()), nil
|
||||
func (wg *wgState) nodesToPeerConfigs(nodes []node) ([]wgtypes.PeerConfig, error) {
|
||||
peerCfgs := make([]wgtypes.PeerConfig, len(nodes))
|
||||
for i, node := range nodes {
|
||||
pubKey, err := wgtypes.ParseKey(node.PubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
peerCfgs[i] = wgtypes.PeerConfig{
|
||||
PublicKey: pubKey,
|
||||
ReplaceAllowedIPs: true,
|
||||
Endpoint: &net.UDPAddr{
|
||||
IP: node.Addr,
|
||||
Port: wg.Port,
|
||||
},
|
||||
AllowedIPs: []net.IPNet{
|
||||
node.OverlayAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
return peerCfgs, nil
|
||||
}
|
||||
|
||||
func (wg *wgState) createWgInterface() error {
|
||||
if _, err := wg.client.Device(wg.iface); err == nil {
|
||||
// device already exists, but we are running e2e tests, so we're using the user-mode implementation
|
||||
if _, e2e := os.LookupEnv("WESHER_E2E_TESTS"); e2e {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err := netlink.LinkAdd(&wireguard{LinkAttrs: netlink.LinkAttrs{Name: wg.iface}}); err != nil {
|
||||
return errors.Wrapf(err, "could not create interface %s", wg.iface)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,38 +6,6 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
wgPath = "tests/wg"
|
||||
wgQuickPath = "tests/wg-quick"
|
||||
}
|
||||
|
||||
func Test_wgKeyPair(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want string
|
||||
want1 string
|
||||
wantErr bool
|
||||
}{
|
||||
// see tests/wg for values
|
||||
{"generate fixed values", "ILICZ3yBMCGAWNIq5Pn0bewBVimW3Q2yRVJ/Be+b1Uc=", "VceweY6x/QdGXEQ6frXrSd8CwUAInUmqIc6G/qi8FHo=", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1, err := wgKeyPair()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("wgKeyPair() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("wgKeyPair() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
if got1 != tt.want1 {
|
||||
t.Errorf("wgKeyPair() got1 = %v, want %v", got1, tt.want1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_wgState_assignOverlayAddr(t *testing.T) {
|
||||
type args struct {
|
||||
ipnet *net.IPNet
|
||||
|
@ -66,7 +34,7 @@ func Test_wgState_assignOverlayAddr(t *testing.T) {
|
|||
wg := &wgState{}
|
||||
wg.assignOverlayAddr(tt.args.ipnet, tt.args.name)
|
||||
|
||||
if !reflect.DeepEqual(wg.OverlayAddr.String(), tt.want) {
|
||||
if !reflect.DeepEqual(wg.OverlayAddr.IP.String(), tt.want) {
|
||||
t.Errorf("assignOverlayAddr() set = %s, want %s", wg.OverlayAddr, tt.want)
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue