constellation/coordinator/wireguard/wireguard.go

145 lines
3.1 KiB
Go
Raw Normal View History

package wireguard
import (
"errors"
"fmt"
"net"
"os"
"time"
"github.com/edgelesssys/constellation/coordinator/util"
"github.com/vishvananda/netlink"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
netInterface = "wg0"
port = 51820
)
type Wireguard struct{}
func New() *Wireguard {
return &Wireguard{}
}
func (w *Wireguard) Setup(privKey []byte) ([]byte, error) {
client, err := wgctrl.New()
if err != nil {
return nil, fmt.Errorf("failed to open wgctrl: %w", err)
}
defer client.Close()
var key wgtypes.Key
if len(privKey) == 0 {
key, err = wgtypes.GeneratePrivateKey()
} else {
key, err = wgtypes.NewKey(privKey)
}
if err != nil {
return nil, err
}
listenPort := port
if err := client.ConfigureDevice(netInterface, wgtypes.Config{PrivateKey: &key, ListenPort: &listenPort}); err != nil {
return nil, prettyWgError(err)
}
return key[:], nil
}
func (w *Wireguard) GetPublicKey(privKey []byte) ([]byte, error) {
key, err := wgtypes.NewKey(privKey)
if err != nil {
return nil, err
}
pubkey := key.PublicKey()
return pubkey[:], nil
}
func (w *Wireguard) GetInterfaceIP() (string, error) {
return util.GetInterfaceIP(netInterface)
}
// SetInterfaceIP sets the ip interface ip.
func (w *Wireguard) SetInterfaceIP(ip string) error {
addr, err := netlink.ParseAddr(ip + "/16")
if err != nil {
return err
}
link, err := netlink.LinkByName(netInterface)
if err != nil {
return err
}
if err := netlink.AddrAdd(link, addr); err != nil {
return err
}
return netlink.LinkSetUp(link)
}
// AddPeer adds a new peer to a wireguard interface.
func (w *Wireguard) AddPeer(pubKey []byte, publicIP string, vpnIP string) error {
client, err := wgctrl.New()
if err != nil {
return fmt.Errorf("failed to open wgctrl: %w", err)
}
defer client.Close()
_, allowedIPs, err := net.ParseCIDR(vpnIP + "/32")
if err != nil {
return err
}
key, err := wgtypes.NewKey(pubKey)
if err != nil {
return err
}
var endpoint *net.UDPAddr
if ip := net.ParseIP(publicIP); ip != nil {
endpoint = &net.UDPAddr{IP: ip, Port: port}
}
keepAlive := 10 * time.Second
cfg := wgtypes.Config{
ReplacePeers: false,
Peers: []wgtypes.PeerConfig{
{
PublicKey: key,
UpdateOnly: false,
Endpoint: endpoint,
AllowedIPs: []net.IPNet{*allowedIPs},
PersistentKeepaliveInterval: &keepAlive,
},
},
}
return prettyWgError(client.ConfigureDevice(netInterface, cfg))
}
// RemovePeer removes a peer from the wireguard interface.
func (w *Wireguard) RemovePeer(pubKey []byte) error {
client, err := wgctrl.New()
if err != nil {
return fmt.Errorf("failed to open wgctrl: %w", err)
}
defer client.Close()
key, err := wgtypes.NewKey(pubKey)
if err != nil {
return err
}
cfg := wgtypes.Config{Peers: []wgtypes.PeerConfig{{PublicKey: key, Remove: true}}}
return prettyWgError(client.ConfigureDevice(netInterface, cfg))
}
func prettyWgError(err error) error {
if errors.Is(err, os.ErrNotExist) {
return errors.New("interface not found or is not a WireGuard interface")
}
return err
}