configure proxy for ext clients

This commit is contained in:
Abhishek Kondur 2022-11-14 14:10:52 +05:30
parent 5d7bf9502b
commit 0a17a0744b
11 changed files with 269 additions and 60 deletions

1
go.mod
View file

@ -41,6 +41,7 @@ require (
require (
github.com/coreos/go-oidc/v3 v3.4.0
github.com/google/gopacket v1.1.19
github.com/gorilla/websocket v1.5.0
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.0

2
go.sum
View file

@ -232,6 +232,8 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=

View file

@ -413,6 +413,7 @@ func getExtPeersForProxy(node *models.Node, proxyPeerConf map[string]manager.Pee
extConf := manager.PeerConf{
IsExtClient: true,
Address: extPeer.Address,
}
if extPeer.IngressGatewayID == node.ID {
extConf.IsAttachedExtClient = true

View file

@ -34,7 +34,7 @@ import (
var ProxyMgmChan = make(chan *manager.ManagerAction, 100)
var messageCache = new(sync.Map)
var ProxyStatus = "OFF"
var serverSet map[string]bool
var mqclient mqtt.Client
@ -126,13 +126,26 @@ func startGoRoutines(wg *sync.WaitGroup) context.CancelFunc {
go Checkin(ctx, wg)
if len(networks) != 0 {
cfg := config.ClientConfig{}
cfg.Network = networks[0]
cfg.ReadConfig()
apiHost, _, err := net.SplitHostPort(cfg.Server.API)
if err == nil {
go nmproxy.Start(ctx, ProxyMgmChan, apiHost)
}
go func() {
cfg := config.ClientConfig{}
cfg.Network = networks[0]
cfg.ReadConfig()
apiHost, _, err := net.SplitHostPort(cfg.Server.API)
if err == nil {
if ProxyStatus != "ON" {
ProxyStatus = "ON"
pCtx, pCancel := context.WithCancel(context.Background())
go nmproxy.Start(pCtx, ProxyMgmChan, apiHost)
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, os.Interrupt)
<-quit
pCancel()
logger.Log(0, "Proxy Shutting down....")
}
}
}()
}
go func(networks []string) {

View file

@ -63,9 +63,11 @@ type Proxy struct {
}
type RemotePeer struct {
PeerKey string
Interface string
Endpoint *net.UDPAddr
PeerKey string
Interface string
Endpoint *net.UDPAddr
IsExtClient bool
IsAttachedExtClient bool
}
var WgIFaceMap = make(map[string]map[string]*Conn)
@ -76,8 +78,6 @@ var WgIfaceKeyMap = make(map[string]struct{})
var RelayPeerMap = make(map[string]map[string]RemotePeer)
var ExtClientsMap = make(map[string]RemotePeer)
// RunCmd - runs a local command
func RunCmd(command string, printerr bool) (string, error) {
args := strings.Fields(command)

View file

@ -7,13 +7,25 @@ import (
"log"
"net"
"runtime"
"time"
"github.com/gravitl/netmaker/nm-proxy/common"
"github.com/gravitl/netmaker/nm-proxy/packet"
peerpkg "github.com/gravitl/netmaker/nm-proxy/peer"
"github.com/gravitl/netmaker/nm-proxy/wg"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
)
/*
TODO:-
1. ON Ingress node
--> for attached ext clients
-> start sniffer (will recieve pkts from ext clients (add ebf filter to listen on only ext traffic) if not intended to the interface forward it.)
-> start remote conn after endpoint is updated
-->
*/
var sent bool
type ProxyAction string
type ManagerPayload struct {
@ -35,6 +47,7 @@ type RelayedConf struct {
type PeerConf struct {
IsExtClient bool `json:"is_ext_client"`
Address string `json:"address"`
IsAttachedExtClient bool `json:"is_attached_ext_client"`
IngressGatewayEndPoint *net.UDPAddr `json:"ingress_gateway_endpoint"`
IsRelayed bool `json:"is_relayed"`
@ -60,9 +73,13 @@ func StartProxyManager(manageChan chan *ManagerAction) {
select {
case mI := <-manageChan:
if sent {
continue
}
log.Printf("-------> PROXY-MANAGER: %+v\n", mI)
switch mI.Action {
case AddInterface:
sent = true
common.IsRelay = mI.Payload.IsRelay
if mI.Payload.IsRelay {
mI.RelayPeers()
@ -103,11 +120,14 @@ func (m *ManagerAction) RelayPeers() {
common.RelayPeerMap[relayedNodePubKeyHash] = make(map[string]common.RemotePeer)
}
for _, peer := range relayedNodeConf.Peers {
peer.Endpoint.Port = common.NmProxyPort
remotePeerKeyHash := fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))
common.RelayPeerMap[relayedNodePubKeyHash][remotePeerKeyHash] = common.RemotePeer{
Endpoint: peer.Endpoint,
if peer.Endpoint != nil {
peer.Endpoint.Port = common.NmProxyPort
remotePeerKeyHash := fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))
common.RelayPeerMap[relayedNodePubKeyHash][remotePeerKeyHash] = common.RemotePeer{
Endpoint: peer.Endpoint,
}
}
}
relayedNodeConf.RelayedPeerEndpoint.Port = common.NmProxyPort
common.RelayPeerMap[relayedNodePubKeyHash][relayedNodePubKeyHash] = common.RemotePeer{
@ -174,6 +194,8 @@ func cleanUp(iface string) {
}
}
delete(common.WgIFaceMap, iface)
time.Sleep(time.Second * 5)
log.Println("CLEANED UP..........")
}
func (m *ManagerAction) AddInterfaceToProxy() error {
@ -208,9 +230,34 @@ func (m *ManagerAction) AddInterfaceToProxy() error {
log.Println("Endpoint nil for peer: ", peerI.PublicKey.String())
continue
}
common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peerI.PublicKey.String())))] = common.RemotePeer{
Interface: ifaceName,
PeerKey: peerI.PublicKey.String(),
shouldProceed := false
if peerConf.IsExtClient && peerConf.IsAttachedExtClient {
// check if ext client got endpoint,otherwise continue
for _, devpeerI := range wgInterface.Device.Peers {
if devpeerI.PublicKey.String() == peerI.PublicKey.String() && devpeerI.Endpoint != nil {
peerI.Endpoint = devpeerI.Endpoint
shouldProceed = true
break
}
}
} else {
shouldProceed = true
}
if peerConf.IsExtClient && peerConf.IsAttachedExtClient && shouldProceed {
go packet.StartSniffer(wgInterface.Name, peerConf.Address)
}
if peerConf.IsExtClient && !peerConf.IsAttachedExtClient {
peerI.Endpoint = peerConf.IngressGatewayEndPoint
}
if shouldProceed {
common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peerI.PublicKey.String())))] = common.RemotePeer{
Interface: ifaceName,
PeerKey: peerI.PublicKey.String(),
IsExtClient: peerConf.IsExtClient,
IsAttachedExtClient: peerConf.IsAttachedExtClient,
}
}
var isRelayed bool
@ -224,9 +271,48 @@ func (m *ManagerAction) AddInterfaceToProxy() error {
relayedTo = peerConf.RelayedTo
}
if !shouldProceed && peerConf.IsAttachedExtClient {
log.Println("Extclient endpoint not updated yet....skipping")
// TODO - watch the interface for ext client update
go func(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig,
isRelayed, isExtClient, isAttachedExtClient bool, relayTo *net.UDPAddr, peerConf PeerConf) {
addExtClient := false
defer func() {
if addExtClient {
common.PeerKeyHashMap[fmt.Sprintf("%x", md5.Sum([]byte(peer.PublicKey.String())))] = common.RemotePeer{
Interface: wgInterface.Name,
PeerKey: peer.PublicKey.String(),
IsExtClient: peerConf.IsExtClient,
IsAttachedExtClient: peerConf.IsAttachedExtClient,
Endpoint: peer.Endpoint,
}
peerpkg.AddNewPeer(wgInterface, peer, isRelayed,
peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
}
}()
for {
wgInterface, err := wg.NewWGIFace(ifaceName, "127.0.0.1/32", wg.DefaultMTU)
if err != nil {
log.Println("Failed init new interface: ", err)
continue
}
for _, devpeerI := range wgInterface.Device.Peers {
if devpeerI.PublicKey.String() == peer.PublicKey.String() && devpeerI.Endpoint != nil {
peer.Endpoint = devpeerI.Endpoint
addExtClient = true
return
}
}
time.Sleep(time.Second * 5)
}
}(wgInterface, &peerI, isRelayed, peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo, peerConf)
continue
}
peerpkg.AddNewPeer(wgInterface, &peerI, isRelayed,
peerConf.IsExtClient, peerConf.IngressGatewayEndPoint, relayedTo)
peerConf.IsExtClient, peerConf.IsAttachedExtClient, relayedTo)
}
log.Printf("------> PEERHASHMAP: %+v\n", common.PeerKeyHashMap)
return nil

View file

@ -21,7 +21,7 @@ import (
func Start(ctx context.Context, mgmChan chan *manager.ManagerAction, apiServerAddr string) {
log.Println("Starting Proxy...")
common.IsHostNetwork = (os.Getenv("HOST_NETWORK") == "" || os.Getenv("HOST_NETWORK") == "on")
go manager.StartProxyManager(mgmChan)
hInfo := stun.GetHostInfo(apiServerAddr)
stun.Host = hInfo
log.Printf("HOSTINFO: %+v", hInfo)
@ -33,6 +33,7 @@ func Start(ctx context.Context, mgmChan chan *manager.ManagerAction, apiServerAd
if err != nil {
log.Fatal("failed to create proxy: ", err)
}
go manager.StartProxyManager(mgmChan)
server.NmProxyServer.Listen(ctx)
}

View file

@ -3,6 +3,12 @@ package packet
import (
"crypto/md5"
"fmt"
"log"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)
var udpHeaderLen = 8
@ -34,3 +40,95 @@ func ExtractInfo(buffer []byte, n int) (int, string, string) {
n -= 32
return n, fmt.Sprintf("%x", srcKeyHash), fmt.Sprintf("%x", dstKeyHash)
}
func StartSniffer(ifaceName string, extClient string) {
var (
snapshotLen int32 = 1024
promiscuous bool = false
err error
timeout time.Duration = 30 * time.Second
handle *pcap.Handle
)
// Open device
handle, err = pcap.OpenLive(ifaceName, snapshotLen, promiscuous, timeout)
if err != nil {
log.Fatal(err)
}
defer handle.Close()
// var tcp layers.TCP
// var icmp layers.ICMPv4
// var udp layers.UDP
// parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &udp, &tcp, &icmp)
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for {
packet, err := packetSource.NextPacket()
if err == nil {
printPacketInfo(packet)
}
}
}
func printPacketInfo(packet gopacket.Packet) {
// Let's see if the packet is an ethernet packet
// ethernetLayer := packet.Layer(layers.LayerTypeEthernet)
// if ethernetLayer != nil {
// fmt.Println("Ethernet layer detected.")
// ethernetPacket, _ := ethernetLayer.(*layers.Ethernet)
// fmt.Println("Source MAC: ", ethernetPacket.SrcMAC)
// fmt.Println("Destination MAC: ", ethernetPacket.DstMAC)
// // Ethernet type is typically IPv4 but could be ARP or other
// fmt.Println("Ethernet type: ", ethernetPacket.EthernetType)
// fmt.Println()
// }
// Let's see if the packet is IP (even though the ether type told us)
ipLayer := packet.Layer(layers.LayerTypeIPv4)
if ipLayer != nil {
fmt.Println("IPv4 layer detected.")
ip, _ := ipLayer.(*layers.IPv4)
// IP layer variables:
// Version (Either 4 or 6)
// IHL (IP Header Length in 32-bit words)
// TOS, Length, Id, Flags, FragOffset, TTL, Protocol (TCP?),
// Checksum, SrcIP, DstIP
fmt.Printf("From %s to %s\n", ip.SrcIP, ip.DstIP)
fmt.Println("Protocol: ", ip.Protocol)
fmt.Println()
}
// udpLayer := packet.Layer(layers.LayerTypeUDP)
// if udpLayer != nil {
// udp, _ := udpLayer.(*layers.UDP)
// fmt.Printf("UDP: From port %d to %d\n", udp.SrcPort, udp.DstPort)
// fmt.Println()
// }
// // Iterate over all layers, printing out each layer type
// fmt.Println("All packet layers:")
// for _, layer := range packet.Layers() {
// fmt.Println("- ", layer.LayerType())
// }
// When iterating through packet.Layers() above,
// if it lists Payload layer then that is the same as
// this applicationLayer. applicationLayer contains the payload
// applicationLayer := packet.ApplicationLayer()
// if applicationLayer != nil {
// fmt.Println("Application layer/Payload found.")
// fmt.Printf("%s\n", applicationLayer.Payload())
// // Search for a string inside the payload
// if strings.Contains(string(applicationLayer.Payload()), "HTTP") {
// fmt.Println("HTTP found!")
// }
// }
// Check for errors
if err := packet.ErrorLayer(); err != nil {
fmt.Println("Error decoding some part of the packet:", err)
}
}

View file

@ -2,6 +2,7 @@ package peer
import (
"crypto/md5"
"errors"
"fmt"
"log"
"net"
@ -34,7 +35,7 @@ type ConnConfig struct {
}
func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig,
isRelayed, isExtClient bool, ingGateway, relayTo *net.UDPAddr) error {
isRelayed, isExtClient, isAttachedExtClient bool, relayTo *net.UDPAddr) error {
c := proxy.Config{
Port: peer.Endpoint.Port,
@ -44,16 +45,21 @@ func AddNewPeer(wgInterface *wg.WGIface, peer *wgtypes.PeerConfig,
AllowedIps: peer.AllowedIPs,
}
p := proxy.NewProxy(c)
peerPort := common.NmProxyPort
if isExtClient && isAttachedExtClient {
peerPort = peer.Endpoint.Port
}
peerEndpoint := peer.Endpoint.IP.String()
if isRelayed {
//go server.NmProxyServer.KeepAlive(peer.Endpoint.IP.String(), common.NmProxyPort)
if relayTo == nil {
return errors.New("relay endpoint is nil")
}
peerEndpoint = relayTo.IP.String()
} else if isExtClient {
peerEndpoint = ingGateway.IP.String()
}
remoteConn, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peerEndpoint, common.NmProxyPort))
remoteConn, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", peerEndpoint, peerPort))
if err != nil {
return err
}

View file

@ -25,10 +25,10 @@ func NewProxy(config Config) *Proxy {
// proxyToRemote proxies everything from Wireguard to the RemoteKey peer
func (p *Proxy) ProxyToRemote() {
buf := make([]byte, 1500)
go func() {
<-p.Ctx.Done()
defer p.LocalConn.Close()
log.Println("Closing connection for: ", p.LocalConn.LocalAddr().String())
p.LocalConn.Close()
}()
for {
select {
@ -40,14 +40,16 @@ func (p *Proxy) ProxyToRemote() {
log.Println("Failed to split host: ", p.LocalConn.LocalAddr().String(), err)
return
}
if host == "127.0.0.1" {
return
}
_, err = common.RunCmd(fmt.Sprintf("ifconfig lo0 -alias %s 255.255.255.255", host), true)
if err != nil {
log.Println("Failed to add alias: ", err)
if host != "127.0.0.1" {
_, err = common.RunCmd(fmt.Sprintf("ifconfig lo0 -alias %s 255.255.255.255", host), true)
if err != nil {
log.Println("Failed to add alias: ", err)
}
}
}
return
default:
@ -58,15 +60,16 @@ func (p *Proxy) ProxyToRemote() {
}
peers := common.WgIFaceMap[p.Config.WgInterface.Name]
if peerI, ok := peers[p.Config.RemoteKey]; ok {
var srcPeerKeyHash, dstPeerKeyHash string
buf, n, srcPeerKeyHash, dstPeerKeyHash = packet.ProcessPacketBeforeSending(buf, n, peerI.Config.LocalKey, peerI.Config.Key)
//var srcPeerKeyHash, dstPeerKeyHash string
buf, n, _, _ = packet.ProcessPacketBeforeSending(buf, n, peerI.Config.LocalKey, peerI.Config.Key)
if err != nil {
log.Println("failed to process pkt before sending: ", err)
}
log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s [[ SrcPeerHash: %s, DstPeerHash: %s ]]\n",
server.NmProxyServer.Server.LocalAddr().String(), p.RemoteConn.String(), srcPeerKeyHash, dstPeerKeyHash)
// log.Printf("PROXING TO REMOTE!!!---> %s >>>>> %s [[ SrcPeerHash: %s, DstPeerHash: %s ]]\n",
// server.NmProxyServer.Server.LocalAddr().String(), p.RemoteConn.String(), srcPeerKeyHash, dstPeerKeyHash)
} else {
log.Printf("Peer: %s not found in config\n", p.Config.RemoteKey)
p.Cancel()
continue
}
//test(n, buf)

View file

@ -2,7 +2,6 @@ package server
import (
"context"
"fmt"
"log"
"net"
"time"
@ -62,28 +61,23 @@ func (p *ProxyServer) Listen(ctx context.Context) {
var srcPeerKeyHash, dstPeerKeyHash string
n, srcPeerKeyHash, dstPeerKeyHash = packet.ExtractInfo(buffer, n)
//log.Printf("--------> RECV PKT [DSTPORT: %d], [SRCKEYHASH: %s], SourceIP: [%s] \n", localWgPort, srcPeerKeyHash, source.IP.String())
if common.IsIngressGateway {
if peerConf, ok := common.ExtClientsMap[source.IP.String()]; ok {
log.Println("Pkt recieved from Ext clients...forward it to all peers")
if peers, ok := common.WgIFaceMap[peerConf.Interface]; ok {
for _, peerI := range peers {
// log.Printf("--------> Forwading Ext PKT [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
// source.String(), srcPeerKeyHash, peerI.Config.RemoteProxyIP, dstPeerKeyHash)
out, outN, _, _ := packet.ProcessPacketBeforeSending(buffer[:1500], n-32, peerConf.PeerKey, peerI.Config.Key)
_, err = NmProxyServer.Server.WriteToUDP(out[:outN], &net.UDPAddr{
IP: peerI.Config.RemoteProxyIP,
Port: peerI.Config.RemoteProxyPort,
})
if err != nil {
log.Println("Failed to send to remote: ", err)
}
if _, ok := common.WgIfaceKeyMap[dstPeerKeyHash]; !ok {
if common.IsIngressGateway {
log.Println("----> fowarding PKT to EXT client...")
if val, ok := common.PeerKeyHashMap[dstPeerKeyHash]; ok && val.IsAttachedExtClient {
log.Printf("-------->Forwarding the pkt to extClient [ SourceIP: %s ], [ SourceKeyHash: %s ], [ DstIP: %s ], [ DstHashKey: %s ] \n",
source.String(), srcPeerKeyHash, val.Endpoint.String(), dstPeerKeyHash)
_, err = NmProxyServer.Server.WriteToUDP(buffer[:n+32], val.Endpoint)
if err != nil {
log.Println("Failed to send to remote: ", err)
}
continue
}
}
}
if common.IsRelay && dstPeerKeyHash != "" && srcPeerKeyHash != "" {
if _, ok := common.WgIfaceKeyMap[dstPeerKeyHash]; !ok {
if common.IsRelay {
log.Println("----------> Relaying######")
// check for routing map and forward to right proxy
@ -95,6 +89,7 @@ func (p *ProxyServer) Listen(ctx context.Context) {
if err != nil {
log.Println("Failed to send to remote: ", err)
}
continue
}
} else {
if remoteMap, ok := common.RelayPeerMap[dstPeerKeyHash]; ok {
@ -105,19 +100,22 @@ func (p *ProxyServer) Listen(ctx context.Context) {
if err != nil {
log.Println("Failed to send to remote: ", err)
}
continue
}
}
}
}
}
if peerInfo, ok := common.PeerKeyHashMap[srcPeerKeyHash]; ok {
if peers, ok := common.WgIFaceMap[peerInfo.Interface]; ok {
if peerI, ok := peers[peerInfo.PeerKey]; ok {
log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
peerI.Proxy.LocalConn.RemoteAddr(), peerI.Proxy.LocalConn.LocalAddr(),
fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
// log.Printf("PROXING TO LOCAL!!!---> %s <<<< %s <<<<<<<< %s [[ RECV PKT [SRCKEYHASH: %s], [DSTKEYHASH: %s], SourceIP: [%s] ]]\n",
// peerI.Proxy.LocalConn.RemoteAddr(), peerI.Proxy.LocalConn.LocalAddr(),
// fmt.Sprintf("%s:%d", source.IP.String(), source.Port), srcPeerKeyHash, dstPeerKeyHash, source.IP.String())
_, err = peerI.Proxy.LocalConn.Write(buffer[:n])
if err != nil {
log.Println("Failed to proxy to Wg local interface: ", err)