| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321 | package wgimport (	"errors"	"fmt"	"log"	"net"	"os"	"os/exec"	"strings"	"sync"	"time"	"golang.zx2c4.com/wireguard/wgctrl"	"golang.zx2c4.com/wireguard/wgctrl/wgtypes")const (	DefaultMTU         = 1280	DefaultWgPort      = 51820	DefaultWgKeepAlive = 20 * time.Second)// WGIface represents a interface instancetype WGIface struct {	Name      string	Port      int	MTU       int	Device    *wgtypes.Device	Address   WGAddress	Interface NetInterface	mu        sync.Mutex}// NetInterface represents a generic network tunnel interfacetype NetInterface interface {	Close() error}// WGAddress Wireguard parsed addresstype WGAddress struct {	IP      net.IP	Network *net.IPNet}// NewWGIFace Creates a new Wireguard interface instancefunc NewWGIFace(iface string, address string, mtu int) (*WGIface, error) {	wgIface := &WGIface{		Name: iface,		MTU:  mtu,		mu:   sync.Mutex{},	}	wgAddress, err := parseAddress(address)	if err != nil {		return wgIface, err	}	wgIface.Address = wgAddress	err = wgIface.GetWgIface(iface)	if err != nil {		return nil, err	}	return wgIface, nil}func (w *WGIface) GetWgIface(iface string) error {	wgClient, err := wgctrl.New()	if err != nil {		return err	}	dev, err := wgClient.Device(iface)	if err != nil {		return err	}	//log.Printf("----> DEVICE: %+v\n", dev)	w.Device = dev	w.Port = dev.ListenPort	return nil}func GetWgIfacePubKey(iface string) [32]byte {	wgClient, err := wgctrl.New()	if err != nil {		log.Println("Error fetching pub key: ", iface, err)		return [32]byte{}	}	dev, err := wgClient.Device(iface)	if err != nil {		log.Println("Error fetching pub key: ", iface, err)		return [32]byte{}	}	return dev.PublicKey}func GetWgIfacePrivKey(iface string) [32]byte {	wgClient, err := wgctrl.New()	if err != nil {		log.Println("Error fetching pub key: ", iface, err)		return [32]byte{}	}	dev, err := wgClient.Device(iface)	if err != nil {		log.Println("Error fetching pub key: ", iface, err)		return [32]byte{}	}	return dev.PrivateKey}// parseAddress parse a string ("1.2.3.4/24") address to WG Addressfunc parseAddress(address string) (WGAddress, error) {	ip, network, err := net.ParseCIDR(address)	if err != nil {		return WGAddress{}, err	}	return WGAddress{		IP:      ip,		Network: network,	}, nil}// UpdatePeer updates existing Wireguard Peer or creates a new one if doesn't existfunc (w *WGIface) UpdatePeer(peerKey string, allowedIps []net.IPNet, keepAlive time.Duration, endpoint *net.UDPAddr, preSharedKey *wgtypes.Key) error {	w.mu.Lock()	defer w.mu.Unlock()	log.Printf("updating interface %s peer %s: endpoint %s ", w.Name, peerKey, endpoint)	// //parse allowed ips	// _, ipNet, err := net.ParseCIDR(allowedIps)	// if err != nil {	// 	return err	// }	peerKeyParsed, err := wgtypes.ParseKey(peerKey)	if err != nil {		return err	}	peer := wgtypes.PeerConfig{		PublicKey:                   peerKeyParsed,		ReplaceAllowedIPs:           true,		AllowedIPs:                  allowedIps,		PersistentKeepaliveInterval: &keepAlive,		PresharedKey:                preSharedKey,		Endpoint:                    endpoint,	}	config := wgtypes.Config{		Peers: []wgtypes.PeerConfig{peer},	}	err = w.configureDevice(config)	if err != nil {		return fmt.Errorf("received error \"%v\" while updating peer on interface %s with settings: allowed ips %s, endpoint %s", err, w.Name, allowedIps, endpoint.String())	}	return nil}// configureDevice configures the wireguard devicefunc (w *WGIface) configureDevice(config wgtypes.Config) error {	wg, err := wgctrl.New()	if err != nil {		return err	}	defer wg.Close()	// validate if device with name exists	_, err = wg.Device(w.Name)	if err != nil {		return err	}	log.Printf("got Wireguard device %s\n", w.Name)	return wg.ConfigureDevice(w.Name, config)}// GetListenPort returns the listening port of the Wireguard endpointfunc (w *WGIface) GetListenPort() (*int, error) {	log.Printf("getting Wireguard listen port of interface %s", w.Name)	//discover Wireguard current configuration	wg, err := wgctrl.New()	if err != nil {		return nil, err	}	defer wg.Close()	d, err := wg.Device(w.Name)	if err != nil {		return nil, err	}	log.Printf("got Wireguard device listen port %s, %d", w.Name, d.ListenPort)	return &d.ListenPort, nil}// GetRealIface - retrieves tun iface based on reference iface name from config filefunc GetRealIface(iface string) (string, error) {	RunCmd("wg show interfaces", false)	ifacePath := "/var/run/wireguard/" + iface + ".name"	if !(FileExists(ifacePath)) {		return "", errors.New(ifacePath + " does not exist")	}	realIfaceName, err := GetFileAsString(ifacePath)	if err != nil {		return "", err	}	realIfaceName = strings.TrimSpace(realIfaceName)	if !(FileExists(fmt.Sprintf("/var/run/wireguard/%s.sock", realIfaceName))) {		return "", errors.New("interface file does not exist")	}	return realIfaceName, nil}// FileExists - checks if file exists locallyfunc FileExists(f string) bool {	info, err := os.Stat(f)	if os.IsNotExist(err) {		return false	}	if err != nil && strings.Contains(err.Error(), "not a directory") {		return false	}	if err != nil {		log.Println(0, "error reading file: "+f+", "+err.Error())	}	return !info.IsDir()}// GetFileAsString - returns the string contents of a given filefunc GetFileAsString(path string) (string, error) {	content, err := os.ReadFile(path)	if err != nil {		return "", err	}	return string(content), err}// RunCmd - runs a local commandfunc RunCmd(command string, printerr bool) (string, error) {	args := strings.Fields(command)	cmd := exec.Command(args[0], args[1:]...)	cmd.Wait()	out, err := cmd.CombinedOutput()	if err != nil && printerr {		log.Println("error running command: ", command)		log.Println(strings.TrimSuffix(string(out), "\n"))	}	return string(out), err}// RemovePeer removes a Wireguard Peer from the interface ifacefunc (w *WGIface) RemovePeer(peerKey string) error {	w.mu.Lock()	defer w.mu.Unlock()	log.Printf("Removing peer %s from interface %s ", peerKey, w.Name)	peerKeyParsed, err := wgtypes.ParseKey(peerKey)	if err != nil {		return err	}	peer := wgtypes.PeerConfig{		PublicKey: peerKeyParsed,		Remove:    true,	}	config := wgtypes.Config{		Peers: []wgtypes.PeerConfig{peer},	}	err = w.configureDevice(config)	if err != nil {		return fmt.Errorf("received error \"%v\" while removing peer %s from interface %s", err, peerKey, w.Name)	}	return nil}// UpdatePeerfunc (w *WGIface) Update(peerConf wgtypes.PeerConfig, updateOnly bool) error {	w.mu.Lock()	defer w.mu.Unlock()	var err error	log.Printf("---------> NEWWWWWW Updating peer %+v from interface %s ", peerConf, w.Name)	peerConf.UpdateOnly = updateOnly	peerConf.ReplaceAllowedIPs = true	config := wgtypes.Config{		Peers: []wgtypes.PeerConfig{peerConf},	}	err = w.configureDevice(config)	if err != nil {		return fmt.Errorf("received error \"%v\" while Updating peer %s from interface %s", err, peerConf.PublicKey.String(), w.Name)	}	return nil}func GetPeer(ifaceName, peerPubKey string) (wgtypes.Peer, error) {	wg, err := wgctrl.New()	if err != nil {		return wgtypes.Peer{}, err	}	defer func() {		err = wg.Close()		if err != nil {			log.Printf("got error while closing wgctl: %v", err)		}	}()	wgDevice, err := wg.Device(ifaceName)	if err != nil {		return wgtypes.Peer{}, err	}	for _, peer := range wgDevice.Peers {		if peer.PublicKey.String() == peerPubKey {			return peer, nil		}	}	return wgtypes.Peer{}, fmt.Errorf("peer not found")}
 |