2019-03-25 01:02:10 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2019-03-28 20:27:08 +01:00
|
|
|
"hash/fnv"
|
2019-03-25 01:02:10 +01:00
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
|
|
|
)
|
|
|
|
|
|
|
|
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
|
|
|
|
Port int
|
|
|
|
PrivKey string
|
|
|
|
PubKey string
|
|
|
|
}
|
|
|
|
|
2019-03-28 20:27:08 +01:00
|
|
|
var wgPath = "wg"
|
|
|
|
var wgQuickPath = "wg-quick"
|
|
|
|
|
2019-03-25 01:02:10 +01:00
|
|
|
func newWGConfig(iface string, port int) (*wgState, error) {
|
2019-03-28 20:27:08 +01:00
|
|
|
if err := exec.Command(wgPath).Run(); err != nil {
|
2019-03-25 01:02:10 +01:00
|
|
|
return nil, fmt.Errorf("could not exec wireguard: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
privKey, pubKey, err := wgKeyPair()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
wgState := wgState{
|
|
|
|
iface: iface,
|
|
|
|
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-28 20:27:08 +01:00
|
|
|
ip := make([]byte, net.IPv6len)
|
|
|
|
copy(ip, []byte(ipnet.IP))
|
2019-03-25 01:02:10 +01:00
|
|
|
|
2019-03-28 20:27:08 +01:00
|
|
|
h := fnv.New128a()
|
2019-03-25 01:02:10 +01:00
|
|
|
h.Write([]byte(name))
|
|
|
|
hb := h.Sum(nil)
|
|
|
|
|
2019-03-28 20:27:08 +01:00
|
|
|
for i := 1; i <= (size-bits)/8; i++ {
|
|
|
|
ip[len(ip)-i] = hb[len(hb)-i]
|
2019-03-25 01:02:10 +01:00
|
|
|
}
|
2019-03-28 20:27:08 +01:00
|
|
|
|
2019-03-25 01:02:10 +01:00
|
|
|
wg.OverlayAddr = net.IP(ip)
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-03-28 20:27:08 +01:00
|
|
|
if err := exec.Command(wgPath, "show", wg.iface).Run(); err != nil {
|
2019-03-27 22:48:54 +01:00
|
|
|
return nil // assume a failure means the interface is not there
|
|
|
|
}
|
2019-03-28 20:27:08 +01:00
|
|
|
return exec.Command(wgQuickPath, "down", wg.iface).Run()
|
2019-03-25 01:02:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (wg *wgState) upInterface() error {
|
2019-03-28 20:27:08 +01:00
|
|
|
return exec.Command(wgQuickPath, "up", wg.iface).Run()
|
2019-03-25 01:02:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func wgKeyPair() (string, string, error) {
|
2019-03-28 20:27:08 +01:00
|
|
|
cmd := exec.Command(wgPath, "genkey")
|
2019-03-25 01:02:10 +01:00
|
|
|
outPriv := strings.Builder{}
|
|
|
|
cmd.Stdout = &outPriv
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
return "", "", err
|
|
|
|
}
|
|
|
|
|
2019-03-28 20:27:08 +01:00
|
|
|
cmd = exec.Command(wgPath, "pubkey")
|
2019-03-25 01:02:10 +01:00
|
|
|
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
|
|
|
|
}
|