use wgctrl lib instead of wrapping wg-tools

fixes: #3
This commit is contained in:
Leo Antunes 2019-07-12 11:08:14 +02:00
parent 3559fcf3b3
commit f4e61df211
10 changed files with 166 additions and 149 deletions

View File

@ -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 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. 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. 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.

View File

@ -107,7 +107,7 @@ func (c *cluster) LocalState(join bool) []byte { return nil }
func (c *cluster) MergeRemoteState(buf []byte, join bool) {} func (c *cluster) MergeRemoteState(buf []byte, join bool) {}
type nodeMeta struct { type nodeMeta struct {
OverlayAddr net.IP OverlayAddr net.IPNet
PubKey string PubKey string
} }
@ -115,7 +115,7 @@ func (c *cluster) NodeMeta(limit int) []byte {
buf := &bytes.Buffer{} buf := &bytes.Buffer{}
if err := gob.NewEncoder(buf).Encode(nodeMeta{ if err := gob.NewEncoder(buf).Encode(nodeMeta{
OverlayAddr: c.wg.OverlayAddr, OverlayAddr: c.wg.OverlayAddr,
PubKey: c.wg.PubKey, PubKey: c.wg.PubKey.String(),
}); err != nil { }); err != nil {
logrus.Errorf("could not encode local state: %s", err) logrus.Errorf("could not encode local state: %s", err)
return nil return nil
@ -128,6 +128,8 @@ func (c *cluster) NodeMeta(limit int) []byte {
} }
func decodeNodeMeta(b []byte) (nodeMeta, error) { 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{} nm := nodeMeta{}
if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&nm); err != nil { if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&nm); err != nil {
return nm, errwrap.Wrapf("could not decode: {{err}}", err) return nm, errwrap.Wrapf("could not decode: {{err}}", err)

5
go.mod
View File

@ -6,6 +6,11 @@ require (
github.com/hashicorp/go-sockaddr v1.0.0 github.com/hashicorp/go-sockaddr v1.0.0
github.com/hashicorp/memberlist v0.1.3 github.com/hashicorp/memberlist v0.1.3
github.com/mattn/go-isatty v0.0.7 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/sirupsen/logrus v1.3.0
github.com/stevenroose/gonfig v0.1.4 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
View File

@ -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/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 h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 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= 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/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 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 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 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 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 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 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 h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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= 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 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-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 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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 h1:x6rhz8Y9CjbgQkccRGmELH6K+LJj7tOoh3XWeC1yaQM=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= 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-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 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-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 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=

10
main.go
View File

@ -53,13 +53,7 @@ func main() {
for _, node := range nodes { for _, node := range nodes {
logrus.Infof("\taddr: %s, overlay: %s, pubkey: %s", node.Addr, node.OverlayAddr, node.PubKey) logrus.Infof("\taddr: %s, overlay: %s, pubkey: %s", node.Addr, node.OverlayAddr, node.PubKey)
} }
if err := wg.downInterface(); err != nil { if err := wg.setUpInterface(nodes); 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 {
logrus.Errorf("could not up interface: %s", err) logrus.Errorf("could not up interface: %s", err)
} }
if !config.NoEtcHosts { if !config.NoEtcHosts {
@ -88,7 +82,7 @@ func main() {
func writeToEtcHosts(nodes []node) error { func writeToEtcHosts(nodes []node) error {
hosts := make(map[string][]string, len(nodes)) hosts := make(map[string][]string, len(nodes))
for _, n := range nodes { for _, n := range nodes {
hosts[n.OverlayAddr.String()] = []string{n.Name} hosts[n.OverlayAddr.IP.String()] = []string{n.Name}
} }
hostsFile := &etchosts.EtcHosts{ hostsFile := &etchosts.EtcHosts{
Logger: logrus.StandardLogger(), Logger: logrus.StandardLogger(),

18
netlink.go Normal file
View File

@ -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"
}

View File

@ -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

View File

@ -1,14 +0,0 @@
#!/bin/sh
case $1 in
up)
echo "up $2"
;;
pubkey)
echo "down $2"
;;
*)
echo "what?"
exit 1
;;
esac

View File

@ -1,53 +1,40 @@
package main package main
import ( import (
"fmt"
"hash/fnv" "hash/fnv"
"net" "net"
"os" "os"
"os/exec"
"strings" "github.com/pkg/errors"
"text/template" "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 { type wgState struct {
iface string iface string
OverlayAddr net.IP client *wgctrl.Client
OverlayAddr net.IPNet
Port int Port int
PrivKey string PrivKey wgtypes.Key
PubKey string PubKey wgtypes.Key
} }
var wgPath = "wg"
var wgQuickPath = "wg-quick"
func newWGConfig(iface string, port int) (*wgState, error) { func newWGConfig(iface string, port int) (*wgState, error) {
if err := exec.Command(wgPath).Run(); err != nil { client, err := wgctrl.New()
return nil, fmt.Errorf("could not exec wireguard: %s", err) if err != nil {
return nil, errors.Wrap(err, "could not instantiate wireguard client")
} }
privKey, pubKey, err := wgKeyPair() privKey, err := wgtypes.GeneratePrivateKey()
if err != nil { if err != nil {
return nil, err return nil, err
} }
pubKey := privKey.PublicKey()
wgState := wgState{ wgState := wgState{
iface: iface, iface: iface,
client: client,
Port: port, Port: port,
PrivKey: privKey, PrivKey: privKey,
PubKey: pubKey, PubKey: pubKey,
@ -69,51 +56,93 @@ func (wg *wgState) assignOverlayAddr(ipnet *net.IPNet, name string) {
ip[len(ip)-i] = hb[len(hb)-i] 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 { func (wg *wgState) downInterface() error {
if err := exec.Command(wgPath, "show", wg.iface).Run(); err != nil { if _, err := wg.client.Device(wg.iface); err != nil {
return nil // assume a failure means the interface is not there 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 { func (wg *wgState) setUpInterface(nodes []node) error {
return exec.Command(wgQuickPath, "up", wg.iface).Run() if err := wg.createWgInterface(); err != nil {
return err
} }
func wgKeyPair() (string, string, error) { peerCfgs, err := wg.nodesToPeerConfigs(nodes)
cmd := exec.Command(wgPath, "genkey") if err != nil {
outPriv := strings.Builder{} return errors.Wrap(err, "error converting received node information to wireguard format")
cmd.Stdout = &outPriv }
if err := cmd.Run(); err != nil { wg.client.ConfigureDevice(wg.iface, wgtypes.Config{
return "", "", err 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") return nil
outPub := strings.Builder{}
cmd.Stdout = &outPub
cmd.Stdin = strings.NewReader(outPriv.String())
if err := cmd.Run(); err != nil {
return "", "", err
} }
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
} }

View File

@ -6,38 +6,6 @@ import (
"testing" "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) { func Test_wgState_assignOverlayAddr(t *testing.T) {
type args struct { type args struct {
ipnet *net.IPNet ipnet *net.IPNet
@ -66,7 +34,7 @@ func Test_wgState_assignOverlayAddr(t *testing.T) {
wg := &wgState{} wg := &wgState{}
wg.assignOverlayAddr(tt.args.ipnet, tt.args.name) 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) t.Errorf("assignOverlayAddr() set = %s, want %s", wg.OverlayAddr, tt.want)
} }
}) })