mirror of
https://github.com/gravitl/netmaker.git
synced 2024-09-20 23:36:18 +08:00
proxy integration with netclient inital commit
This commit is contained in:
parent
aabb573a19
commit
953b5eae0a
BIN
netclient/netclient-proxy/bin/proxy.linux
Executable file
BIN
netclient/netclient-proxy/bin/proxy.linux
Executable file
Binary file not shown.
146
netclient/netclient-proxy/common/common.go
Normal file
146
netclient/netclient-proxy/common/common.go
Normal 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
|
||||
}
|
78
netclient/netclient-proxy/netclient-proxy.go
Normal file
78
netclient/netclient-proxy/netclient-proxy.go
Normal 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
|
||||
}
|
39
netclient/netclient-proxy/packet/packet.go
Normal file
39
netclient/netclient-proxy/packet/packet.go
Normal 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
|
||||
}
|
116
netclient/netclient-proxy/peer/peer.go
Normal file
116
netclient/netclient-proxy/peer/peer.go
Normal 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
|
||||
}
|
37
netclient/netclient-proxy/proxy/proxy.go
Normal file
37
netclient/netclient-proxy/proxy/proxy.go
Normal 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
|
||||
}
|
195
netclient/netclient-proxy/proxy/wireguard.go
Normal file
195
netclient/netclient-proxy/proxy/wireguard.go
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
115
netclient/netclient-proxy/server/server.go
Normal file
115
netclient/netclient-proxy/server/server.go
Normal 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
|
||||
}
|
||||
}
|
66
netclient/netclient-proxy/stun/stun.go
Normal file
66
netclient/netclient-proxy/stun/stun.go
Normal 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
|
||||
}
|
153
netclient/netclient-proxy/wg/wg.go
Normal file
153
netclient/netclient-proxy/wg/wg.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue