node.go 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. package controller
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. "github.com/gorilla/mux"
  8. "github.com/gravitl/netmaker/database"
  9. "github.com/gravitl/netmaker/logger"
  10. "github.com/gravitl/netmaker/logic"
  11. "github.com/gravitl/netmaker/logic/pro"
  12. "github.com/gravitl/netmaker/models"
  13. "github.com/gravitl/netmaker/models/promodels"
  14. "github.com/gravitl/netmaker/mq"
  15. "github.com/gravitl/netmaker/servercfg"
  16. "golang.org/x/crypto/bcrypt"
  17. )
  18. func nodeHandlers(r *mux.Router) {
  19. r.HandleFunc("/api/nodes", authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods("GET")
  20. r.HandleFunc("/api/nodes/{network}", authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods("GET")
  21. r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(getNode))).Methods("GET")
  22. r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
  23. r.HandleFunc("/api/nodes/{network}/{nodeid}", authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
  24. r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods("POST")
  25. r.HandleFunc("/api/nodes/{network}/{nodeid}/deleterelay", authorize(false, true, "user", http.HandlerFunc(deleteRelay))).Methods("DELETE")
  26. r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", authorize(false, true, "user", http.HandlerFunc(createEgressGateway))).Methods("POST")
  27. r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
  28. r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
  29. r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
  30. r.HandleFunc("/api/nodes/{network}/{nodeid}/approve", authorize(false, true, "user", http.HandlerFunc(uncordonNode))).Methods("POST")
  31. r.HandleFunc("/api/nodes/{network}", nodeauth(checkFreeTierLimits(node_l, http.HandlerFunc(createNode)))).Methods("POST")
  32. r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(false, true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
  33. r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
  34. }
  35. // swagger:route POST /api/nodes/adm/{network}/authenticate nodes authenticate
  36. //
  37. // Authenticate to make further API calls related to a network.
  38. //
  39. // Schemes: https
  40. //
  41. // Security:
  42. // oauth
  43. //
  44. // Responses:
  45. // 200: successResponse
  46. func authenticate(response http.ResponseWriter, request *http.Request) {
  47. var authRequest models.AuthParams
  48. var result models.Node
  49. var errorResponse = models.ErrorResponse{
  50. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  51. }
  52. decoder := json.NewDecoder(request.Body)
  53. decoderErr := decoder.Decode(&authRequest)
  54. defer request.Body.Close()
  55. if decoderErr != nil {
  56. errorResponse.Code = http.StatusBadRequest
  57. errorResponse.Message = decoderErr.Error()
  58. logger.Log(0, request.Header.Get("user"), "error decoding request body: ",
  59. decoderErr.Error())
  60. logic.ReturnErrorResponse(response, request, errorResponse)
  61. return
  62. }
  63. errorResponse.Code = http.StatusBadRequest
  64. if authRequest.ID == "" {
  65. errorResponse.Message = "W1R3: ID can't be empty"
  66. logger.Log(0, request.Header.Get("user"), errorResponse.Message)
  67. logic.ReturnErrorResponse(response, request, errorResponse)
  68. return
  69. } else if authRequest.Password == "" {
  70. errorResponse.Message = "W1R3: Password can't be empty"
  71. logger.Log(0, request.Header.Get("user"), errorResponse.Message)
  72. logic.ReturnErrorResponse(response, request, errorResponse)
  73. return
  74. }
  75. var err error
  76. result, err = logic.GetNodeByID(authRequest.ID)
  77. if err != nil {
  78. errorResponse.Code = http.StatusBadRequest
  79. errorResponse.Message = err.Error()
  80. logger.Log(0, request.Header.Get("user"),
  81. fmt.Sprintf("failed to get node info [%s]: %v", authRequest.ID, err))
  82. logic.ReturnErrorResponse(response, request, errorResponse)
  83. return
  84. }
  85. err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
  86. if err != nil {
  87. errorResponse.Code = http.StatusBadRequest
  88. errorResponse.Message = err.Error()
  89. logger.Log(0, request.Header.Get("user"),
  90. "error validating user password: ", err.Error())
  91. logic.ReturnErrorResponse(response, request, errorResponse)
  92. return
  93. }
  94. event := mq.DynSecAction{
  95. Payload: mq.MqDynsecPayload{
  96. Commands: []mq.MqDynSecCmd{
  97. {
  98. Command: mq.CreateRoleCmd,
  99. RoleName: result.Network,
  100. Textname: "Network wide role with Acls for nodes",
  101. Acls: mq.FetchNetworkAcls(result.Network),
  102. },
  103. {
  104. Command: mq.CreateRoleCmd,
  105. RoleName: fmt.Sprintf("%s-%s", "Node", result.ID),
  106. Acls: mq.FetchNodeAcls(result.ID),
  107. Textname: "Role for node " + result.Name,
  108. },
  109. {
  110. Command: mq.CreateClientCmd,
  111. Username: result.ID,
  112. Password: authRequest.Password,
  113. Textname: result.Name,
  114. Roles: []mq.MqDynSecRole{
  115. {
  116. Rolename: fmt.Sprintf("%s-%s", "Node", result.ID),
  117. Priority: -1,
  118. },
  119. {
  120. Rolename: result.Network,
  121. Priority: -1,
  122. },
  123. },
  124. Groups: make([]mq.MqDynSecGroup, 0),
  125. },
  126. },
  127. },
  128. }
  129. if err := mq.PublishEventToDynSecTopic(event); err != nil {
  130. logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
  131. event.Payload.Commands, err.Error()))
  132. errorResponse.Code = http.StatusInternalServerError
  133. errorResponse.Message = fmt.Sprintf("could not create mq client for node [%s]: %v", result.ID, err)
  134. return
  135. }
  136. tokenString, err := logic.CreateJWT(authRequest.ID, authRequest.MacAddress, result.Network)
  137. if tokenString == "" {
  138. errorResponse.Code = http.StatusBadRequest
  139. errorResponse.Message = "Could not create Token"
  140. logger.Log(0, request.Header.Get("user"),
  141. fmt.Sprintf("%s: %v", errorResponse.Message, err))
  142. logic.ReturnErrorResponse(response, request, errorResponse)
  143. return
  144. }
  145. var successResponse = models.SuccessResponse{
  146. Code: http.StatusOK,
  147. Message: "W1R3: Device " + authRequest.ID + " Authorized",
  148. Response: models.SuccessfulLoginResponse{
  149. AuthToken: tokenString,
  150. ID: authRequest.ID,
  151. },
  152. }
  153. successJSONResponse, jsonError := json.Marshal(successResponse)
  154. if jsonError != nil {
  155. errorResponse.Code = http.StatusBadRequest
  156. errorResponse.Message = err.Error()
  157. logger.Log(0, request.Header.Get("user"),
  158. "error marshalling resp: ", err.Error())
  159. logic.ReturnErrorResponse(response, request, errorResponse)
  160. return
  161. }
  162. response.WriteHeader(http.StatusOK)
  163. response.Header().Set("Content-Type", "application/json")
  164. response.Write(successJSONResponse)
  165. }
  166. // auth middleware for api calls from nodes where node is has not yet joined the server (register, join)
  167. func nodeauth(next http.Handler) http.HandlerFunc {
  168. return func(w http.ResponseWriter, r *http.Request) {
  169. bearerToken := r.Header.Get("Authorization")
  170. var tokenSplit = strings.Split(bearerToken, " ")
  171. var token = ""
  172. if len(tokenSplit) < 2 {
  173. errorResponse := models.ErrorResponse{
  174. Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
  175. }
  176. logic.ReturnErrorResponse(w, r, errorResponse)
  177. return
  178. } else {
  179. token = tokenSplit[1]
  180. }
  181. found := false
  182. networks, err := logic.GetNetworks()
  183. if err != nil {
  184. logger.Log(0, "no networks", err.Error())
  185. errorResponse := models.ErrorResponse{
  186. Code: http.StatusNotFound, Message: "no networks",
  187. }
  188. logic.ReturnErrorResponse(w, r, errorResponse)
  189. return
  190. }
  191. for _, network := range networks {
  192. for _, key := range network.AccessKeys {
  193. if key.Value == token {
  194. found = true
  195. break
  196. }
  197. }
  198. }
  199. if !found {
  200. logger.Log(0, "valid access key not found")
  201. errorResponse := models.ErrorResponse{
  202. Code: http.StatusUnauthorized, Message: "You are unauthorized to access this endpoint.",
  203. }
  204. logic.ReturnErrorResponse(w, r, errorResponse)
  205. return
  206. }
  207. next.ServeHTTP(w, r)
  208. }
  209. }
  210. // The middleware for most requests to the API
  211. // They all pass through here first
  212. // This will validate the JWT (or check for master token)
  213. // This will also check against the authNetwork and make sure the node should be accessing that endpoint,
  214. // even if it's technically ok
  215. // This is kind of a poor man's RBAC. There's probably a better/smarter way.
  216. // TODO: Consider better RBAC implementations
  217. func authorize(nodesAllowed, networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
  218. return func(w http.ResponseWriter, r *http.Request) {
  219. var errorResponse = models.ErrorResponse{
  220. Code: http.StatusUnauthorized, Message: logic.Unauthorized_Msg,
  221. }
  222. var params = mux.Vars(r)
  223. networkexists, _ := logic.NetworkExists(params["network"])
  224. //check that the request is for a valid network
  225. //if (networkCheck && !networkexists) || err != nil {
  226. if networkCheck && !networkexists {
  227. logic.ReturnErrorResponse(w, r, errorResponse)
  228. return
  229. } else {
  230. w.Header().Set("Content-Type", "application/json")
  231. //get the auth token
  232. bearerToken := r.Header.Get("Authorization")
  233. var tokenSplit = strings.Split(bearerToken, " ")
  234. //I put this in in case the user doesn't put in a token at all (in which case it's empty)
  235. //There's probably a smarter way of handling this.
  236. var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
  237. if len(tokenSplit) > 1 {
  238. authToken = tokenSplit[1]
  239. } else {
  240. logic.ReturnErrorResponse(w, r, errorResponse)
  241. return
  242. }
  243. //check if node instead of user
  244. if nodesAllowed {
  245. // TODO --- should ensure that node is only operating on itself
  246. if _, _, _, err := logic.VerifyToken(authToken); err == nil {
  247. next.ServeHTTP(w, r)
  248. return
  249. }
  250. }
  251. var isAuthorized = false
  252. var nodeID = ""
  253. username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
  254. if errN != nil {
  255. logic.ReturnErrorResponse(w, r, errorResponse)
  256. return
  257. }
  258. isnetadmin := isadmin
  259. if errN == nil && isadmin {
  260. nodeID = "mastermac"
  261. isAuthorized = true
  262. r.Header.Set("ismasterkey", "yes")
  263. }
  264. if !isadmin && params["network"] != "" {
  265. if logic.StringSliceContains(networks, params["network"]) && pro.IsUserNetAdmin(params["network"], username) {
  266. isnetadmin = true
  267. }
  268. }
  269. //The mastermac (login with masterkey from config) can do everything!! May be dangerous.
  270. if nodeID == "mastermac" {
  271. isAuthorized = true
  272. r.Header.Set("ismasterkey", "yes")
  273. //for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
  274. //So each route defines which access network should be allowed to access it
  275. } else {
  276. switch authNetwork {
  277. case "all":
  278. isAuthorized = true
  279. case "nodes":
  280. isAuthorized = (nodeID != "") || isnetadmin
  281. case "network":
  282. if isnetadmin {
  283. isAuthorized = true
  284. } else {
  285. node, err := logic.GetNodeByID(nodeID)
  286. if err != nil {
  287. logic.ReturnErrorResponse(w, r, errorResponse)
  288. return
  289. }
  290. isAuthorized = (node.Network == params["network"])
  291. }
  292. case "node":
  293. if isnetadmin {
  294. isAuthorized = true
  295. } else {
  296. isAuthorized = (nodeID == params["netid"])
  297. }
  298. case "user":
  299. isAuthorized = true
  300. default:
  301. isAuthorized = false
  302. }
  303. }
  304. if !isAuthorized {
  305. logic.ReturnErrorResponse(w, r, errorResponse)
  306. return
  307. } else {
  308. //If authorized, this function passes along it's request and output to the appropriate route function.
  309. if username == "" {
  310. username = "(user not found)"
  311. }
  312. r.Header.Set("user", username)
  313. next.ServeHTTP(w, r)
  314. }
  315. }
  316. }
  317. }
  318. // swagger:route GET /api/nodes/{network} nodes getNetworkNodes
  319. //
  320. // Gets all nodes associated with network including pending nodes.
  321. //
  322. // Schemes: https
  323. //
  324. // Security:
  325. // oauth
  326. //
  327. // Responses:
  328. // 200: nodeSliceResponse
  329. func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
  330. w.Header().Set("Content-Type", "application/json")
  331. var nodes []models.Node
  332. var params = mux.Vars(r)
  333. networkName := params["network"]
  334. nodes, err := logic.GetNetworkNodes(networkName)
  335. if err != nil {
  336. logger.Log(0, r.Header.Get("user"),
  337. fmt.Sprintf("error fetching nodes on network %s: %v", networkName, err))
  338. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  339. return
  340. }
  341. for _, node := range nodes {
  342. if len(node.NetworkSettings.AccessKeys) > 0 {
  343. node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network
  344. }
  345. }
  346. //Returns all the nodes in JSON format
  347. logger.Log(2, r.Header.Get("user"), "fetched nodes on network", networkName)
  348. w.WriteHeader(http.StatusOK)
  349. json.NewEncoder(w).Encode(nodes)
  350. }
  351. // swagger:route GET /api/nodes nodes getAllNodes
  352. //
  353. // Get all nodes across all networks.
  354. //
  355. // Schemes: https
  356. //
  357. // Security:
  358. // oauth
  359. //
  360. // Responses:
  361. // 200: nodeSliceResponse
  362. // 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
  363. func getAllNodes(w http.ResponseWriter, r *http.Request) {
  364. w.Header().Set("Content-Type", "application/json")
  365. user, err := logic.GetUser(r.Header.Get("user"))
  366. if err != nil && r.Header.Get("ismasterkey") != "yes" {
  367. logger.Log(0, r.Header.Get("user"),
  368. "error fetching user info: ", err.Error())
  369. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  370. return
  371. }
  372. var nodes []models.Node
  373. if user.IsAdmin || r.Header.Get("ismasterkey") == "yes" {
  374. nodes, err = logic.GetAllNodes()
  375. if err != nil {
  376. logger.Log(0, "error fetching all nodes info: ", err.Error())
  377. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  378. return
  379. }
  380. } else {
  381. nodes, err = getUsersNodes(user)
  382. if err != nil {
  383. logger.Log(0, r.Header.Get("user"),
  384. "error fetching nodes: ", err.Error())
  385. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  386. return
  387. }
  388. }
  389. //Return all the nodes in JSON format
  390. logger.Log(3, r.Header.Get("user"), "fetched all nodes they have access to")
  391. w.WriteHeader(http.StatusOK)
  392. json.NewEncoder(w).Encode(nodes)
  393. }
  394. func getUsersNodes(user models.User) ([]models.Node, error) {
  395. var nodes []models.Node
  396. var err error
  397. for _, networkName := range user.Networks {
  398. tmpNodes, err := logic.GetNetworkNodes(networkName)
  399. if err != nil {
  400. continue
  401. }
  402. nodes = append(nodes, tmpNodes...)
  403. }
  404. return nodes, err
  405. }
  406. // swagger:route GET /api/nodes/{network}/{nodeid} nodes getNode
  407. //
  408. // Get an individual node.
  409. //
  410. // Schemes: https
  411. //
  412. // Security:
  413. // oauth
  414. //
  415. // Responses:
  416. // 200: nodeResponse
  417. func getNode(w http.ResponseWriter, r *http.Request) {
  418. // set header.
  419. w.Header().Set("Content-Type", "application/json")
  420. var params = mux.Vars(r)
  421. nodeid := params["nodeid"]
  422. node, err := logic.GetNodeByID(nodeid)
  423. if err != nil {
  424. logger.Log(0, r.Header.Get("user"),
  425. fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
  426. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  427. return
  428. }
  429. peerUpdate, err := logic.GetPeerUpdate(&node)
  430. if err != nil && !database.IsEmptyRecord(err) {
  431. logger.Log(0, r.Header.Get("user"),
  432. fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", nodeid, err))
  433. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  434. return
  435. }
  436. if len(node.NetworkSettings.AccessKeys) > 0 {
  437. node.NetworkSettings.AccessKeys = []models.AccessKey{} // not to be sent back to client; client already knows how to join the network
  438. }
  439. response := models.NodeGet{
  440. Node: node,
  441. Peers: peerUpdate.Peers,
  442. ServerConfig: servercfg.GetServerInfo(),
  443. PeerIDs: peerUpdate.PeerIDs,
  444. }
  445. logger.Log(2, r.Header.Get("user"), "fetched node", params["nodeid"])
  446. w.WriteHeader(http.StatusOK)
  447. json.NewEncoder(w).Encode(response)
  448. }
  449. // swagger:route GET /api/nodes/adm/{network}/lastmodified nodes getLastModified
  450. //
  451. // Get the time that a network of nodes was last modified.
  452. //
  453. // Schemes: https
  454. //
  455. // Security:
  456. // oauth
  457. //
  458. // Responses:
  459. // 200: nodeLastModifiedResponse
  460. // TODO: This needs to be refactored
  461. // Potential way to do this: On UpdateNode, set a new field for "LastModified"
  462. // If we go with the existing way, we need to at least set network.NodesLastModified on UpdateNode
  463. func getLastModified(w http.ResponseWriter, r *http.Request) {
  464. // set header.
  465. w.Header().Set("Content-Type", "application/json")
  466. var params = mux.Vars(r)
  467. networkName := params["network"]
  468. network, err := logic.GetNetwork(networkName)
  469. if err != nil {
  470. logger.Log(0, r.Header.Get("user"),
  471. fmt.Sprintf("error fetching network [%s] info: %v", networkName, err))
  472. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  473. return
  474. }
  475. logger.Log(2, r.Header.Get("user"), "called last modified")
  476. w.WriteHeader(http.StatusOK)
  477. json.NewEncoder(w).Encode(network.NodesLastModified)
  478. }
  479. // swagger:route POST /api/nodes/{network} nodes createNode
  480. //
  481. // Create a node on a network.
  482. //
  483. // Schemes: https
  484. //
  485. // Security:
  486. // oauth
  487. //
  488. // Responses:
  489. // 200: nodeGetResponse
  490. func createNode(w http.ResponseWriter, r *http.Request) {
  491. w.Header().Set("Content-Type", "application/json")
  492. var params = mux.Vars(r)
  493. var errorResponse = models.ErrorResponse{
  494. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  495. }
  496. networkName := params["network"]
  497. networkexists, err := logic.NetworkExists(networkName)
  498. if err != nil {
  499. logger.Log(0, r.Header.Get("user"),
  500. fmt.Sprintf("failed to fetch network [%s] info: %v", networkName, err))
  501. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  502. return
  503. } else if !networkexists {
  504. errorResponse = models.ErrorResponse{
  505. Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ",
  506. }
  507. logger.Log(0, r.Header.Get("user"),
  508. fmt.Sprintf("network [%s] does not exist", networkName))
  509. logic.ReturnErrorResponse(w, r, errorResponse)
  510. return
  511. }
  512. var node = models.Node{}
  513. //get node from body of request
  514. err = json.NewDecoder(r.Body).Decode(&node)
  515. if err != nil {
  516. logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
  517. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  518. return
  519. }
  520. node.Network = networkName
  521. network, err := logic.GetNetworkByNode(&node)
  522. if err != nil {
  523. logger.Log(0, r.Header.Get("user"),
  524. fmt.Sprintf("failed to get network [%s] info: %v", node.Network, err))
  525. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  526. return
  527. }
  528. node.NetworkSettings, err = logic.GetNetworkSettings(node.Network)
  529. if err != nil {
  530. logger.Log(0, r.Header.Get("user"),
  531. fmt.Sprintf("failed to get network [%s] settings: %v", node.Network, err))
  532. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  533. return
  534. }
  535. keyName, validKey := logic.IsKeyValid(networkName, node.AccessKey)
  536. if !validKey {
  537. // Check to see if network will allow manual sign up
  538. // may want to switch this up with the valid key check and avoid a DB call that way.
  539. if network.AllowManualSignUp == "yes" {
  540. node.IsPending = "yes"
  541. } else {
  542. errorResponse = models.ErrorResponse{
  543. Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
  544. }
  545. logger.Log(0, r.Header.Get("user"),
  546. fmt.Sprintf("failed to create node on network [%s]: %s",
  547. node.Network, errorResponse.Message))
  548. logic.ReturnErrorResponse(w, r, errorResponse)
  549. return
  550. }
  551. }
  552. user, err := pro.GetNetworkUser(networkName, promodels.NetworkUserID(keyName))
  553. if err == nil {
  554. if user.ID != "" {
  555. logger.Log(1, "associating new node with user", keyName)
  556. node.OwnerID = string(user.ID)
  557. }
  558. }
  559. key, keyErr := logic.RetrievePublicTrafficKey()
  560. if keyErr != nil {
  561. logger.Log(0, "error retrieving key: ", keyErr.Error())
  562. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  563. return
  564. }
  565. if key == nil {
  566. logger.Log(0, "error: server traffic key is nil")
  567. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  568. return
  569. }
  570. if node.TrafficKeys.Mine == nil {
  571. logger.Log(0, "error: node traffic key is nil")
  572. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  573. return
  574. }
  575. node.TrafficKeys = models.TrafficKeys{
  576. Mine: node.TrafficKeys.Mine,
  577. Server: key,
  578. }
  579. nodePassword := node.Password
  580. err = logic.CreateNode(&node)
  581. if err != nil {
  582. logger.Log(0, r.Header.Get("user"),
  583. fmt.Sprintf("failed to create node on network [%s]: %s",
  584. node.Network, err))
  585. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  586. return
  587. }
  588. // check if key belongs to a user
  589. // if so add to their netuser data
  590. // if it fails remove the node and fail request
  591. if user != nil {
  592. var updatedUserNode bool
  593. user.Nodes = append(user.Nodes, node.ID) // add new node to user
  594. if err = pro.UpdateNetworkUser(networkName, user); err == nil {
  595. logger.Log(1, "added node", node.ID, node.Name, "to user", string(user.ID))
  596. updatedUserNode = true
  597. }
  598. if !updatedUserNode { // user was found but not updated, so delete node
  599. logger.Log(0, "failed to add node to user", keyName)
  600. logic.DeleteNodeByID(&node, true)
  601. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  602. return
  603. }
  604. }
  605. peerUpdate, err := logic.GetPeerUpdate(&node)
  606. if err != nil && !database.IsEmptyRecord(err) {
  607. logger.Log(0, r.Header.Get("user"),
  608. fmt.Sprintf("error fetching wg peers config for node [ %s ]: %v", node.ID, err))
  609. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  610. return
  611. }
  612. // Delete Any Existing Client with this ID.
  613. event := mq.DynSecAction{
  614. Payload: mq.MqDynsecPayload{
  615. Commands: []mq.MqDynSecCmd{
  616. {
  617. Command: mq.DeleteClientCmd,
  618. Username: node.ID,
  619. },
  620. },
  621. },
  622. }
  623. if err := mq.PublishEventToDynSecTopic(event); err != nil {
  624. logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
  625. event.Payload.Commands, err.Error()))
  626. }
  627. // Create client for this node in Mq
  628. event = mq.DynSecAction{
  629. Payload: mq.MqDynsecPayload{
  630. Commands: []mq.MqDynSecCmd{
  631. {
  632. Command: mq.CreateRoleCmd,
  633. RoleName: fmt.Sprintf("%s-%s", "Node", node.ID),
  634. Acls: mq.FetchNodeAcls(node.ID),
  635. Textname: "Role for node " + node.Name,
  636. },
  637. {
  638. Command: mq.CreateClientCmd,
  639. Username: node.ID,
  640. Password: nodePassword,
  641. Textname: node.Name,
  642. Roles: []mq.MqDynSecRole{
  643. {
  644. Rolename: fmt.Sprintf("%s-%s", "Node", node.ID),
  645. Priority: -1,
  646. },
  647. {
  648. Rolename: node.Network,
  649. Priority: -1,
  650. },
  651. },
  652. Groups: make([]mq.MqDynSecGroup, 0),
  653. },
  654. },
  655. },
  656. }
  657. if err := mq.PublishEventToDynSecTopic(event); err != nil {
  658. logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
  659. event.Payload.Commands, err.Error()))
  660. }
  661. response := models.NodeGet{
  662. Node: node,
  663. Peers: peerUpdate.Peers,
  664. ServerConfig: servercfg.GetServerInfo(),
  665. PeerIDs: peerUpdate.PeerIDs,
  666. }
  667. logger.Log(1, r.Header.Get("user"), "created new node", node.Name, "on network", node.Network)
  668. w.WriteHeader(http.StatusOK)
  669. json.NewEncoder(w).Encode(response)
  670. runForceServerUpdate(&node, true)
  671. }
  672. // swagger:route POST /api/nodes/{network}/{nodeid}/approve nodes uncordonNode
  673. //
  674. // Takes a node out of pending state.
  675. //
  676. // Schemes: https
  677. //
  678. // Security:
  679. // oauth
  680. //
  681. // Responses:
  682. // 200: nodeResponse
  683. // Takes node out of pending state
  684. // TODO: May want to use cordon/uncordon terminology instead of "ispending".
  685. func uncordonNode(w http.ResponseWriter, r *http.Request) {
  686. var params = mux.Vars(r)
  687. w.Header().Set("Content-Type", "application/json")
  688. var nodeid = params["nodeid"]
  689. node, err := logic.UncordonNode(nodeid)
  690. if err != nil {
  691. logger.Log(0, r.Header.Get("user"),
  692. fmt.Sprintf("failed to uncordon node [%s]: %v", node.Name, err))
  693. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  694. return
  695. }
  696. logger.Log(1, r.Header.Get("user"), "uncordoned node", node.Name)
  697. w.WriteHeader(http.StatusOK)
  698. json.NewEncoder(w).Encode("SUCCESS")
  699. runUpdates(&node, false)
  700. }
  701. // == EGRESS ==
  702. // swagger:route POST /api/nodes/{network}/{nodeid}/creategateway nodes createEgressGateway
  703. //
  704. // Create an egress gateway.
  705. //
  706. // Schemes: https
  707. //
  708. // Security:
  709. // oauth
  710. //
  711. // Responses:
  712. // 200: nodeResponse
  713. func createEgressGateway(w http.ResponseWriter, r *http.Request) {
  714. var gateway models.EgressGatewayRequest
  715. var params = mux.Vars(r)
  716. w.Header().Set("Content-Type", "application/json")
  717. err := json.NewDecoder(r.Body).Decode(&gateway)
  718. if err != nil {
  719. logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
  720. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  721. return
  722. }
  723. gateway.NetID = params["network"]
  724. gateway.NodeID = params["nodeid"]
  725. node, err := logic.CreateEgressGateway(gateway)
  726. if err != nil {
  727. logger.Log(0, r.Header.Get("user"),
  728. fmt.Sprintf("failed to create egress gateway on node [%s] on network [%s]: %v",
  729. gateway.NodeID, gateway.NetID, err))
  730. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  731. return
  732. }
  733. logger.Log(1, r.Header.Get("user"), "created egress gateway on node", gateway.NodeID, "on network", gateway.NetID)
  734. w.WriteHeader(http.StatusOK)
  735. json.NewEncoder(w).Encode(node)
  736. runUpdates(&node, true)
  737. }
  738. // swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
  739. //
  740. // Delete an egress gateway.
  741. //
  742. // Schemes: https
  743. //
  744. // Security:
  745. // oauth
  746. //
  747. // Responses:
  748. // 200: nodeResponse
  749. func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
  750. w.Header().Set("Content-Type", "application/json")
  751. var params = mux.Vars(r)
  752. nodeid := params["nodeid"]
  753. netid := params["network"]
  754. node, err := logic.DeleteEgressGateway(netid, nodeid)
  755. if err != nil {
  756. logger.Log(0, r.Header.Get("user"),
  757. fmt.Sprintf("failed to delete egress gateway on node [%s] on network [%s]: %v",
  758. nodeid, netid, err))
  759. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  760. return
  761. }
  762. logger.Log(1, r.Header.Get("user"), "deleted egress gateway on node", nodeid, "on network", netid)
  763. w.WriteHeader(http.StatusOK)
  764. json.NewEncoder(w).Encode(node)
  765. runUpdates(&node, true)
  766. }
  767. // == INGRESS ==
  768. // swagger:route POST /api/nodes/{network}/{nodeid}/createingress nodes createIngressGateway
  769. //
  770. // Create an ingress gateway.
  771. //
  772. // Schemes: https
  773. //
  774. // Security:
  775. // oauth
  776. //
  777. // Responses:
  778. // 200: nodeResponse
  779. func createIngressGateway(w http.ResponseWriter, r *http.Request) {
  780. var params = mux.Vars(r)
  781. w.Header().Set("Content-Type", "application/json")
  782. nodeid := params["nodeid"]
  783. netid := params["network"]
  784. node, err := logic.CreateIngressGateway(netid, nodeid)
  785. if err != nil {
  786. logger.Log(0, r.Header.Get("user"),
  787. fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v",
  788. nodeid, netid, err))
  789. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  790. return
  791. }
  792. logger.Log(1, r.Header.Get("user"), "created ingress gateway on node", nodeid, "on network", netid)
  793. w.WriteHeader(http.StatusOK)
  794. json.NewEncoder(w).Encode(node)
  795. runUpdates(&node, true)
  796. }
  797. // swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway
  798. //
  799. // Delete an ingress gateway.
  800. //
  801. // Schemes: https
  802. //
  803. // Security:
  804. // oauth
  805. //
  806. // Responses:
  807. // 200: nodeResponse
  808. func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
  809. w.Header().Set("Content-Type", "application/json")
  810. var params = mux.Vars(r)
  811. nodeid := params["nodeid"]
  812. netid := params["network"]
  813. node, err := logic.DeleteIngressGateway(netid, nodeid)
  814. if err != nil {
  815. logger.Log(0, r.Header.Get("user"),
  816. fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v",
  817. nodeid, netid, err))
  818. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  819. return
  820. }
  821. logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)
  822. w.WriteHeader(http.StatusOK)
  823. json.NewEncoder(w).Encode(node)
  824. runUpdates(&node, true)
  825. }
  826. // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
  827. //
  828. // Update an individual node.
  829. //
  830. // Schemes: https
  831. //
  832. // Security:
  833. // oauth
  834. //
  835. // Responses:
  836. // 200: nodeResponse
  837. func updateNode(w http.ResponseWriter, r *http.Request) {
  838. w.Header().Set("Content-Type", "application/json")
  839. var params = mux.Vars(r)
  840. var node models.Node
  841. //start here
  842. nodeid := params["nodeid"]
  843. node, err := logic.GetNodeByID(nodeid)
  844. if err != nil {
  845. logger.Log(0, r.Header.Get("user"),
  846. fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
  847. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  848. return
  849. }
  850. var newNode models.Node
  851. // we decode our body request params
  852. err = json.NewDecoder(r.Body).Decode(&newNode)
  853. if err != nil {
  854. logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
  855. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  856. return
  857. }
  858. relayupdate := false
  859. if node.IsRelay == "yes" && len(newNode.RelayAddrs) > 0 {
  860. if len(newNode.RelayAddrs) != len(node.RelayAddrs) {
  861. relayupdate = true
  862. } else {
  863. for i, addr := range newNode.RelayAddrs {
  864. if addr != node.RelayAddrs[i] {
  865. relayupdate = true
  866. }
  867. }
  868. }
  869. }
  870. relayedUpdate := false
  871. if node.IsRelayed == "yes" && (node.Address != newNode.Address || node.Address6 != newNode.Address6) {
  872. relayedUpdate = true
  873. }
  874. if !servercfg.GetRce() {
  875. newNode.PostDown = node.PostDown
  876. newNode.PostUp = node.PostUp
  877. }
  878. ifaceDelta := logic.IfaceDelta(&node, &newNode)
  879. // for a hub change also need to update the existing hub
  880. if newNode.IsHub == "yes" && node.IsHub != "yes" {
  881. nodeToUpdate, err := logic.UnsetHub(newNode.Network)
  882. if err != nil {
  883. logger.Log(2, "failed to unset hubs", err.Error())
  884. }
  885. if err := mq.NodeUpdate(nodeToUpdate); err != nil {
  886. logger.Log(2, "failed to update hub node", nodeToUpdate.Name, err.Error())
  887. }
  888. if nodeToUpdate.IsServer == "yes" {
  889. // set ifacdelta true to force server to update peeers
  890. if err := logic.ServerUpdate(nodeToUpdate, true); err != nil {
  891. logger.Log(2, "failed to update server node on hub change", err.Error())
  892. }
  893. }
  894. }
  895. err = logic.UpdateNode(&node, &newNode)
  896. if err != nil {
  897. logger.Log(0, r.Header.Get("user"),
  898. fmt.Sprintf("failed to update node info [ %s ] info: %v", nodeid, err))
  899. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  900. return
  901. }
  902. if relayupdate {
  903. updatenodes := logic.UpdateRelay(node.Network, node.RelayAddrs, newNode.RelayAddrs)
  904. if len(updatenodes) > 0 {
  905. for _, relayedNode := range updatenodes {
  906. runUpdates(&relayedNode, false)
  907. }
  908. }
  909. }
  910. if relayedUpdate {
  911. updateRelay(&node, &newNode)
  912. }
  913. if servercfg.IsDNSMode() {
  914. logic.SetDNS()
  915. }
  916. logger.Log(1, r.Header.Get("user"), "updated node", node.ID, "on network", node.Network)
  917. w.WriteHeader(http.StatusOK)
  918. json.NewEncoder(w).Encode(newNode)
  919. runUpdates(&newNode, ifaceDelta)
  920. }
  921. // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
  922. //
  923. // Delete an individual node.
  924. //
  925. // Schemes: https
  926. //
  927. // Security:
  928. // oauth
  929. //
  930. // Responses:
  931. // 200: nodeResponse
  932. func deleteNode(w http.ResponseWriter, r *http.Request) {
  933. // Set header
  934. w.Header().Set("Content-Type", "application/json")
  935. // get params
  936. var params = mux.Vars(r)
  937. var nodeid = params["nodeid"]
  938. var node, err = logic.GetNodeByID(nodeid)
  939. if err != nil {
  940. logger.Log(0, r.Header.Get("user"),
  941. fmt.Sprintf("error fetching node [ %s ] info: %v", nodeid, err))
  942. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  943. return
  944. }
  945. if isServer(&node) {
  946. err := fmt.Errorf("cannot delete server node")
  947. logger.Log(0, r.Header.Get("user"),
  948. fmt.Sprintf("failed to delete node [ %s ]: %v", nodeid, err))
  949. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  950. return
  951. }
  952. if r.Header.Get("ismaster") != "yes" {
  953. username := r.Header.Get("user")
  954. if username != "" && !doesUserOwnNode(username, params["network"], nodeid) {
  955. logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("user not permitted"), "badrequest"))
  956. return
  957. }
  958. }
  959. //send update to node to be deleted before deleting on server otherwise message cannot be sent
  960. node.Action = models.NODE_DELETE
  961. err = logic.DeleteNodeByID(&node, false)
  962. if err != nil {
  963. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  964. return
  965. }
  966. event := mq.DynSecAction{
  967. Payload: mq.MqDynsecPayload{
  968. Commands: []mq.MqDynSecCmd{
  969. {
  970. Command: mq.DeleteRoleCmd,
  971. RoleName: fmt.Sprintf("%s-%s", "Node", nodeid),
  972. },
  973. {
  974. Command: mq.DeleteClientCmd,
  975. Username: nodeid,
  976. },
  977. },
  978. },
  979. }
  980. if err := mq.PublishEventToDynSecTopic(event); err != nil {
  981. logger.Log(0, fmt.Sprintf("failed to send DynSec command [%v]: %v",
  982. event.Payload.Commands, err.Error()))
  983. }
  984. logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
  985. logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
  986. runUpdates(&node, false)
  987. runForceServerUpdate(&node, false)
  988. }
  989. func runUpdates(node *models.Node, ifaceDelta bool) {
  990. go func() { // don't block http response
  991. // publish node update if not server
  992. if err := mq.NodeUpdate(node); err != nil {
  993. logger.Log(1, "error publishing node update to node", node.Name, node.ID, err.Error())
  994. }
  995. if err := runServerUpdate(node, ifaceDelta); err != nil {
  996. logger.Log(1, "error running server update", err.Error())
  997. }
  998. }()
  999. }
  1000. // updates local peers for a server on a given node's network
  1001. func runServerUpdate(node *models.Node, ifaceDelta bool) error {
  1002. if servercfg.IsClientMode() != "on" || !isServer(node) {
  1003. return nil
  1004. }
  1005. currentServerNode, err := logic.GetNetworkServerLocal(node.Network)
  1006. if err != nil {
  1007. return err
  1008. }
  1009. if ifaceDelta && logic.IsLeader(&currentServerNode) {
  1010. if err := mq.PublishPeerUpdate(&currentServerNode, false); err != nil {
  1011. logger.Log(1, "failed to publish peer update "+err.Error())
  1012. }
  1013. }
  1014. if err := logic.ServerUpdate(&currentServerNode, ifaceDelta); err != nil {
  1015. logger.Log(1, "server node:", currentServerNode.ID, "failed update")
  1016. return err
  1017. }
  1018. return nil
  1019. }
  1020. func runForceServerUpdate(node *models.Node, publishPeerUpdateToNode bool) {
  1021. go func() {
  1022. if err := mq.PublishPeerUpdate(node, publishPeerUpdateToNode); err != nil {
  1023. logger.Log(1, "failed a peer update after creation of node", node.Name)
  1024. }
  1025. var currentServerNode, getErr = logic.GetNetworkServerLeader(node.Network)
  1026. if getErr == nil {
  1027. if err := logic.ServerUpdate(&currentServerNode, false); err != nil {
  1028. logger.Log(1, "server node:", currentServerNode.ID, "failed update")
  1029. }
  1030. }
  1031. }()
  1032. }
  1033. func isServer(node *models.Node) bool {
  1034. return node.IsServer == "yes"
  1035. }
  1036. func updateRelay(oldnode, newnode *models.Node) {
  1037. relay := logic.FindRelay(oldnode)
  1038. newrelay := relay
  1039. //check if node's address has been updated and if so, update the relayAddrs of the relay node with the updated address of the relayed node
  1040. if oldnode.Address != newnode.Address {
  1041. for i, ip := range newrelay.RelayAddrs {
  1042. if ip == oldnode.Address {
  1043. newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], relay.RelayAddrs[i+1:]...)
  1044. newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address)
  1045. }
  1046. }
  1047. }
  1048. //check if node's address(v6) has been updated and if so, update the relayAddrs of the relay node with the updated address(v6) of the relayed node
  1049. if oldnode.Address6 != newnode.Address6 {
  1050. for i, ip := range newrelay.RelayAddrs {
  1051. if ip == oldnode.Address {
  1052. newrelay.RelayAddrs = append(newrelay.RelayAddrs[:i], newrelay.RelayAddrs[i+1:]...)
  1053. newrelay.RelayAddrs = append(newrelay.RelayAddrs, newnode.Address6)
  1054. }
  1055. }
  1056. }
  1057. logic.UpdateNode(relay, newrelay)
  1058. }
  1059. func doesUserOwnNode(username, network, nodeID string) bool {
  1060. u, err := logic.GetUser(username)
  1061. if err != nil {
  1062. return false
  1063. }
  1064. if u.IsAdmin {
  1065. return true
  1066. }
  1067. netUser, err := pro.GetNetworkUser(network, promodels.NetworkUserID(u.UserName))
  1068. if err != nil {
  1069. return false
  1070. }
  1071. if netUser.AccessLevel == pro.NET_ADMIN {
  1072. return true
  1073. }
  1074. return logic.StringSliceContains(netUser.Nodes, nodeID)
  1075. }