adding extclient functionality

This commit is contained in:
afeiszli 2021-05-19 13:59:10 -04:00
parent 5662a1538e
commit 29e332edf7
16 changed files with 1976 additions and 1509 deletions

View file

@ -58,6 +58,49 @@ func GetPeersList(networkName string) ([]models.PeersResponse, error) {
return peers, err
}
func GetExtPeersList(networkName string, macaddress string) ([]models.ExtPeersResponse, error) {
var peers []models.ExtPeersResponse
//Connection mongoDB with mongoconn class
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
//Get all nodes in the relevant network which are NOT in pending state
filter := bson.M{"network": networkName, "gatewayid": macaddress}
cur, err := collection.Find(ctx, filter)
if err != nil {
return peers, err
}
// Close the cursor once finished and cancel if it takes too long
defer cancel()
for cur.Next(context.TODO()) {
var peer models.ExtPeersResponse
err := cur.Decode(&peer)
if err != nil {
log.Fatal(err)
}
// add the node to our node array
//maybe better to just return this? But then that's just GetNodes...
peers = append(peers, peer)
}
//Uh oh, fatal error! This needs some better error handling
//TODO: needs appropriate error handling so the server doesnt shut down.
if err := cur.Err(); err != nil {
log.Fatal(err)
}
return peers, err
}
func ValidateNodeCreate(networkName string, node models.Node) error {
v := validator.New()
_ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {

View file

@ -31,6 +31,8 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
dnsHandlers(r)
fileHandlers(r)
serverHandlers(r)
extClientHandlers(r)
port := servercfg.GetAPIPort()

View file

@ -0,0 +1,413 @@
package controller
import (
"context"
"encoding/json"
"errors"
"fmt"
// "fmt"
"net/http"
"time"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/skip2/go-qrcode"
)
func extClientHandlers(r *mux.Router) {
r.HandleFunc("/api/extclients", securityCheck(http.HandlerFunc(getAllExtClients))).Methods("GET")
r.HandleFunc("/api/extclients/{network}", securityCheck(http.HandlerFunc(getNetworkExtClients))).Methods("GET")
r.HandleFunc("/api/extclients/{network}/{clientid}", securityCheck(http.HandlerFunc(getExtClient))).Methods("GET")
r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", securityCheck(http.HandlerFunc(getExtClientConf))).Methods("GET")
r.HandleFunc("/api/extclients/{network}/{clientid}", securityCheck(http.HandlerFunc(updateExtClient))).Methods("PUT")
r.HandleFunc("/api/extclients/{network}/{clientid}", securityCheck(http.HandlerFunc(deleteExtClient))).Methods("DELETE")
r.HandleFunc("/api/extclients/{network}/{macaddress}", securityCheck(http.HandlerFunc(createExtClient))).Methods("POST")
}
// TODO: Implement Validation
func ValidateExtClientCreate(networkName string, extclient models.ExtClient) error {
// v := validator.New()
// _ = v.RegisterValidation("macaddress_unique", func(fl validator.FieldLevel) bool {
// var isFieldUnique bool = functions.IsFieldUnique(networkName, "macaddress", extclient.MacAddress)
// return isFieldUnique
// })
// _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
// _, err := extclient.GetNetwork()
// return err == nil
// })
// err := v.Struct(extclient)
// if err != nil {
// for _, e := range err.(validator.ValidationErrors) {
// fmt.Println(e)
// }
// }
return nil
}
// TODO: Implement Validation
func ValidateExtClientUpdate(networkName string, extclient models.ExtClient) error {
// v := validator.New()
// _ = v.RegisterValidation("network_exists", func(fl validator.FieldLevel) bool {
// _, err := extclient.GetNetwork()
// return err == nil
// })
// err := v.Struct(extclient)
// if err != nil {
// for _, e := range err.(validator.ValidationErrors) {
// fmt.Println(e)
// }
// }
return nil
}
func checkIngressExists(network string, macaddress string) bool {
node, err := functions.GetNodeByMacAddress(network, macaddress)
if err != nil {
return false
}
return node.IsIngressGateway
}
//Gets all extclients associated with network, including pending extclients
func getNetworkExtClients(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var extclients []models.ExtClient
var params = mux.Vars(r)
extclients, err := GetNetworkExtClients(params["network"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
//Returns all the extclients in JSON format
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(extclients)
}
func GetNetworkExtClients(network string) ([]models.ExtClient, error) {
var extclients []models.ExtClient
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"network": network}
//Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
if err != nil {
return []models.ExtClient{}, err
}
defer cancel()
for cur.Next(context.TODO()) {
//Using a different model for the ReturnExtClient (other than regular extclient).
//Either we should do this for ALL structs (so Networks and Keys)
//OR we should just use the original struct
//My preference is to make some new return structs
//TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
var extclient models.ExtClient
err := cur.Decode(&extclient)
if err != nil {
return []models.ExtClient{}, err
}
// add item our array of extclients
extclients = append(extclients, extclient)
}
//TODO: Another fatal error we should take care of.
if err := cur.Err(); err != nil {
return []models.ExtClient{}, err
}
return extclients, nil
}
//A separate function to get all extclients, not just extclients for a particular network.
//Not quite sure if this is necessary. Probably necessary based on front end but may want to review after iteration 1 if it's being used or not
func getAllExtClients(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
extclients, err := functions.GetAllExtClients()
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
//Return all the extclients in JSON format
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(extclients)
}
//Get an individual extclient. Nothin fancy here folks.
func getExtClient(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
var extclient models.ExtClient
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"network": params["network"], "clientid": params["clientid"]}
err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&extclient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
defer cancel()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(extclient)
}
//Get an individual extclient. Nothin fancy here folks.
func getExtClientConf(w http.ResponseWriter, r *http.Request) {
// set header.
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
var extclient models.ExtClient
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"network": params["network"], "clientid": params["clientid"]}
err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&extclient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
gwnode, err := functions.GetNodeByMacAddress(extclient.Network, extclient.IngressGatewayID)
if err != nil {
fmt.Println("Could not retrieve Ingress Gateway Node " + extclient.IngressGatewayID)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
network, err := functions.GetParentNetwork(extclient.Network)
if err != nil {
fmt.Println("Could not retrieve Ingress Gateway Network " + extclient.Network)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
config := fmt.Sprintf(`[Interface]
Address = %s
PrivateKey = %s
[Peer]
PublicKey = %s
AllowedIPs = %s
Endpoint = %s
`, extclient.Address + "/32",
extclient.PrivateKey,
gwnode.PublicKey,
network.AddressRange,
network.DefaultKeepalive)
if params["type"] == "qr" {
bytes, err := qrcode.Encode(config, qrcode.Medium, 220)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
w.Header().Set("Content-Type", "image/png")
w.WriteHeader(http.StatusOK)
_, err = w.Write(bytes)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
return
}
if params["type"] == "file" {
name := extclient.ClientID + ".conf"
w.Header().Set("Content-Type", "application/config")
w.Header().Set("Content-Disposition", "attachment; filename=\"" + name + "\"")
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprint(w, config)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
}
return
}
defer cancel()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(extclient)
}
func CreateExtClient(extclient models.ExtClient) error {
fmt.Println(extclient)
// Generate Private Key for new ExtClient
if extclient.PrivateKey == "" {
privateKey, err := wgtypes.GeneratePrivateKey()
if err != nil {
return err
}
extclient.PrivateKey = privateKey.String()
extclient.PublicKey = privateKey.PublicKey().String()
}
if extclient.Address == "" {
newAddress, err := functions.UniqueAddress(extclient.Network)
if err != nil {
return err
}
extclient.Address = newAddress
}
extclient.LastModified = time.Now().Unix()
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// insert our network into the network table
_, err := collection.InsertOne(ctx, extclient)
defer cancel()
return err
}
//This one's a doozy
//To create a extclient
//Must have valid key and be unique
func createExtClient(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
networkName := params["network"]
macaddress := params["macaddress"]
//Check if network exists first
//TODO: This is inefficient. Let's find a better way.
//Just a few rows down we grab the network anyway
ingressExists := checkIngressExists(networkName, macaddress)
if !ingressExists {
returnErrorResponse(w, r, formatError(errors.New("ingress does not exist"), "internal"))
return
}
var extclient models.ExtClient
extclient.Network = networkName
extclient.IngressGatewayID = macaddress
//get extclient from body of request
err := json.NewDecoder(r.Body).Decode(&extclient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
err = ValidateExtClientCreate(params["network"], extclient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "badrequest"))
return
}
err = CreateExtClient(extclient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
w.WriteHeader(http.StatusOK)
}
func updateExtClient(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
var newExtClient models.ExtClient
var oldExtClient models.ExtClient
// we decode our body request params
_ = json.NewDecoder(r.Body).Decode(&newExtClient)
// TODO: Validation for update.
// err := ValidateExtClientUpdate(params["network"], params["clientid"], newExtClient)
// if err != nil {
// returnErrorResponse(w, r, formatError(err, "badrequest"))
// return
// }
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"network": params["network"], "clientid": params["clientid"]}
err := collection.FindOne(ctx, filter, options.FindOne().SetProjection(bson.M{"_id": 0})).Decode(&oldExtClient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
success, err := DeleteExtClient(params["network"], params["clientid"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
} else if !success {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
oldExtClient.ClientID = newExtClient.ClientID
CreateExtClient(oldExtClient)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(oldExtClient)
}
func DeleteExtClient(network string, clientid string) (bool, error) {
deleted := false
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
filter := bson.M{"network": network, "clientid": clientid}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
result, err := collection.DeleteOne(ctx, filter)
deletecount := result.DeletedCount
if deletecount > 0 {
deleted = true
}
defer cancel()
fmt.Println("Deleted extclient client " + clientid + " from network " + network)
return deleted, err
}
//Delete a extclient
//Pretty straightforward
func deleteExtClient(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
// get params
var params = mux.Vars(r)
success, err := DeleteExtClient(params["network"], params["clientid"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
} else if !success {
err = errors.New("Could not delete extclient " + params["clientid"])
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
returnSuccessResponse(w, r, params["clientid"]+" deleted.")
}

View file

@ -347,8 +347,8 @@ func (s *NodeServiceServer) GetPeers(req *nodepb.GetPeersReq, stream nodepb.Node
Address: peers[i].Address,
Address6: peers[i].Address6,
Endpoint: peers[i].Endpoint,
Gatewayrange: peers[i].GatewayRange,
Isgateway: peers[i].IsGateway,
Egressgatewayrange: peers[i].EgressGatewayRange,
Isegressgateway: peers[i].IsEgressGateway,
Publickey: peers[i].PublicKey,
Keepalive: peers[i].KeepAlive,
Listenport: peers[i].ListenPort,
@ -369,3 +369,43 @@ func (s *NodeServiceServer) GetPeers(req *nodepb.GetPeersReq, stream nodepb.Node
return nil
}
func (s *NodeServiceServer) GetExtPeers(req *nodepb.GetExtPeersReq, stream nodepb.NodeService_GetExtPeersServer) error {
// Initiate a NodeItem type to write decoded data to
//data := &models.PeersResponse{}
// collection.Find returns a cursor for our (empty) query
//cursor, err := s.NodeDB.Find(context.Background(), bson.M{})
peers, err := GetExtPeersList(req.GetNetwork(), req.GetMacaddress())
if err != nil {
return status.Errorf(codes.Internal, fmt.Sprintf("Unknown internal error: %v", err))
}
// cursor.Next() returns a boolean, if false there are no more items and loop will break
for i := 0; i < len(peers); i++ {
// If no error is found send node over stream
stream.Send(&nodepb.GetExtPeersRes{
Extpeers: &nodepb.ExtPeersResponse{
Address: peers[i].Address,
Address6: peers[i].Address6,
Endpoint: peers[i].Endpoint,
Publickey: peers[i].PublicKey,
Keepalive: peers[i].KeepAlive,
Listenport: peers[i].ListenPort,
Localaddress: peers[i].LocalAddress,
},
})
}
node, err := functions.GetNodeByMacAddress(req.GetNetwork(), req.GetMacaddress())
if err != nil {
return status.Errorf(codes.Internal, fmt.Sprintf("Could not get node: %v", err))
}
err = TimestampNode(node, false, true, false)
if err != nil {
return status.Errorf(codes.Internal, fmt.Sprintf("Internal error occurred: %v", err))
}
return nil
}

View file

@ -26,10 +26,10 @@ func nodeHandlers(r *mux.Router) {
r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{macaddress}/checkin", authorize(true, "node", http.HandlerFunc(checkIn))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "master", http.HandlerFunc(createGateway))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "master", http.HandlerFunc(deleteGateway))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(http.HandlerFunc(createIngress))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(http.HandlerFunc(deleteIngress))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "master", http.HandlerFunc(createEgressGateway))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "master", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(http.HandlerFunc(createIngressGateway))).Methods("POST")
r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).Methods("POST")
r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
@ -526,8 +526,8 @@ func UncordonNode(network, macaddress string) (models.Node, error) {
return node, nil
}
func createGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.GatewayRequest
func createEgressGateway(w http.ResponseWriter, r *http.Request) {
var gateway models.EgressGatewayRequest
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
err := json.NewDecoder(r.Body).Decode(&gateway)
@ -537,7 +537,7 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
}
gateway.NetID = params["network"]
gateway.NodeID = params["macaddress"]
node, err := CreateGateway(gateway)
node, err := CreateEgressGateway(gateway)
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -546,18 +546,18 @@ func createGateway(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(node)
}
func CreateGateway(gateway models.GatewayRequest) (models.Node, error) {
func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
node, err := functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
if err != nil {
return models.Node{}, err
}
err = ValidateGateway(gateway)
err = ValidateEgressGateway(gateway)
if err != nil {
return models.Node{}, err
}
var nodechange models.Node
nodechange.IsGateway = true
nodechange.GatewayRange = gateway.RangeString
nodechange.IsEgressGateway = true
nodechange.EgressGatewayRange = gateway.RangeString
if gateway.PostUp == "" {
nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
} else {
@ -579,8 +579,8 @@ func CreateGateway(gateway models.GatewayRequest) (models.Node, error) {
{"$set", bson.D{
{"postup", nodechange.PostUp},
{"postdown", nodechange.PostDown},
{"isgateway", nodechange.IsGateway},
{"gatewayrange", nodechange.GatewayRange},
{"isgateway", nodechange.IsEgressGateway},
{"gatewayrange", nodechange.EgressGatewayRange},
{"lastmodified", nodechange.LastModified},
}},
}
@ -602,7 +602,7 @@ func CreateGateway(gateway models.GatewayRequest) (models.Node, error) {
return node, nil
}
func ValidateGateway(gateway models.GatewayRequest) error {
func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
var err error
isIp := functions.IsIpCIDR(gateway.RangeString)
empty := gateway.RangeString == ""
@ -616,10 +616,10 @@ func ValidateGateway(gateway models.GatewayRequest) error {
return err
}
func deleteGateway(w http.ResponseWriter, r *http.Request) {
func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
node, err := DeleteGateway(params["network"], params["macaddress"])
node, err := DeleteEgressGateway(params["network"], params["macaddress"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -628,7 +628,7 @@ func deleteGateway(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(node)
}
func DeleteGateway(network, macaddress string) (models.Node, error) {
func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
var nodeupdate models.Node
var nodechange models.Node
@ -637,8 +637,8 @@ func DeleteGateway(network, macaddress string) (models.Node, error) {
return models.Node{}, err
}
nodechange.IsGateway = false
nodechange.GatewayRange = ""
nodechange.IsEgressGateway = false
nodechange.EgressGatewayRange = ""
nodechange.PostUp = ""
nodechange.PostDown = ""
@ -652,8 +652,8 @@ func DeleteGateway(network, macaddress string) (models.Node, error) {
{"$set", bson.D{
{"postup", nodechange.PostUp},
{"postdown", nodechange.PostDown},
{"isgateway", nodechange.IsGateway},
{"gatewayrange", nodechange.GatewayRange},
{"isgateway", nodechange.IsEgressGateway},
{"gatewayrange", nodechange.EgressGatewayRange},
{"lastmodified", nodechange.LastModified},
}},
}
@ -674,10 +674,10 @@ func DeleteGateway(network, macaddress string) (models.Node, error) {
return node, nil
}
// == INGRESS ==
func createIngress(w http.ResponseWriter, r *http.Request) {
func createIngressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json")
node, err := CreateIngress(params["network"], params["macaddress"])
node, err := CreateIngressGateway(params["network"], params["macaddress"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -686,7 +686,7 @@ func createIngress(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(node)
}
func CreateIngress(network string, macaddress string) (models.Node, error) {
func CreateIngressGateway(network string, macaddress string) (models.Node, error) {
node, err := functions.GetNodeByMacAddress(network, macaddress)
if err != nil {
return models.Node{}, err
@ -717,10 +717,10 @@ func CreateIngress(network string, macaddress string) (models.Node, error) {
return node, nil
}
func deleteIngress(w http.ResponseWriter, r *http.Request) {
func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
node, err := DeleteIngress(params["network"], params["macaddress"])
node, err := DeleteIngressGateway(params["network"], params["macaddress"])
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
return
@ -729,7 +729,7 @@ func deleteIngress(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(node)
}
func DeleteIngress(network, macaddress string) (models.Node, error) {
func DeleteIngressGateway(network, macaddress string) (models.Node, error) {
var nodeupdate models.Node
node, err := functions.GetNodeByMacAddress(network, macaddress)

View file

@ -481,6 +481,34 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error)
return node, nil
}
func GetAllExtClients() ([]models.ExtClient, error) {
var extclient models.ExtClient
var extclients []models.ExtClient
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// Filter out them ID's again
cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
if err != nil {
return []models.ExtClient{}, err
}
defer cancel()
for cur.Next(context.TODO()) {
err := cur.Decode(&extclient)
if err != nil {
return []models.ExtClient{}, err
}
// add node to our array
extclients = append(extclients, extclient)
}
//TODO: Fatal error
if err := cur.Err(); err != nil {
return []models.ExtClient{}, err
}
return extclients, nil
}
//This returns a unique address for a node to use
//it iterates through the list of IP's in the subnet
//and checks against all nodes to see if it's taken, until it finds one.
@ -506,7 +534,7 @@ func UniqueAddress(networkName string) (string, error) {
offset = false
continue
}
if IsIPUnique(networkName, ip.String()) {
if IsIPUnique(networkName, ip.String()) && IsIPUniqueExtClients(networkName, ip.String()) {
return ip.String(), err
}
}
@ -582,6 +610,33 @@ func GenKeyName() string {
return "key" + string(b)
}
func IsIPUniqueExtClients(network string, ip string) bool {
var extclient models.ExtClient
isunique := true
collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"address": ip, "network": network}
err := collection.FindOne(ctx, filter).Decode(&extclient)
defer cancel()
if err != nil {
fmt.Println(err)
return isunique
}
if extclient.Address == ip {
isunique = false
}
return isunique
}
//checks if IP is unique in the address range
//used by UniqueAddress
func IsIPUnique(network string, ip string) bool {
@ -749,30 +804,3 @@ func GetAllNodes() ([]models.ReturnNode, error) {
return nodes, nil
}
func GetAllExternals() ([]models.ReturnNode, error) {
var node models.ReturnNode
var nodes []models.ReturnNode
collection := mongoconn.Client.Database("netmaker").Collection("nodes")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// Filter out them ID's again
cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0}))
if err != nil {
return []models.ReturnNode{}, err
}
defer cancel()
for cur.Next(context.TODO()) {
err := cur.Decode(&node)
if err != nil {
return []models.ReturnNode{}, err
}
// add node to our array
nodes = append(nodes, node)
}
//TODO: Fatal error
if err := cur.Err(); err != nil {
return []models.ReturnNode{}, err
}
return nodes, nil
}

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@ service NodeService {
rpc UpdateNode(UpdateNodeReq) returns (UpdateNodeRes);
rpc DeleteNode(DeleteNodeReq) returns (DeleteNodeRes);
rpc GetPeers(GetPeersReq) returns (stream GetPeersRes);
rpc GetExtPeers(GetExtPeersReq) returns (stream GetExtPeersRes);
rpc CheckIn(CheckInReq) returns (CheckInRes);
}
@ -45,6 +46,7 @@ message Node {
string postchanges = 21;
string allowedips = 22;
bool islocal = 23;
bool isingressgateway = 28;
bool isdualstack = 27;
bool dnsoff = 24;
string localrange = 25;
@ -61,8 +63,18 @@ message CheckInResponse {
}
message PeersResponse {
bool isgateway = 1;
string gatewayrange = 2;
bool isegressgateway = 1;
string egressgatewayrange = 2;
string publickey = 5;
string endpoint = 6;
string address = 3;
string address6 = 8;
int32 listenport = 4;
string localaddress = 7;
int32 keepalive = 13;
}
message ExtPeersResponse {
string publickey = 5;
string endpoint = 6;
string address = 3;
@ -111,10 +123,19 @@ message GetPeersReq {
string network = 2;
}
message GetExtPeersReq {
string macaddress = 1;
string network = 2;
}
message GetPeersRes {
PeersResponse peers = 1;
}
message GetExtPeersRes {
ExtPeersResponse extpeers = 1;
}
message CheckInReq {
Node node = 1;
// bool postchanges = 2;

View file

@ -24,6 +24,7 @@ type NodeServiceClient interface {
UpdateNode(ctx context.Context, in *UpdateNodeReq, opts ...grpc.CallOption) (*UpdateNodeRes, error)
DeleteNode(ctx context.Context, in *DeleteNodeReq, opts ...grpc.CallOption) (*DeleteNodeRes, error)
GetPeers(ctx context.Context, in *GetPeersReq, opts ...grpc.CallOption) (NodeService_GetPeersClient, error)
GetExtPeers(ctx context.Context, in *GetExtPeersReq, opts ...grpc.CallOption) (NodeService_GetExtPeersClient, error)
CheckIn(ctx context.Context, in *CheckInReq, opts ...grpc.CallOption) (*CheckInRes, error)
}
@ -112,6 +113,38 @@ func (x *nodeServiceGetPeersClient) Recv() (*GetPeersRes, error) {
return m, nil
}
func (c *nodeServiceClient) GetExtPeers(ctx context.Context, in *GetExtPeersReq, opts ...grpc.CallOption) (NodeService_GetExtPeersClient, error) {
stream, err := c.cc.NewStream(ctx, &NodeService_ServiceDesc.Streams[1], "/node.NodeService/GetExtPeers", opts...)
if err != nil {
return nil, err
}
x := &nodeServiceGetExtPeersClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type NodeService_GetExtPeersClient interface {
Recv() (*GetExtPeersRes, error)
grpc.ClientStream
}
type nodeServiceGetExtPeersClient struct {
grpc.ClientStream
}
func (x *nodeServiceGetExtPeersClient) Recv() (*GetExtPeersRes, error) {
m := new(GetExtPeersRes)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *nodeServiceClient) CheckIn(ctx context.Context, in *CheckInReq, opts ...grpc.CallOption) (*CheckInRes, error) {
out := new(CheckInRes)
err := c.cc.Invoke(ctx, "/node.NodeService/CheckIn", in, out, opts...)
@ -131,6 +164,7 @@ type NodeServiceServer interface {
UpdateNode(context.Context, *UpdateNodeReq) (*UpdateNodeRes, error)
DeleteNode(context.Context, *DeleteNodeReq) (*DeleteNodeRes, error)
GetPeers(*GetPeersReq, NodeService_GetPeersServer) error
GetExtPeers(*GetExtPeersReq, NodeService_GetExtPeersServer) error
CheckIn(context.Context, *CheckInReq) (*CheckInRes, error)
mustEmbedUnimplementedNodeServiceServer()
}
@ -157,6 +191,9 @@ func (UnimplementedNodeServiceServer) DeleteNode(context.Context, *DeleteNodeReq
func (UnimplementedNodeServiceServer) GetPeers(*GetPeersReq, NodeService_GetPeersServer) error {
return status.Errorf(codes.Unimplemented, "method GetPeers not implemented")
}
func (UnimplementedNodeServiceServer) GetExtPeers(*GetExtPeersReq, NodeService_GetExtPeersServer) error {
return status.Errorf(codes.Unimplemented, "method GetExtPeers not implemented")
}
func (UnimplementedNodeServiceServer) CheckIn(context.Context, *CheckInReq) (*CheckInRes, error) {
return nil, status.Errorf(codes.Unimplemented, "method CheckIn not implemented")
}
@ -284,6 +321,27 @@ func (x *nodeServiceGetPeersServer) Send(m *GetPeersRes) error {
return x.ServerStream.SendMsg(m)
}
func _NodeService_GetExtPeers_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(GetExtPeersReq)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(NodeServiceServer).GetExtPeers(m, &nodeServiceGetExtPeersServer{stream})
}
type NodeService_GetExtPeersServer interface {
Send(*GetExtPeersRes) error
grpc.ServerStream
}
type nodeServiceGetExtPeersServer struct {
grpc.ServerStream
}
func (x *nodeServiceGetExtPeersServer) Send(m *GetExtPeersRes) error {
return x.ServerStream.SendMsg(m)
}
func _NodeService_CheckIn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CheckInReq)
if err := dec(in); err != nil {
@ -340,6 +398,11 @@ var NodeService_ServiceDesc = grpc.ServiceDesc{
Handler: _NodeService_GetPeers_Handler,
ServerStreams: true,
},
{
StreamName: "GetExtPeers",
Handler: _NodeService_GetExtPeers_Handler,
ServerStreams: true,
},
},
Metadata: "grpc/node.proto",
}

45
models/extclient.go Normal file
View file

@ -0,0 +1,45 @@
package models
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
//What the client needs to get
/*
[Interface]
# The address their computer will use on the network
Address = 10.0.0.8/32 # The Address they'll use on the network
PrivateKey = XXXXXXXXXXXXXXXX # The private key they'll use
# All of this info can come from the node!!
[Peer]
# Ingress Gateway's wireguard public key
PublicKey = CcZHeaO08z55/x3FXdsSGmOQvZG32SvHlrwHnsWlGTs=
# Public IP address of the Ingress Gateway
# Use the floating IP address if you created one for your VPN server
Endpoint = 123.123.123.123:51820
# 10.0.0.0/24 is the VPN sub
*/
// External Struct
// == BACKEND FIELDS ==
// PrivateKey, PublicKey, Address (Private), LastModified, IngressEndpoint
// == FRONTEND FIELDS ==
// ClientID, Network, IngressGateway
type ExtClient struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
ClientID string `json:"clientid" bson:"clientid"`
Description string `json:"description" bson:"description"`
PrivateKey string `json:"privatekey" bson:"privatekey"`
PublicKey string `json:"publickey" bson:"publickey"`
Network string `json:"network" bson:"network"`
Address string `json:"address" bson:"address"`
LastModified int64 `json:"lastmodified" bson:"lastmodified"`
IngressGatewayID string `json:"ingressgatewayid" bson:"ingressgatewayid"`
IngressGatewayEnpoint string `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
}

View file

@ -1,18 +0,0 @@
package models
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
//External Struct
//At some point, need to replace all instances of Name with something else like Identifier
type External struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
ClientID string `json:"clientid" bson:"clientid"`
PrivateKey string `json:"privatekey" bson:"privatekey"`
PublicKey string `json:"publickey" bson:"publickey"`
Network string `json:"network" bson:"network"`
Address string `json:"address" bson:"address"`
LastModified string `json:"lastmodified" bson:"lastmodified"`
IngressGateway string `json:"ingressgateway" bson:"ingressgateway"`
}

View file

@ -43,8 +43,9 @@ type Node struct {
Password string `json:"password" bson:"password" validate:"required,min=6"`
Network string `json:"network" bson:"network" validate:"network_exists"`
IsPending bool `json:"ispending" bson:"ispending"`
IsGateway bool `json:"isgateway" bson:"isgateway"`
GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
IsEgressGateway bool `json:"isegressgateway" bson:"isegressgateway"`
IsIngressGateway bool `json:"isingressgateway" bson:"isingressgateway"`
EgressGatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
PostChanges string `json:"postchanges" bson:"postchanges"`
}
@ -75,8 +76,9 @@ type NodeUpdate struct {
Password string `json:"password" bson:"password" validate:"omitempty,min=5"`
Network string `json:"network" bson:"network" validate:"network_exists"`
IsPending bool `json:"ispending" bson:"ispending"`
IsGateway bool `json:"isgateway" bson:"isgateway"`
GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
IsIngressGateway bool `json:"isingressgateway" bson:"isingressgateway"`
IsEgressGateway bool `json:"isegressgateway" bson:"isegressgateway"`
EgressGatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
PostChanges string `json:"postchanges" bson:"postchanges"`
}

View file

@ -19,8 +19,9 @@ type ReturnNode struct {
Interface string `json:"interface" bson:"interface"`
Network string `json:"network" bson:"network"`
IsPending *bool `json:"ispending" bson:"ispending"`
IsGateway *bool `json:"isgateway" bson:"isgateway"`
GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
IsEgressGateway *bool `json:"isegressgateway" bson:"isegressgateway"`
IsIngressGateway *bool `json:"isingressgateway" bson:"isingressgateway"`
EgressGatewayRange string `json:"egressgatewayrange" bson:"egressgatewayrange"`
LocalAddress string `json:"localaddress" bson:"localaddress" validate:"localaddress_check"`
ExpirationDateTime int64 `json:"expdatetime" bson:"expdatetime"`
}

View file

@ -95,13 +95,23 @@ type PeersResponse struct {
Address string `json:"address" bson:"address"`
Address6 string `json:"address6" bson:"address6"`
LocalAddress string `json:"localaddress" bson:"localaddress"`
IsGateway bool `json:"isgateway" bson:"isgateway"`
GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
IsEgressGateway bool `json:"isgateway" bson:"isgateway"`
EgressGatewayRange string `json:"gatewayrange" bson:"gatewayrange"`
ListenPort int32 `json:"listenport" bson:"listenport"`
KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"`
}
type GatewayRequest struct {
type ExtPeersResponse struct {
PublicKey string `json:"publickey" bson:"publickey"`
Endpoint string `json:"endpoint" bson:"endpoint"`
Address string `json:"address" bson:"address"`
Address6 string `json:"address6" bson:"address6"`
LocalAddress string `json:"localaddress" bson:"localaddress"`
ListenPort int32 `json:"listenport" bson:"listenport"`
KeepAlive int32 `json:"persistentkeepalive" bson:"persistentkeepalive"`
}
type EgressGatewayRequest struct {
NodeID string `json:"nodeid" bson:"nodeid"`
NetID string `json:"netid" bson:"netid"`
RangeString string `json:"rangestring" bson:"rangestring"`

View file

@ -2,12 +2,10 @@ package functions
import (
"fmt"
"time"
"errors"
"context"
"net/http"
"io/ioutil"
"io"
"strings"
"log"
"net"
@ -454,7 +452,7 @@ func Install(accesskey string, password string, server string, network string, n
}
}
peers, hasGateway, gateways, err := getPeers(node.Macaddress, network, server, node.Isdualstack)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, network, server, node.Isdualstack, node.Isingressgateway)
if err != nil {
return err
@ -931,7 +929,7 @@ func setWGConfig(network string) error {
nodecfg := cfg.Node
node := getNode(network)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, nodecfg.Network, servercfg.Address, node.Isdualstack)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, nodecfg.Network, servercfg.Address, node.Isdualstack, node.Isingressgateway)
if err != nil {
return err
}
@ -1451,138 +1449,3 @@ func DeleteInterface(ifacename string, postdown string) error{
}
return err
}
func getPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, bool, []string, error) {
//need to implement checkin on server side
hasGateway := false
var gateways []string
var peers []wgtypes.PeerConfig
var wcclient nodepb.NodeServiceClient
cfg, err := config.ReadConfig(network)
if err != nil {
log.Fatalf("Issue retrieving config for network: " + network + ". Please investigate: %v", err)
}
nodecfg := cfg.Node
keepalive := nodecfg.KeepAlive
keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
if err != nil {
log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err)
}
fmt.Println("Registering with GRPC Server")
requestOpts := grpc.WithInsecure()
conn, err := grpc.Dial(server, requestOpts)
if err != nil {
log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
}
// Instantiate the BlogServiceClient with our client connection to the server
wcclient = nodepb.NewNodeServiceClient(conn)
req := &nodepb.GetPeersReq{
Macaddress: macaddress,
Network: network,
}
ctx := context.Background()
fmt.Println("Authenticating with GRPC Server")
ctx, err = SetJWT(wcclient, network)
if err != nil {
fmt.Println("Failed to authenticate.")
return peers, hasGateway, gateways, err
}
var header metadata.MD
stream, err := wcclient.GetPeers(ctx, req, grpc.Header(&header))
if err != nil {
fmt.Println("Error retrieving peers")
fmt.Println(err)
return nil, hasGateway, gateways, err
}
fmt.Println("Parsing peers response")
for {
res, err := stream.Recv()
// If end of stream, break the loop
if err == io.EOF {
break
}
// if err, return an error
if err != nil {
if strings.Contains(err.Error(), "mongo: no documents in result") {
continue
} else {
fmt.Println("ERROR ENCOUNTERED WITH RESPONSE")
fmt.Println(res)
return peers, hasGateway, gateways, err
}
}
pubkey, err := wgtypes.ParseKey(res.Peers.Publickey)
if err != nil {
fmt.Println("error parsing key")
return peers, hasGateway, gateways, err
}
if nodecfg.PublicKey == res.Peers.Publickey {
fmt.Println("Peer is self. Skipping")
continue
}
if nodecfg.Endpoint == res.Peers.Endpoint {
fmt.Println("Peer is self. Skipping")
continue
}
var peer wgtypes.PeerConfig
var peeraddr = net.IPNet{
IP: net.ParseIP(res.Peers.Address),
Mask: net.CIDRMask(32, 32),
}
var allowedips []net.IPNet
allowedips = append(allowedips, peeraddr)
if res.Peers.Isgateway {
hasGateway = true
gateways = append(gateways,res.Peers.Gatewayrange)
_, ipnet, err := net.ParseCIDR(res.Peers.Gatewayrange)
if err != nil {
fmt.Println("ERROR ENCOUNTERED SETTING GATEWAY")
fmt.Println("NOT SETTING GATEWAY")
fmt.Println(err)
} else {
fmt.Println(" Gateway Range: " + res.Peers.Gatewayrange)
allowedips = append(allowedips, *ipnet)
}
}
if res.Peers.Address6 != "" && dualstack {
var addr6 = net.IPNet{
IP: net.ParseIP(res.Peers.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, addr6)
}
if keepalive != 0 {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
PersistentKeepaliveInterval: &keepalivedur,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Peers.Endpoint),
Port: int(res.Peers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
} else {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Peers.Endpoint),
Port: int(res.Peers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
}
peers = append(peers, peer)
}
fmt.Println("Finished parsing peers response")
return peers, hasGateway, gateways, err
}

View file

@ -0,0 +1,283 @@
package functions
import (
"fmt"
"time"
"context"
"io"
"strings"
"log"
"net"
"strconv"
"github.com/gravitl/netmaker/netclient/config"
nodepb "github.com/gravitl/netmaker/grpc"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
//homedir "github.com/mitchellh/go-homedir"
)
func getPeers(macaddress string, network string, server string, dualstack bool, isIngressGateway bool) ([]wgtypes.PeerConfig, bool, []string, error) {
//need to implement checkin on server side
hasGateway := false
var gateways []string
var peers []wgtypes.PeerConfig
var wcclient nodepb.NodeServiceClient
cfg, err := config.ReadConfig(network)
if err != nil {
log.Fatalf("Issue retrieving config for network: " + network + ". Please investigate: %v", err)
}
nodecfg := cfg.Node
keepalive := nodecfg.KeepAlive
keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
if err != nil {
log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err)
}
fmt.Println("Registering with GRPC Server")
requestOpts := grpc.WithInsecure()
conn, err := grpc.Dial(server, requestOpts)
if err != nil {
log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
}
// Instantiate the BlogServiceClient with our client connection to the server
wcclient = nodepb.NewNodeServiceClient(conn)
req := &nodepb.GetPeersReq{
Macaddress: macaddress,
Network: network,
}
ctx := context.Background()
fmt.Println("Authenticating with GRPC Server")
ctx, err = SetJWT(wcclient, network)
if err != nil {
fmt.Println("Failed to authenticate.")
return peers, hasGateway, gateways, err
}
var header metadata.MD
stream, err := wcclient.GetPeers(ctx, req, grpc.Header(&header))
if err != nil {
fmt.Println("Error retrieving peers")
fmt.Println(err)
return nil, hasGateway, gateways, err
}
fmt.Println("Parsing peers response")
for {
res, err := stream.Recv()
// If end of stream, break the loop
if err == io.EOF {
break
}
// if err, return an error
if err != nil {
if strings.Contains(err.Error(), "mongo: no documents in result") {
continue
} else {
fmt.Println("ERROR ENCOUNTERED WITH RESPONSE")
fmt.Println(res)
return peers, hasGateway, gateways, err
}
}
pubkey, err := wgtypes.ParseKey(res.Peers.Publickey)
if err != nil {
fmt.Println("error parsing key")
return peers, hasGateway, gateways, err
}
if nodecfg.PublicKey == res.Peers.Publickey {
fmt.Println("Peer is self. Skipping")
continue
}
if nodecfg.Endpoint == res.Peers.Endpoint {
fmt.Println("Peer is self. Skipping")
continue
}
var peer wgtypes.PeerConfig
var peeraddr = net.IPNet{
IP: net.ParseIP(res.Peers.Address),
Mask: net.CIDRMask(32, 32),
}
var allowedips []net.IPNet
allowedips = append(allowedips, peeraddr)
if res.Peers.Isegressgateway {
hasGateway = true
gateways = append(gateways,res.Peers.Egressgatewayrange)
_, ipnet, err := net.ParseCIDR(res.Peers.Egressgatewayrange)
if err != nil {
fmt.Println("ERROR ENCOUNTERED SETTING GATEWAY")
fmt.Println("NOT SETTING GATEWAY")
fmt.Println(err)
} else {
fmt.Println(" Gateway Range: " + res.Peers.Egressgatewayrange)
allowedips = append(allowedips, *ipnet)
}
}
if res.Peers.Address6 != "" && dualstack {
var addr6 = net.IPNet{
IP: net.ParseIP(res.Peers.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, addr6)
}
if keepalive != 0 {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
PersistentKeepaliveInterval: &keepalivedur,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Peers.Endpoint),
Port: int(res.Peers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
} else {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Peers.Endpoint),
Port: int(res.Peers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
}
peers = append(peers, peer)
}
if isIngressGateway {
fmt.Println("Adding external peers...")
extPeers, err := getExtPeers(macaddress, network, server, dualstack)
if err == nil {
peers = append(peers, extPeers...)
fmt.Println("Added " + strconv.Itoa(len(extPeers)) + " external clients.")
} else {
fmt.Println("ERROR RETRIEVING EXTERNAL PEERS")
fmt.Println(err)
}
}
fmt.Println("Finished parsing peers response")
return peers, hasGateway, gateways, err
}
func getExtPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, error) {
var peers []wgtypes.PeerConfig
var wcclient nodepb.NodeServiceClient
cfg, err := config.ReadConfig(network)
if err != nil {
log.Fatalf("Issue retrieving config for network: " + network + ". Please investigate: %v", err)
}
nodecfg := cfg.Node
keepalive := nodecfg.KeepAlive
keepalivedur, err := time.ParseDuration(strconv.FormatInt(int64(keepalive), 10) + "s")
if err != nil {
log.Fatalf("Issue with format of keepalive value. Please update netconfig: %v", err)
}
fmt.Println("Registering with GRPC Server")
requestOpts := grpc.WithInsecure()
conn, err := grpc.Dial(server, requestOpts)
if err != nil {
log.Fatalf("Unable to establish client connection to localhost:50051: %v", err)
}
// Instantiate the BlogServiceClient with our client connection to the server
wcclient = nodepb.NewNodeServiceClient(conn)
req := &nodepb.GetExtPeersReq{
Macaddress: macaddress,
Network: network,
}
ctx := context.Background()
fmt.Println("Authenticating with GRPC Server")
ctx, err = SetJWT(wcclient, network)
if err != nil {
fmt.Println("Failed to authenticate.")
return peers, err
}
var header metadata.MD
stream, err := wcclient.GetExtPeers(ctx, req, grpc.Header(&header))
if err != nil {
fmt.Println("Error retrieving peers")
fmt.Println(err)
return nil, err
}
fmt.Println("Parsing peers response")
for {
res, err := stream.Recv()
// If end of stream, break the loop
if err == io.EOF {
break
}
// if err, return an error
if err != nil {
if strings.Contains(err.Error(), "mongo: no documents in result") {
continue
} else {
fmt.Println("ERROR ENCOUNTERED WITH RESPONSE")
fmt.Println(res)
return peers, err
}
}
pubkey, err := wgtypes.ParseKey(res.Extpeers.Publickey)
if err != nil {
fmt.Println("error parsing key")
return peers, err
}
if nodecfg.PublicKey == res.Extpeers.Publickey {
fmt.Println("Peer is self. Skipping")
continue
}
if nodecfg.Endpoint == res.Extpeers.Endpoint {
fmt.Println("Peer is self. Skipping")
continue
}
var peer wgtypes.PeerConfig
var peeraddr = net.IPNet{
IP: net.ParseIP(res.Extpeers.Address),
Mask: net.CIDRMask(32, 32),
}
var allowedips []net.IPNet
allowedips = append(allowedips, peeraddr)
if res.Extpeers.Address6 != "" && dualstack {
var addr6 = net.IPNet{
IP: net.ParseIP(res.Extpeers.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, addr6)
}
if keepalive != 0 {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
PersistentKeepaliveInterval: &keepalivedur,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Extpeers.Endpoint),
Port: int(res.Extpeers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
} else {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,
Endpoint: &net.UDPAddr{
IP: net.ParseIP(res.Extpeers.Endpoint),
Port: int(res.Extpeers.Listenport),
},
ReplaceAllowedIPs: true,
AllowedIPs: allowedips,
}
}
peers = append(peers, peer)
}
return peers, err
}