proxy integration with netclient inital commit

This commit is contained in:
Abhishek Kondur 2022-10-27 15:20:29 +05:30
parent aabb573a19
commit 953b5eae0a
10 changed files with 945 additions and 0 deletions

Binary file not shown.

View file

@ -0,0 +1,146 @@
package common
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/exec"
"strings"
"time"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/netclient-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
NmProxyPort = 51722
)
type Conn struct {
Config ConnConfig
Proxy Proxy
}
// ConnConfig is a peer Connection configuration
type ConnConfig struct {
// Key is a public key of a remote peer
Key string
// LocalKey is a public key of a local peer
LocalKey string
ProxyConfig Config
AllowedIPs []net.IPNet
LocalWgPort int
RemoteProxyIP net.IP
RemoteWgPort int
RemoteProxyPort int
}
type Config struct {
Port int
BodySize int
Addr string
WgListenAddr string
RemoteKey string
WgInterface *wg.WGIface
AllowedIps []net.IPNet
PreSharedKey *wgtypes.Key
//ProxyServer *server.ProxyServer
}
// Proxy - WireguardProxy proxies
type Proxy struct {
Ctx context.Context
Cancel context.CancelFunc
Config Config
RemoteConn net.Conn
LocalConn net.Conn
}
var Peers = make(map[string]*Conn)
var RemoteEndpointsMap = make(map[string][]string)
// RunCmd - runs a local command
func 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
}
// API function to interact with netmaker api endpoints. response from endpoint is returned
func API(data interface{}, method, url, authorization string) (*http.Response, error) {
var request *http.Request
var err error
if data != "" {
payload, err := json.Marshal(data)
if err != nil {
return nil, fmt.Errorf("error encoding data %w", err)
}
request, err = http.NewRequest(method, url, bytes.NewBuffer(payload))
if err != nil {
return nil, fmt.Errorf("error creating http request %w", err)
}
request.Header.Set("Content-Type", "application/json")
} else {
request, err = http.NewRequest(method, url, nil)
if err != nil {
return nil, fmt.Errorf("error creating http request %w", err)
}
}
if authorization != "" {
request.Header.Set("authorization", "Bearer "+authorization)
}
request.Header.Set("requestfrom", "node")
var httpClient http.Client
httpClient.Timeout = time.Minute
return httpClient.Do(request)
}
// Authenticate authenticates with api to permit subsequent interactions with the api
func Authenticate(cfg *config.ClientConfig) (string, error) {
pass, err := os.ReadFile(ncutils.GetNetclientPathSpecific() + "secret-" + cfg.Network)
if err != nil {
return "", fmt.Errorf("could not read secrets file %w", err)
}
data := models.AuthParams{
MacAddress: cfg.Node.MacAddress,
ID: cfg.Node.ID,
Password: string(pass),
}
url := "https://" + cfg.Server.API + "/api/nodes/adm/" + cfg.Network + "/authenticate"
response, err := API(data, http.MethodPost, url, "")
if err != nil {
return "", err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
bodybytes, _ := io.ReadAll(response.Body)
return "", fmt.Errorf("failed to authenticate %s %s", response.Status, string(bodybytes))
}
resp := models.SuccessResponse{}
if err := json.NewDecoder(response.Body).Decode(&resp); err != nil {
return "", fmt.Errorf("error decoding respone %w", err)
}
tokenData := resp.Response.(map[string]interface{})
token := tokenData["AuthToken"]
return token.(string), nil
}

View file

@ -0,0 +1,78 @@
package netclientproxy
import (
"fmt"
"log"
"net"
"runtime"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/ncutils"
"github.com/gravitl/netmaker/netclient/netclient-proxy/common"
peerpkg "github.com/gravitl/netmaker/netclient/netclient-proxy/peer"
"github.com/gravitl/netmaker/netclient/netclient-proxy/server"
"github.com/gravitl/netmaker/netclient/netclient-proxy/stun"
"github.com/gravitl/netmaker/netclient/netclient-proxy/wg"
"github.com/gravitl/netmaker/netclient/wireguard"
)
func Start() {
log.Println("Starting Proxy...")
hInfo := stun.GetHostInfo()
stun.Host = hInfo
log.Printf("HOSTINFO: %+v", hInfo)
// start the netclient proxy server
p, err := server.CreateProxyServer(0, 0, hInfo.PrivIp.String())
if err != nil {
log.Fatal("failed to create proxy: ", err)
}
go p.Listen()
networks, _ := ncutils.GetSystemNetworks()
for _, network := range networks {
logger.Log(3, "initializing network", network)
cfg := config.ClientConfig{}
cfg.Network = network
cfg.ReadConfig()
node, err := peerpkg.GetNodeInfo(&cfg)
if err != nil {
log.Println("Failed to get node info: ", err)
continue
}
for _, peerI := range node.Peers {
ifaceName := node.Node.Interface
log.Println("--------> IFACE: ", ifaceName)
if runtime.GOOS == "darwin" {
ifaceName, err = wireguard.GetRealIface(ifaceName)
if err != nil {
log.Println("failed to get real iface: ", err)
}
}
wgInterface, err := wg.NewWGIFace(ifaceName, "127.0.0.1/32", wg.DefaultMTU)
if err != nil {
log.Fatal("Failed init new interface: ", err)
}
log.Printf("wg: %+v\n", wgInterface)
peerpkg.AddNewPeer(p, wgInterface, &peerI)
if val, ok := common.RemoteEndpointsMap[peerI.Endpoint.IP.String()]; ok {
val = append(val, peerI.PublicKey.String())
common.RemoteEndpointsMap[peerI.Endpoint.IP.String()] = val
} else {
common.RemoteEndpointsMap[peerI.Endpoint.IP.String()] = []string{peerI.PublicKey.String()}
}
}
}
fmt.Printf("\nPEERS-------> %+v\n", common.Peers)
fmt.Printf("\nREMOTE ENDPOINTS-------> %+v\n", common.RemoteEndpointsMap)
select {}
}
// IsPublicIP indicates whether IP is public or not.
func IsPublicIP(ip net.IP) bool {
if ip.IsLoopback() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast() || ip.IsPrivate() {
return false
}
return true
}

View file

@ -0,0 +1,39 @@
package packet
import (
"bytes"
"encoding/binary"
"log"
)
var udpHeaderLen = 8
func ProcessPacketBeforeSending(buf []byte, n, dstPort int) ([]byte, int, error) {
log.Println("@###### DST Port: ", dstPort)
portbuf := new(bytes.Buffer)
binary.Write(portbuf, binary.BigEndian, uint16(dstPort))
if n > len(buf)-2 {
buf = append(buf, portbuf.Bytes()[0])
buf = append(buf, portbuf.Bytes()[1])
} else {
buf[n] = portbuf.Bytes()[0]
buf[n+1] = portbuf.Bytes()[1]
}
n += 2
return buf, n, nil
}
func ExtractInfo(buffer []byte, n int) (int, int, error) {
data := buffer[:n]
var localWgPort uint16
portBuf := data[n-2 : n+1]
reader := bytes.NewReader(portBuf)
err := binary.Read(reader, binary.BigEndian, &localWgPort)
if err != nil {
log.Println("Failed to read port buffer: ", err)
}
n -= 2
return int(localWgPort), n, err
}

View file

@ -0,0 +1,116 @@
package peer
import (
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/netclient-proxy/common"
"github.com/gravitl/netmaker/netclient/netclient-proxy/proxy"
"github.com/gravitl/netmaker/netclient/netclient-proxy/server"
"github.com/gravitl/netmaker/netclient/netclient-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
type Conn struct {
Config ConnConfig
Proxy proxy.Proxy
}
// ConnConfig is a peer Connection configuration
type ConnConfig struct {
// Key is a public key of a remote peer
Key string
// LocalKey is a public key of a local peer
LocalKey string
ProxyConfig proxy.Config
AllowedIPs string
LocalWgPort int
RemoteProxyIP net.IP
RemoteWgPort int
RemoteProxyPort int
}
func GetNodeInfo(cfg *config.ClientConfig) (models.NodeGet, error) {
var nodeGET models.NodeGet
token, err := common.Authenticate(cfg)
if err != nil {
return nodeGET, err
}
url := fmt.Sprintf("https://%s/api/nodes/%s/%s", cfg.Server.API, cfg.Network, cfg.Node.ID)
response, err := common.API("", http.MethodGet, url, token)
if err != nil {
return nodeGET, err
}
if response.StatusCode != http.StatusOK {
bytes, err := io.ReadAll(response.Body)
if err != nil {
fmt.Println(err)
}
return nodeGET, (fmt.Errorf("%s %w", string(bytes), err))
}
defer response.Body.Close()
if err := json.NewDecoder(response.Body).Decode(&nodeGET); err != nil {
return nodeGET, fmt.Errorf("error decoding node %w", err)
}
return nodeGET, nil
}
func AddNewPeer(pserver *server.ProxyServer, wgInterface *wg.WGIface, peer *wgtypes.PeerConfig) error {
c := proxy.Config{
Port: peer.Endpoint.Port,
WgListenAddr: "127.0.0.1",
RemoteKey: peer.PublicKey.String(),
WgInterface: wgInterface,
AllowedIps: peer.AllowedIPs,
ProxyServer: pserver,
}
p := proxy.NewProxy(c)
remoteConn, err := net.Dial("udp", fmt.Sprintf("%s:%d", peer.Endpoint.IP.String(), common.NmProxyPort))
if err != nil {
return err
}
log.Printf("Starting proxy for Peer: %s\n", peer.PublicKey.String())
err = p.Start(remoteConn)
if err != nil {
return err
}
log.Println("-------> Here1")
connConf := common.ConnConfig{
Key: peer.PublicKey.String(),
LocalKey: "",
ProxyConfig: common.Config{
Port: peer.Endpoint.Port,
WgListenAddr: "127.0.0.1",
RemoteKey: peer.PublicKey.String(),
WgInterface: wgInterface,
AllowedIps: peer.AllowedIPs,
//ProxyServer: pserver,
},
AllowedIPs: peer.AllowedIPs,
RemoteProxyIP: net.ParseIP(peer.Endpoint.IP.String()),
RemoteWgPort: peer.Endpoint.Port,
RemoteProxyPort: common.NmProxyPort,
}
peerProxy := common.Proxy{
Ctx: p.Ctx,
Cancel: p.Cancel,
Config: connConf.ProxyConfig,
RemoteConn: remoteConn,
LocalConn: p.LocalConn,
}
peerConn := common.Conn{
Config: connConf,
Proxy: peerProxy,
}
common.Peers[peer.PublicKey.String()] = &peerConn
return nil
}

View file

@ -0,0 +1,37 @@
package proxy
import (
"context"
"net"
"github.com/gravitl/netmaker/netclient/netclient-proxy/server"
"github.com/gravitl/netmaker/netclient/netclient-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
defaultBodySize = 10000
defaultPort = 51722
)
type Config struct {
Port int
BodySize int
Addr string
WgListenAddr string
RemoteKey string
WgInterface *wg.WGIface
AllowedIps []net.IPNet
PreSharedKey *wgtypes.Key
ProxyServer *server.ProxyServer
}
// Proxy - WireguardProxy proxies
type Proxy struct {
Ctx context.Context
Cancel context.CancelFunc
Config Config
RemoteConn net.Conn
LocalConn net.Conn
}

View file

@ -0,0 +1,195 @@
package proxy
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"log"
"net"
"runtime"
"strconv"
"github.com/c-robinson/iplib"
"github.com/gravitl/netmaker/netclient/netclient-proxy/common"
"github.com/gravitl/netmaker/netclient/netclient-proxy/packet"
"github.com/gravitl/netmaker/netclient/netclient-proxy/wg"
)
func NewProxy(config Config) *Proxy {
p := &Proxy{Config: config}
p.Ctx, p.Cancel = context.WithCancel(context.Background())
return p
}
// proxyToRemote proxies everything from Wireguard to the RemoteKey peer
func (p *Proxy) ProxyToRemote() {
buf := make([]byte, 1500)
for {
select {
case <-p.Ctx.Done():
log.Printf("stopped proxying to remote peer %s due to closed connection\n", p.Config.RemoteKey)
return
default:
n, err := p.LocalConn.Read(buf)
if err != nil {
log.Println("ERRR READ: ", err)
continue
}
if peerI, ok := common.Peers[p.Config.RemoteKey]; ok {
log.Println("PROCESSING PKT BEFORE SENDING")
buf, n, err = packet.ProcessPacketBeforeSending(buf, n, peerI.Config.RemoteWgPort)
if err != nil {
log.Println("failed to process pkt before sending: ", err)
}
} else {
log.Printf("Peer: %s not found in config\n", p.Config.RemoteKey)
}
// test(n, buf)
log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s\n", p.Config.ProxyServer.Server.LocalAddr().String(), p.RemoteConn.RemoteAddr().String())
host, port, _ := net.SplitHostPort(p.RemoteConn.RemoteAddr().String())
portInt, _ := strconv.Atoi(port)
_, err = p.Config.ProxyServer.Server.WriteToUDP(buf[:n], &net.UDPAddr{
IP: net.ParseIP(host),
Port: portInt,
})
if err != nil {
log.Println("Failed to send to remote: ", err)
}
}
}
}
func test(n int, data []byte) {
var localWgPort uint16
//portBuf := data[n-2 : n+1]
portBuf := data[2:4]
reader := bytes.NewReader(portBuf)
err := binary.Read(reader, binary.BigEndian, &localWgPort)
if err != nil {
log.Println("Failed to read port buffer: ", err)
}
log.Println("TEST WFGPO: ", localWgPort)
}
// proxyToLocal proxies everything from the RemoteKey peer to local Wireguard
func (p *Proxy) ProxyToLocal() {
buf := make([]byte, 1500)
for {
select {
case <-p.Ctx.Done():
log.Printf("stopped proxying from remote peer %s due to closed connection\n", p.Config.RemoteKey)
return
default:
n, err := p.RemoteConn.Read(buf)
if err != nil {
continue
}
log.Printf("PROXING TO LOCAL!!!---> %s <<<<<<<< %s\n", p.LocalConn.LocalAddr().String(), p.RemoteConn.RemoteAddr().String())
_, err = p.LocalConn.Write(buf[:n])
if err != nil {
continue
}
}
}
}
func (p *Proxy) updateEndpoint() error {
udpAddr, err := net.ResolveUDPAddr("udp", p.LocalConn.LocalAddr().String())
if err != nil {
return err
}
log.Println("--------> UDPADDR: ", udpAddr)
// add local proxy connection as a Wireguard peer
err = p.Config.WgInterface.UpdatePeer(p.Config.RemoteKey, p.Config.AllowedIps, wg.DefaultWgKeepAlive,
udpAddr, p.Config.PreSharedKey)
if err != nil {
return err
}
return nil
}
func (p *Proxy) Start(remoteConn net.Conn) error {
p.RemoteConn = remoteConn
var err error
addr, err := GetFreeIp("127.0.0.1/8")
if err != nil {
log.Println("Failed to get freeIp: ", err)
return err
}
wgAddr := "127.0.0.1"
if runtime.GOOS == "darwin" {
wgAddr = addr
}
wgPort, err := p.Config.WgInterface.GetListenPort()
if err != nil {
log.Printf("Failed to get listen port for iface: %s,Err: %v\n", p.Config.WgInterface.Name, err)
return err
}
p.LocalConn, err = net.DialUDP("udp", &net.UDPAddr{
IP: net.ParseIP(addr),
Port: common.NmProxyPort,
}, &net.UDPAddr{
IP: net.ParseIP(wgAddr),
Port: *wgPort,
})
if err != nil {
log.Fatalf("failed dialing to local Wireguard port,Err: %v\n", err)
}
log.Printf("Dialing to local Wireguard port %s --> %s\n", p.LocalConn.LocalAddr().String(), p.LocalConn.RemoteAddr().String())
err = p.updateEndpoint()
if err != nil {
log.Printf("error while updating Wireguard peer endpoint [%s] %v\n", p.Config.RemoteKey, err)
return err
}
go p.ProxyToRemote()
return nil
}
func GetFreeIp(cidrAddr string) (string, error) {
//ensure AddressRange is valid
if _, _, err := net.ParseCIDR(cidrAddr); err != nil {
log.Println("UniqueAddress encountered an error")
return "", err
}
net4 := iplib.Net4FromStr(cidrAddr)
newAddrs := net4.FirstAddress()
log.Println("COUNT: ", net4.Count())
for {
if runtime.GOOS == "darwin" {
_, err := common.RunCmd(fmt.Sprintf("ifconfig lo0 alias %s 255.255.255.255", newAddrs.String()), true)
if err != nil {
log.Println("Failed to add alias: ", err)
}
}
conn, err := net.DialUDP("udp", &net.UDPAddr{
IP: net.ParseIP(newAddrs.String()),
Port: 51722,
}, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: 51820,
})
if err == nil {
conn.Close()
return newAddrs.String(), nil
}
newAddrs, err = net4.NextIP(newAddrs)
if err != nil {
return "", err
}
}
}

View file

@ -0,0 +1,115 @@
package server
import (
"fmt"
"log"
"net"
"time"
"github.com/gravitl/netmaker/netclient/netclient-proxy/common"
"github.com/gravitl/netmaker/netclient/netclient-proxy/packet"
)
const (
defaultBodySize = 10000
defaultPort = 51722
)
type Config struct {
Port int
BodySize int
Addr net.Addr
LocalWgInterfaces []string
}
type ProxyServer struct {
Config Config
Server *net.UDPConn
}
// Proxy.Listen - begins listening for packets
func (p *ProxyServer) Listen() {
// Buffer with indicated body size
buffer := make([]byte, 1502)
for {
// Read Packet
n, source, err := p.Server.ReadFromUDP(buffer)
if err != nil { // in future log errors?
log.Println("RECV ERROR: ", err)
continue
}
var localWgPort int
localWgPort, n, err = packet.ExtractInfo(buffer, n)
if err != nil {
log.Println("failed to extract info: ", err)
continue
}
log.Println("--------> RECV PKT: ", source.IP.String(), localWgPort)
if val, ok := common.RemoteEndpointsMap[source.IP.String()]; ok {
for _, peerKeys := range val {
if peerI, ok := common.Peers[peerKeys]; ok {
if peerI.Config.LocalWgPort == int(localWgPort) {
log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s\n", peerI.Proxy.LocalConn.RemoteAddr(),
peerI.Proxy.LocalConn.LocalAddr(), fmt.Sprintf("%s:%d", source.IP.String(), source.Port))
_, err = peerI.Proxy.LocalConn.Write(buffer[:n])
if err != nil {
log.Println("Failed to proxy to Wg local interface: ", err)
continue
}
}
}
}
}
}
}
// Create - creats a proxy listener
// port - port for proxy to listen on localhost
// bodySize - default 10000, leave 0 to use default
// addr - the address for proxy to listen on
// forwards - indicate address to forward to, {"<address:port>",...} format
func CreateProxyServer(port, bodySize int, addr string) (p *ProxyServer, err error) {
if p == nil {
p = &ProxyServer{}
}
p.Config.Port = port
p.Config.BodySize = bodySize
p.setDefaults()
p.Server, err = net.ListenUDP("udp", &net.UDPAddr{
Port: p.Config.Port,
IP: net.ParseIP(addr),
})
return
}
func (p *ProxyServer) KeepAlive(ip string, port int) {
for {
_, _ = p.Server.Write([]byte("hello-proxy"))
//fmt.Println("Sending MSg: ", err)
time.Sleep(time.Second)
}
}
// Proxy.setDefaults - sets all defaults of proxy listener
func (p *ProxyServer) setDefaults() {
p.setDefaultBodySize()
p.setDefaultPort()
}
// Proxy.setDefaultPort - sets default port of Proxy listener if 0
func (p *ProxyServer) setDefaultPort() {
if p.Config.Port == 0 {
p.Config.Port = defaultPort
}
}
// Proxy.setDefaultBodySize - sets default body size of Proxy listener if 0
func (p *ProxyServer) setDefaultBodySize() {
if p.Config.BodySize == 0 {
p.Config.BodySize = defaultBodySize
}
}

View file

@ -0,0 +1,66 @@
package stun
import (
"fmt"
"log"
"net"
"strconv"
"strings"
"gortc.io/stun"
)
type HostInfo struct {
PublicIp net.IP
PrivIp net.IP
PubPort int
PrivPort int
}
var Host HostInfo
func GetHostInfo() (info HostInfo) {
s, err := net.ResolveUDPAddr("udp", "stun.nm.134.209.115.146.nip.io:3478")
if err != nil {
log.Fatal("Resolve: ", err)
}
l := &net.UDPAddr{
IP: net.ParseIP(""),
Port: 51722,
}
conn, err := net.DialUDP("udp", l, s)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
fmt.Printf("%+v\n", conn.LocalAddr())
c, err := stun.NewClient(conn)
if err != nil {
panic(err)
}
defer c.Close()
re := strings.Split(conn.LocalAddr().String(), ":")
info.PrivIp = net.ParseIP(re[0])
info.PrivPort, _ = strconv.Atoi(re[1])
// Building binding request with random transaction id.
message := stun.MustBuild(stun.TransactionID, stun.BindingRequest)
//fmt.Printf("MESG: %+v\n", message)
// Sending request to STUN server, waiting for response message.
if err := c.Do(message, func(res stun.Event) {
//fmt.Printf("RESP: %+v\n", res)
if res.Error != nil {
panic(res.Error)
}
// Decoding XOR-MAPPED-ADDRESS attribute from message.
var xorAddr stun.XORMappedAddress
if err := xorAddr.GetFrom(res.Message); err != nil {
panic(err)
}
info.PublicIp = xorAddr.IP
info.PubPort = xorAddr.Port
}); err != nil {
panic(err)
}
return
}

View file

@ -0,0 +1,153 @@
package wg
import (
"fmt"
"log"
"net"
"sync"
"time"
"golang.zx2c4.com/wireguard/wgctrl"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
const (
DefaultMTU = 1280
DefaultWgPort = 51820
DefaultWgKeepAlive = 25 * time.Second
)
// WGIface represents a interface instance
type WGIface struct {
Name string
Port int
MTU int
Address WGAddress
Interface NetInterface
mu sync.Mutex
}
// NetInterface represents a generic network tunnel interface
type NetInterface interface {
Close() error
}
// WGAddress Wireguard parsed address
type WGAddress struct {
IP net.IP
Network *net.IPNet
}
// NewWGIFace Creates a new Wireguard interface instance
func 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
return wgIface, nil
}
func (w *WGIface) GetWgIface(iface string) error {
wgClient, err := wgctrl.New()
if err != nil {
return err
}
wgClient.Device(iface)
return nil
}
// parseAddress parse a string ("1.2.3.4/24") address to WG Address
func 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 exist
// Endpoint is optional
func (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 device
func (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 endpoint
func (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
}