|
@@ -3,7 +3,6 @@ package controller
|
|
import (
|
|
import (
|
|
"context"
|
|
"context"
|
|
"encoding/json"
|
|
"encoding/json"
|
|
- "errors"
|
|
|
|
"fmt"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http"
|
|
"strings"
|
|
"strings"
|
|
@@ -35,7 +34,6 @@ func nodeHandlers(r *mux.Router) {
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
|
|
- r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods(http.MethodPost)
|
|
|
|
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -147,52 +145,6 @@ func authenticate(response http.ResponseWriter, request *http.Request) {
|
|
response.Write(successJSONResponse)
|
|
response.Write(successJSONResponse)
|
|
}
|
|
}
|
|
|
|
|
|
-// auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
|
|
|
|
-func nodeauth(next http.Handler) http.HandlerFunc {
|
|
|
|
- return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
- bearerToken := r.Header.Get("Authorization")
|
|
|
|
- var tokenSplit = strings.Split(bearerToken, " ")
|
|
|
|
- var token = ""
|
|
|
|
- if len(tokenSplit) < 2 {
|
|
|
|
- errorResponse := models.ErrorResponse{
|
|
|
|
- Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
|
|
|
|
- }
|
|
|
|
- logic.ReturnErrorResponse(w, r, errorResponse)
|
|
|
|
- return
|
|
|
|
- } else {
|
|
|
|
- token = tokenSplit[1]
|
|
|
|
- }
|
|
|
|
- found := false
|
|
|
|
- networks, err := logic.GetNetworks()
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, "no networks", err.Error())
|
|
|
|
- errorResponse := models.ErrorResponse{
|
|
|
|
- Code: http.StatusNotFound, Message: "no networks",
|
|
|
|
- }
|
|
|
|
- logic.ReturnErrorResponse(w, r, errorResponse)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- for _, network := range networks {
|
|
|
|
- for _, key := range network.AccessKeys {
|
|
|
|
- if key.Value == token {
|
|
|
|
- found = true
|
|
|
|
- break
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if !found {
|
|
|
|
- logger.Log(0, "valid access key not found")
|
|
|
|
- errorResponse := models.ErrorResponse{
|
|
|
|
- Code: http.StatusUnauthorized, Message: "You are unauthorized to access this endpoint.",
|
|
|
|
- }
|
|
|
|
- logic.ReturnErrorResponse(w, r, errorResponse)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- next.ServeHTTP(w, r)
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// The middleware for most requests to the API
|
|
// The middleware for most requests to the API
|
|
// They all pass through here first
|
|
// They all pass through here first
|
|
// This will validate the JWT (or check for master token)
|
|
// This will validate the JWT (or check for master token)
|
|
@@ -462,218 +414,6 @@ func getNode(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(response)
|
|
json.NewEncoder(w).Encode(response)
|
|
}
|
|
}
|
|
|
|
|
|
-// swagger:route POST /api/nodes/{network} nodes createNode
|
|
|
|
-//
|
|
|
|
-// Create a node on a network.
|
|
|
|
-//
|
|
|
|
-// Schemes: https
|
|
|
|
-//
|
|
|
|
-// Security:
|
|
|
|
-// oauth
|
|
|
|
-//
|
|
|
|
-// Responses:
|
|
|
|
-// 200: nodeGetResponse
|
|
|
|
-func createNode(w http.ResponseWriter, r *http.Request) {
|
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
|
-
|
|
|
|
- var params = mux.Vars(r)
|
|
|
|
-
|
|
|
|
- var errorResponse = models.ErrorResponse{
|
|
|
|
- Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
|
|
|
|
- }
|
|
|
|
- networkName := params["network"]
|
|
|
|
- networkexists, err := logic.NetworkExists(networkName)
|
|
|
|
-
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("failed to fetch network [%s] info: %v", networkName, err))
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- } else if !networkexists {
|
|
|
|
- errorResponse = models.ErrorResponse{
|
|
|
|
- Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ",
|
|
|
|
- }
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("network [%s] does not exist", networkName))
|
|
|
|
- logic.ReturnErrorResponse(w, r, errorResponse)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- //get data from body of request
|
|
|
|
- data := models.JoinData{}
|
|
|
|
- err = json.NewDecoder(r.Body).Decode(&data)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if !logic.IsVersionComptatible(data.Host.Version) {
|
|
|
|
- err := errors.New("incompatible netclient version")
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- data.Node.Network = networkName
|
|
|
|
-
|
|
|
|
- networkSettings, err := logic.GetNetworkSettings(networkName)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("failed to get network [%s] settings: %v", networkName, err))
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- data.Node.NetworkSettings(networkSettings)
|
|
|
|
- keyName, validKey := logic.IsKeyValid(networkName, data.Key)
|
|
|
|
- if !validKey {
|
|
|
|
- errorResponse = models.ErrorResponse{
|
|
|
|
- Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
|
|
|
|
- }
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("failed to create node on network [%s]: %s",
|
|
|
|
- data.Node.Network, errorResponse.Message))
|
|
|
|
- logic.ReturnErrorResponse(w, r, errorResponse)
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- logic.DecrimentKey(networkName, data.Key)
|
|
|
|
- user, err := pro.GetNetworkUser(networkName, promodels.NetworkUserID(keyName))
|
|
|
|
- if err == nil {
|
|
|
|
- if user.ID != "" {
|
|
|
|
- logger.Log(1, "associating new node with user", keyName)
|
|
|
|
- data.Node.OwnerID = string(user.ID)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- key, keyErr := logic.RetrievePublicTrafficKey()
|
|
|
|
- if keyErr != nil {
|
|
|
|
- logger.Log(0, "error retrieving key: ", keyErr.Error())
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- if key == nil {
|
|
|
|
- logger.Log(0, "error: server traffic key is nil")
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- if data.Host.TrafficKeyPublic == nil {
|
|
|
|
- logger.Log(0, "error: node traffic key is nil")
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- server := servercfg.GetServerInfo()
|
|
|
|
- server.TrafficKey = key
|
|
|
|
- data.Node.Server = servercfg.GetServer()
|
|
|
|
- if !logic.HostExists(&data.Host) {
|
|
|
|
- logic.CheckHostPorts(&data.Host)
|
|
|
|
- if servercfg.GetBrokerType() == servercfg.EmqxBrokerType {
|
|
|
|
- // create EMQX credentials for host if it doesn't exists
|
|
|
|
- if err := mq.CreateEmqxUser(data.Host.ID.String(), data.Host.HostPass, false); err != nil {
|
|
|
|
- logger.Log(0, "failed to add host credentials to EMQX: ", data.Host.ID.String(), err.Error())
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if err := logic.CreateHost(&data.Host); err != nil {
|
|
|
|
- if errors.Is(err, logic.ErrHostExists) {
|
|
|
|
- logger.Log(3, "host exists .. no need to create")
|
|
|
|
- host, err := logic.GetHost(data.Host.ID.String())
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"), "failed to find host:", err.Error())
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- logic.UpdateHostFromClient(&data.Host, host) // update the in memory struct values
|
|
|
|
- err = logic.UpsertHost(host)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("failed to update host [ %s ]: %v", host.ID.String(), err))
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- data.Host = *host
|
|
|
|
- } else {
|
|
|
|
- logger.Log(0, "error creating host", err.Error())
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- err = logic.AssociateNodeToHost(&data.Node, &data.Host)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("failed to create node on network [%s]: %s",
|
|
|
|
- networkName, err))
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // check if key belongs to a user
|
|
|
|
- // if so add to their netuser data
|
|
|
|
- // if it fails remove the node and fail request
|
|
|
|
- if user != nil {
|
|
|
|
- var updatedUserNode bool
|
|
|
|
- user.Nodes = append(user.Nodes, data.Node.ID.String()) // add new node to user
|
|
|
|
- if err = pro.UpdateNetworkUser(networkName, user); err == nil {
|
|
|
|
- logger.Log(1, "added node", data.Node.ID.String(), data.Host.Name, "to user", string(user.ID))
|
|
|
|
- updatedUserNode = true
|
|
|
|
- }
|
|
|
|
- if !updatedUserNode { // user was found but not updated, so delete node
|
|
|
|
- logger.Log(0, "failed to add node to user", keyName)
|
|
|
|
- logic.DeleteNode(&data.Node, true)
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- hostPeerUpdate, err := logic.GetPeerUpdateForHost(context.Background(), networkName, &data.Host, nil)
|
|
|
|
- if err != nil && !database.IsEmptyRecord(err) {
|
|
|
|
- logger.Log(0, r.Header.Get("user"),
|
|
|
|
- fmt.Sprintf("error fetching wg peers config for host [ %s ]: %v", data.Host.ID.String(), err))
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- data.Host.HostPass = "" // client should not change password after join
|
|
|
|
- // concealing hash
|
|
|
|
- response := models.NodeJoinResponse{
|
|
|
|
- Node: data.Node,
|
|
|
|
- ServerConfig: server,
|
|
|
|
- Host: data.Host,
|
|
|
|
- Peers: hostPeerUpdate.Peers,
|
|
|
|
- }
|
|
|
|
- logger.Log(1, r.Header.Get("user"), "created new node", data.Host.Name, data.Node.ID.String(), "on network", networkName)
|
|
|
|
- w.WriteHeader(http.StatusOK)
|
|
|
|
- json.NewEncoder(w).Encode(response)
|
|
|
|
-
|
|
|
|
- go func() {
|
|
|
|
- if err := mq.PublishPeerUpdate(); err != nil {
|
|
|
|
- logger.Log(1, "failed a peer update after creation of node", data.Host.Name)
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
- //runForceServerUpdate(&data.Node, true)
|
|
|
|
- go func() {
|
|
|
|
- dns := models.DNSUpdate{
|
|
|
|
- Action: models.DNSInsert,
|
|
|
|
- Name: data.Host.Name + "." + data.Node.Network,
|
|
|
|
- }
|
|
|
|
- if data.Node.Address.IP != nil {
|
|
|
|
- dns.Address = data.Node.Address.IP.String()
|
|
|
|
- //publish new node dns entry to all nodes on network
|
|
|
|
- if err := mq.PublishDNSUpdate(data.Node.Network, dns); err != nil {
|
|
|
|
- logger.Log(1, "failed to publish dns update on node creation", err.Error())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if data.Node.Address6.IP != nil {
|
|
|
|
- dns.Address = data.Node.Address6.IP.String()
|
|
|
|
- //publish new node dns entry to all nodes on network
|
|
|
|
- if err := mq.PublishDNSUpdate(data.Node.Network, dns); err != nil {
|
|
|
|
- logger.Log(1, "failed to publish dns update on node creation", err.Error())
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- //publish add dns records for network to new node
|
|
|
|
- if err := mq.PublishAllDNS(&data.Node); err != nil {
|
|
|
|
- logger.Log(1, "failed to publish dns update on node creation", err.Error())
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// == EGRESS ==
|
|
// == EGRESS ==
|
|
|
|
|
|
// swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway
|
|
// swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway
|