mirror of
https://github.com/gravitl/netmaker.git
synced 2024-09-20 23:36:18 +08:00
adding extclient functionality
This commit is contained in:
parent
5662a1538e
commit
29e332edf7
|
@ -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 {
|
||||
|
|
|
@ -31,6 +31,8 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
|
|||
dnsHandlers(r)
|
||||
fileHandlers(r)
|
||||
serverHandlers(r)
|
||||
extClientHandlers(r)
|
||||
|
||||
|
||||
port := servercfg.GetAPIPort()
|
||||
|
||||
|
|
413
controllers/extClientHttpController.go
Normal file
413
controllers/extClientHttpController.go
Normal 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.")
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
2239
grpc/node.pb.go
2239
grpc/node.pb.go
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
|
|
@ -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
45
models/extclient.go
Normal 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"`
|
||||
}
|
|
@ -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"`
|
||||
}
|
|
@ -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"`
|
||||
}
|
||||
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
283
netclient/functions/peers.go
Normal file
283
netclient/functions/peers.go
Normal 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
|
||||
}
|
Loading…
Reference in a new issue