wesher/wireguard.go

150 lines
3.4 KiB
Go
Raw Normal View History

2019-03-25 01:02:10 +01:00
package main
import (
"hash/fnv"
2019-03-25 01:02:10 +01:00
"net"
"os"
"github.com/pkg/errors"
"github.com/vishvananda/netlink"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
2019-03-25 01:02:10 +01:00
type wgState struct {
iface string
client *wgctrl.Client
OverlayAddr net.IPNet
2019-03-25 01:02:10 +01:00
Port int
PrivKey wgtypes.Key
PubKey wgtypes.Key
2019-03-25 01:02:10 +01:00
}
func newWGConfig(iface string, port int) (*wgState, error) {
client, err := wgctrl.New()
if err != nil {
return nil, errors.Wrap(err, "could not instantiate wireguard client")
2019-03-25 01:02:10 +01:00
}
privKey, err := wgtypes.GeneratePrivateKey()
2019-03-25 01:02:10 +01:00
if err != nil {
return nil, err
}
pubKey := privKey.PublicKey()
2019-03-25 01:02:10 +01:00
wgState := wgState{
iface: iface,
client: client,
2019-03-25 01:02:10 +01:00
Port: port,
PrivKey: privKey,
PubKey: pubKey,
}
return &wgState, nil
}
2019-03-27 21:37:54 +01:00
func (wg *wgState) assignOverlayAddr(ipnet *net.IPNet, name string) {
2019-03-25 01:02:10 +01:00
// TODO: this is way too brittle and opaque
bits, size := ipnet.Mask.Size()
2019-03-29 22:15:17 +01:00
ip := make([]byte, len(ipnet.IP))
copy(ip, []byte(ipnet.IP))
2019-03-25 01:02:10 +01:00
h := fnv.New128a()
2019-03-25 01:02:10 +01:00
h.Write([]byte(name))
hb := h.Sum(nil)
for i := 1; i <= (size-bits)/8; i++ {
ip[len(ip)-i] = hb[len(hb)-i]
2019-03-25 01:02:10 +01:00
}
wg.OverlayAddr = net.IPNet{
IP: net.IP(ip),
Mask: net.CIDRMask(size, size), // either /32 or /128, depending if ipv4 or ipv6
}
2019-03-25 01:02:10 +01:00
}
func (wg *wgState) downInterface() error {
if _, err := wg.client.Device(wg.iface); err != nil {
if os.IsNotExist(err) {
return nil // device already gone; noop
}
return err
}
link, err := netlink.LinkByName(wg.iface)
2019-03-25 01:02:10 +01:00
if err != nil {
return err
}
return netlink.LinkDel(link)
2019-03-25 01:02:10 +01:00
}
func (wg *wgState) setUpInterface(nodes []node) error {
if err := wg.createWgInterface(); 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,
})
2019-03-27 22:48:54 +01:00
}
2019-03-25 01:02:10 +01:00
return nil
2019-03-25 01:02:10 +01:00
}
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,
},
}
2019-03-25 01:02:10 +01:00
}
return peerCfgs, nil
}
2019-03-25 01:02:10 +01:00
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
// see tests/entrypoint.sh
if _, e2e := os.LookupEnv("WESHER_E2E_TESTS"); e2e {
return nil
}
2019-03-25 01:02:10 +01:00
}
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
2019-03-25 01:02:10 +01:00
}