nodeHttpController.go 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. package controller
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "net/http"
  8. "strings"
  9. "time"
  10. "log"
  11. "github.com/gorilla/mux"
  12. "github.com/gravitl/netmaker/functions"
  13. "github.com/gravitl/netmaker/models"
  14. "github.com/gravitl/netmaker/mongoconn"
  15. "go.mongodb.org/mongo-driver/bson"
  16. "go.mongodb.org/mongo-driver/mongo/options"
  17. "golang.org/x/crypto/bcrypt"
  18. )
  19. func nodeHandlers(r *mux.Router) {
  20. r.HandleFunc("/api/nodes", authorize(false, "master", http.HandlerFunc(getAllNodes))).Methods("GET")
  21. r.HandleFunc("/api/nodes/{network}", authorize(true, "network", http.HandlerFunc(getNetworkNodes))).Methods("GET")
  22. r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(getNode))).Methods("GET")
  23. r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(updateNode))).Methods("PUT")
  24. r.HandleFunc("/api/nodes/{network}/{macaddress}", authorize(true, "node", http.HandlerFunc(deleteNode))).Methods("DELETE")
  25. r.HandleFunc("/api/nodes/{network}/{macaddress}/checkin", authorize(true, "node", http.HandlerFunc(checkIn))).Methods("POST")
  26. r.HandleFunc("/api/nodes/{network}/{macaddress}/creategateway", authorize(true, "master", http.HandlerFunc(createEgressGateway))).Methods("POST")
  27. r.HandleFunc("/api/nodes/{network}/{macaddress}/deletegateway", authorize(true, "master", http.HandlerFunc(deleteEgressGateway))).Methods("DELETE")
  28. r.HandleFunc("/api/nodes/{network}/{macaddress}/createingress", securityCheck(false, http.HandlerFunc(createIngressGateway))).Methods("POST")
  29. r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods("DELETE")
  30. r.HandleFunc("/api/nodes/{network}/{macaddress}/approve", authorize(true, "master", http.HandlerFunc(uncordonNode))).Methods("POST")
  31. r.HandleFunc("/api/nodes/{network}", createNode).Methods("POST")
  32. r.HandleFunc("/api/nodes/adm/{network}/lastmodified", authorize(true, "network", http.HandlerFunc(getLastModified))).Methods("GET")
  33. r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods("POST")
  34. }
  35. //Node authenticates using its password and retrieves a JWT for authorization.
  36. func authenticate(response http.ResponseWriter, request *http.Request) {
  37. //Auth request consists of Mac Address and Password (from node that is authorizing
  38. //in case of Master, auth is ignored and mac is set to "mastermac"
  39. var authRequest models.AuthParams
  40. var result models.Node
  41. var errorResponse = models.ErrorResponse{
  42. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  43. }
  44. //Get password fnd mac rom request
  45. decoder := json.NewDecoder(request.Body)
  46. decoderErr := decoder.Decode(&authRequest)
  47. defer request.Body.Close()
  48. if decoderErr != nil {
  49. errorResponse.Code = http.StatusBadRequest
  50. errorResponse.Message = decoderErr.Error()
  51. returnErrorResponse(response, request, errorResponse)
  52. return
  53. } else {
  54. errorResponse.Code = http.StatusBadRequest
  55. if authRequest.MacAddress == "" {
  56. errorResponse.Message = "W1R3: MacAddress can't be empty"
  57. returnErrorResponse(response, request, errorResponse)
  58. return
  59. } else if authRequest.Password == "" {
  60. errorResponse.Message = "W1R3: Password can't be empty"
  61. returnErrorResponse(response, request, errorResponse)
  62. return
  63. } else {
  64. //Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).
  65. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  66. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  67. var err = collection.FindOne(ctx, bson.M{"macaddress": authRequest.MacAddress, "ispending": false}).Decode(&result)
  68. defer cancel()
  69. if err != nil {
  70. errorResponse.Code = http.StatusBadRequest
  71. errorResponse.Message = err.Error()
  72. returnErrorResponse(response, request, errorResponse)
  73. return
  74. }
  75. //compare password from request to stored password in database
  76. //might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
  77. //TODO: Consider a way of hashing the password client side before sending, or using certificates
  78. err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(authRequest.Password))
  79. if err != nil {
  80. errorResponse.Code = http.StatusBadRequest
  81. errorResponse.Message = err.Error()
  82. returnErrorResponse(response, request, errorResponse)
  83. return
  84. } else {
  85. //Create a new JWT for the node
  86. tokenString, _ := functions.CreateJWT(authRequest.MacAddress, result.Network)
  87. if tokenString == "" {
  88. errorResponse.Code = http.StatusBadRequest
  89. errorResponse.Message = "Could not create Token"
  90. returnErrorResponse(response, request, errorResponse)
  91. return
  92. }
  93. var successResponse = models.SuccessResponse{
  94. Code: http.StatusOK,
  95. Message: "W1R3: Device " + authRequest.MacAddress + " Authorized",
  96. Response: models.SuccessfulLoginResponse{
  97. AuthToken: tokenString,
  98. MacAddress: authRequest.MacAddress,
  99. },
  100. }
  101. //Send back the JWT
  102. successJSONResponse, jsonError := json.Marshal(successResponse)
  103. if jsonError != nil {
  104. errorResponse.Code = http.StatusBadRequest
  105. errorResponse.Message = err.Error()
  106. returnErrorResponse(response, request, errorResponse)
  107. return
  108. }
  109. response.WriteHeader(http.StatusOK)
  110. response.Header().Set("Content-Type", "application/json")
  111. response.Write(successJSONResponse)
  112. }
  113. }
  114. }
  115. }
  116. //The middleware for most requests to the API
  117. //They all pass through here first
  118. //This will validate the JWT (or check for master token)
  119. //This will also check against the authNetwork and make sure the node should be accessing that endpoint,
  120. //even if it's technically ok
  121. //This is kind of a poor man's RBAC. There's probably a better/smarter way.
  122. //TODO: Consider better RBAC implementations
  123. func authorize(networkCheck bool, authNetwork string, next http.Handler) http.HandlerFunc {
  124. return func(w http.ResponseWriter, r *http.Request) {
  125. var errorResponse = models.ErrorResponse{
  126. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  127. }
  128. var params = mux.Vars(r)
  129. networkexists, _ := functions.NetworkExists(params["network"])
  130. //check that the request is for a valid network
  131. //if (networkCheck && !networkexists) || err != nil {
  132. if networkCheck && !networkexists {
  133. errorResponse = models.ErrorResponse{
  134. Code: http.StatusNotFound, Message: "W1R3: This network does not exist. ",
  135. }
  136. returnErrorResponse(w, r, errorResponse)
  137. return
  138. } else {
  139. w.Header().Set("Content-Type", "application/json")
  140. //get the auth token
  141. bearerToken := r.Header.Get("Authorization")
  142. var tokenSplit = strings.Split(bearerToken, " ")
  143. //I put this in in case the user doesn't put in a token at all (in which case it's empty)
  144. //There's probably a smarter way of handling this.
  145. var authToken = "928rt238tghgwe@TY@$Y@#WQAEGB2FC#@HG#@$Hddd"
  146. if len(tokenSplit) > 1 {
  147. authToken = tokenSplit[1]
  148. } else {
  149. errorResponse = models.ErrorResponse{
  150. Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
  151. }
  152. returnErrorResponse(w, r, errorResponse)
  153. return
  154. }
  155. //This checks if
  156. //A: the token is the master password
  157. //B: the token corresponds to a mac address, and if so, which one
  158. //TODO: There's probably a better way of dealing with the "master token"/master password. Plz Halp.
  159. var isAuthorized = false
  160. var macaddress = ""
  161. _, networks, isadmin, errN := functions.VerifyUserToken(authToken)
  162. isnetadmin := isadmin
  163. if errN == nil && isadmin {
  164. macaddress = "mastermac"
  165. isAuthorized = true
  166. } else {
  167. mac, _, err := functions.VerifyToken(authToken)
  168. if err != nil {
  169. errorResponse = models.ErrorResponse{
  170. Code: http.StatusUnauthorized, Message: "W1R3: Error Verifying Auth Token.",
  171. }
  172. returnErrorResponse(w, r, errorResponse)
  173. return
  174. }
  175. macaddress = mac
  176. }
  177. if !isadmin && params["network"] != ""{
  178. if functions.SliceContains(networks, params["network"]){
  179. isnetadmin = true
  180. }
  181. }
  182. //The mastermac (login with masterkey from config) can do everything!! May be dangerous.
  183. if macaddress == "mastermac" {
  184. isAuthorized = true
  185. //for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
  186. //So each route defines which access network should be allowed to access it
  187. } else {
  188. switch authNetwork {
  189. case "all":
  190. isAuthorized = true
  191. case "nodes":
  192. isAuthorized = (macaddress != "") || isnetadmin
  193. case "network":
  194. if isnetadmin {
  195. isAuthorized = true
  196. } else {
  197. node, err := functions.GetNodeByMacAddress(params["network"], macaddress)
  198. if err != nil {
  199. errorResponse = models.ErrorResponse{
  200. Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
  201. }
  202. returnErrorResponse(w, r, errorResponse)
  203. return
  204. }
  205. isAuthorized = (node.Network == params["network"])
  206. }
  207. case "node":
  208. if isnetadmin {
  209. isAuthorized = true
  210. } else {
  211. isAuthorized = (macaddress == params["macaddress"])
  212. }
  213. case "master":
  214. isAuthorized = (macaddress == "mastermac")
  215. default:
  216. isAuthorized = false
  217. }
  218. }
  219. if !isAuthorized {
  220. errorResponse = models.ErrorResponse{
  221. Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
  222. }
  223. returnErrorResponse(w, r, errorResponse)
  224. return
  225. } else {
  226. //If authorized, this function passes along it's request and output to the appropriate route function.
  227. next.ServeHTTP(w, r)
  228. }
  229. }
  230. }
  231. }
  232. //Gets all nodes associated with network, including pending nodes
  233. func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
  234. w.Header().Set("Content-Type", "application/json")
  235. var nodes []models.Node
  236. var params = mux.Vars(r)
  237. nodes, err := GetNetworkNodes(params["network"])
  238. if err != nil {
  239. returnErrorResponse(w, r, formatError(err, "internal"))
  240. return
  241. }
  242. //Returns all the nodes in JSON format
  243. w.WriteHeader(http.StatusOK)
  244. json.NewEncoder(w).Encode(nodes)
  245. }
  246. func GetNetworkNodes(network string) ([]models.Node, error) {
  247. var nodes []models.Node
  248. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  249. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  250. filter := bson.M{"network": network}
  251. //Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
  252. cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
  253. if err != nil {
  254. return []models.Node{}, err
  255. }
  256. defer cancel()
  257. for cur.Next(context.TODO()) {
  258. //Using a different model for the Node (other than regular node).
  259. //Either we should do this for ALL structs (so Networks and Keys)
  260. //OR we should just use the original struct
  261. //My preference is to make some new return structs
  262. //TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
  263. var node models.Node
  264. err := cur.Decode(&node)
  265. if err != nil {
  266. return []models.Node{}, err
  267. }
  268. // add item our array of nodes
  269. nodes = append(nodes, node)
  270. }
  271. //TODO: Another fatal error we should take care of.
  272. if err := cur.Err(); err != nil {
  273. return []models.Node{}, err
  274. }
  275. return nodes, nil
  276. }
  277. //A separate function to get all nodes, not just nodes for a particular network.
  278. //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
  279. func getAllNodes(w http.ResponseWriter, r *http.Request) {
  280. w.Header().Set("Content-Type", "application/json")
  281. nodes, err := functions.GetAllNodes()
  282. if err != nil {
  283. returnErrorResponse(w, r, formatError(err, "internal"))
  284. return
  285. }
  286. //Return all the nodes in JSON format
  287. w.WriteHeader(http.StatusOK)
  288. json.NewEncoder(w).Encode(nodes)
  289. }
  290. //This function get's called when a node "checks in" at check in interval
  291. //Honestly I'm not sure what all it should be doing
  292. //TODO: Implement the necessary stuff, including the below
  293. //Check the last modified of the network
  294. //Check the last modified of the nodes
  295. //Write functions for responding to these two thingies
  296. func checkIn(w http.ResponseWriter, r *http.Request) {
  297. //TODO: Current thoughts:
  298. //Dont bother with a networklastmodified
  299. //Instead, implement a "configupdate" boolean on nodes
  300. //when there is a network update that requrires a config update, then the node will pull its new config
  301. // set header.
  302. w.Header().Set("Content-Type", "application/json")
  303. var params = mux.Vars(r)
  304. node, err := CheckIn(params["network"], params["macaddress"])
  305. if err != nil {
  306. returnErrorResponse(w, r, formatError(err, "internal"))
  307. return
  308. }
  309. w.WriteHeader(http.StatusOK)
  310. json.NewEncoder(w).Encode(node)
  311. }
  312. func CheckIn(network, macaddress string) (models.Node, error) {
  313. var node models.Node
  314. //Retrieves node with DB Call which is inefficient. Let's just get the time and set it.
  315. //node = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
  316. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  317. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  318. filter := bson.M{"macaddress": macaddress, "network": network}
  319. //old code was inefficient, this is all we need.
  320. time := time.Now().Unix()
  321. //node.SetLastCheckIn()
  322. // prepare update model with new time
  323. update := bson.D{
  324. {"$set", bson.D{
  325. {"lastcheckin", time},
  326. }},
  327. }
  328. err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
  329. defer cancel()
  330. if err != nil {
  331. return models.Node{}, err
  332. }
  333. //TODO: check node last modified vs network last modified
  334. //Get Updated node to return
  335. node, err = GetNode(macaddress, network)
  336. if err != nil {
  337. return models.Node{}, err
  338. }
  339. return node, nil
  340. }
  341. //Get an individual node. Nothin fancy here folks.
  342. func getNode(w http.ResponseWriter, r *http.Request) {
  343. // set header.
  344. w.Header().Set("Content-Type", "application/json")
  345. var params = mux.Vars(r)
  346. node, err := GetNode(params["macaddress"], params["network"])
  347. if err != nil {
  348. returnErrorResponse(w, r, formatError(err, "internal"))
  349. return
  350. }
  351. w.WriteHeader(http.StatusOK)
  352. json.NewEncoder(w).Encode(node)
  353. }
  354. //Get the time that a network of nodes was last modified.
  355. //TODO: This needs to be refactored
  356. //Potential way to do this: On UpdateNode, set a new field for "LastModified"
  357. //If we go with the existing way, we need to at least set network.NodesLastModified on UpdateNode
  358. func getLastModified(w http.ResponseWriter, r *http.Request) {
  359. // set header.
  360. w.Header().Set("Content-Type", "application/json")
  361. var params = mux.Vars(r)
  362. network, err := GetLastModified(params["network"])
  363. if err != nil {
  364. returnErrorResponse(w, r, formatError(err, "internal"))
  365. return
  366. }
  367. w.WriteHeader(http.StatusOK)
  368. json.NewEncoder(w).Encode(network.NodesLastModified)
  369. }
  370. func GetLastModified(network string) (models.Network, error) {
  371. var net models.Network
  372. collection := mongoconn.Client.Database("netmaker").Collection("networks")
  373. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  374. filter := bson.M{"netid": network}
  375. err := collection.FindOne(ctx, filter).Decode(&net)
  376. defer cancel()
  377. if err != nil {
  378. fmt.Println(err)
  379. return models.Network{}, err
  380. }
  381. return net, nil
  382. }
  383. //This one's a doozy
  384. //To create a node
  385. //Must have valid key and be unique
  386. func createNode(w http.ResponseWriter, r *http.Request) {
  387. w.Header().Set("Content-Type", "application/json")
  388. var params = mux.Vars(r)
  389. var errorResponse = models.ErrorResponse{
  390. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  391. }
  392. networkName := params["network"]
  393. //Check if network exists first
  394. //TODO: This is inefficient. Let's find a better way.
  395. //Just a few rows down we grab the network anyway
  396. networkexists, err := functions.NetworkExists(networkName)
  397. if err != nil {
  398. returnErrorResponse(w, r, formatError(err, "internal"))
  399. return
  400. } else if !networkexists {
  401. errorResponse = models.ErrorResponse{
  402. Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ",
  403. }
  404. returnErrorResponse(w, r, errorResponse)
  405. return
  406. }
  407. var node models.Node
  408. //get node from body of request
  409. err = json.NewDecoder(r.Body).Decode(&node)
  410. if err != nil {
  411. returnErrorResponse(w, r, formatError(err, "internal"))
  412. return
  413. }
  414. node.Network = networkName
  415. network, err := node.GetNetwork()
  416. if err != nil {
  417. returnErrorResponse(w, r, formatError(err, "internal"))
  418. return
  419. }
  420. //Check to see if key is valid
  421. //TODO: Triple inefficient!!! This is the third call to the DB we make for networks
  422. validKey := functions.IsKeyValid(networkName, node.AccessKey)
  423. if !validKey {
  424. //Check to see if network will allow manual sign up
  425. //may want to switch this up with the valid key check and avoid a DB call that way.
  426. if *network.AllowManualSignUp {
  427. node.IsPending = true
  428. } else {
  429. errorResponse = models.ErrorResponse{
  430. Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
  431. }
  432. returnErrorResponse(w, r, errorResponse)
  433. return
  434. }
  435. }
  436. err = ValidateNodeCreate(networkName, node)
  437. if err != nil {
  438. returnErrorResponse(w, r, formatError(err, "badrequest"))
  439. return
  440. }
  441. node, err = CreateNode(node, networkName)
  442. if err != nil {
  443. returnErrorResponse(w, r, formatError(err, "internal"))
  444. return
  445. }
  446. w.WriteHeader(http.StatusOK)
  447. json.NewEncoder(w).Encode(node)
  448. }
  449. //Takes node out of pending state
  450. //TODO: May want to use cordon/uncordon terminology instead of "ispending".
  451. func uncordonNode(w http.ResponseWriter, r *http.Request) {
  452. var params = mux.Vars(r)
  453. w.Header().Set("Content-Type", "application/json")
  454. node, err := UncordonNode(params["network"], params["macaddress"])
  455. if err != nil {
  456. returnErrorResponse(w, r, formatError(err, "internal"))
  457. return
  458. }
  459. fmt.Println("Node " + node.Name + " uncordoned.")
  460. w.WriteHeader(http.StatusOK)
  461. json.NewEncoder(w).Encode("SUCCESS")
  462. }
  463. func UncordonNode(network, macaddress string) (models.Node, error) {
  464. node, err := functions.GetNodeByMacAddress(network, macaddress)
  465. if err != nil {
  466. return models.Node{}, err
  467. }
  468. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  469. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  470. // Create filter
  471. filter := bson.M{"macaddress": macaddress, "network": network}
  472. node.SetLastModified()
  473. fmt.Println("Uncordoning node " + node.Name)
  474. // prepare update model.
  475. update := bson.D{
  476. {"$set", bson.D{
  477. {"ispending", false},
  478. }},
  479. }
  480. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
  481. defer cancel()
  482. if err != nil {
  483. return models.Node{}, err
  484. }
  485. return node, nil
  486. }
  487. func createEgressGateway(w http.ResponseWriter, r *http.Request) {
  488. var gateway models.EgressGatewayRequest
  489. var params = mux.Vars(r)
  490. w.Header().Set("Content-Type", "application/json")
  491. err := json.NewDecoder(r.Body).Decode(&gateway)
  492. if err != nil {
  493. returnErrorResponse(w, r, formatError(err, "internal"))
  494. return
  495. }
  496. gateway.NetID = params["network"]
  497. gateway.NodeID = params["macaddress"]
  498. node, err := CreateEgressGateway(gateway)
  499. if err != nil {
  500. returnErrorResponse(w, r, formatError(err, "internal"))
  501. return
  502. }
  503. w.WriteHeader(http.StatusOK)
  504. json.NewEncoder(w).Encode(node)
  505. }
  506. func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
  507. node, err := functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
  508. if err != nil {
  509. return models.Node{}, err
  510. }
  511. err = ValidateEgressGateway(gateway)
  512. if err != nil {
  513. return models.Node{}, err
  514. }
  515. var nodechange models.Node
  516. nodechange.IsEgressGateway = true
  517. nodechange.EgressGatewayRanges = gateway.Ranges
  518. nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
  519. nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
  520. if gateway.PostUp != "" {
  521. nodechange.PostUp = gateway.PostUp
  522. }
  523. if gateway.PostDown != "" {
  524. nodechange.PostDown = gateway.PostDown
  525. }
  526. if node.PostUp != "" {
  527. if !strings.Contains(node.PostUp, nodechange.PostUp) {
  528. nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp
  529. } else {
  530. nodechange.PostUp = node.PostUp
  531. }
  532. }
  533. if node.PostDown != "" {
  534. if !strings.Contains(node.PostDown, nodechange.PostDown) {
  535. nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
  536. } else {
  537. nodechange.PostDown = node.PostDown
  538. }
  539. }
  540. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  541. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  542. // Create filter
  543. filter := bson.M{"macaddress": gateway.NodeID, "network": gateway.NetID}
  544. nodechange.SetLastModified()
  545. // prepare update model.
  546. update := bson.D{
  547. {"$set", bson.D{
  548. {"postup", nodechange.PostUp},
  549. {"postdown", nodechange.PostDown},
  550. {"isegressgateway", nodechange.IsEgressGateway},
  551. {"egressgatewayrange", nodechange.EgressGatewayRange},
  552. {"lastmodified", nodechange.LastModified},
  553. }},
  554. }
  555. var nodeupdate models.Node
  556. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  557. defer cancel()
  558. if err != nil {
  559. return models.Node{}, err
  560. }
  561. err = SetNetworkNodesLastModified(gateway.NetID)
  562. if err != nil {
  563. return models.Node{}, err
  564. }
  565. //Get updated values to return
  566. node, err = functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
  567. if err != nil {
  568. return models.Node{}, err
  569. }
  570. return node, nil
  571. }
  572. func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
  573. var err error
  574. isIp := functions.IsIpCIDR(gateway.RangeString)
  575. empty := gateway.RangeString == ""
  576. if empty || !isIp {
  577. err = errors.New("IP Range Not Valid")
  578. }
  579. empty = gateway.Interface == ""
  580. if empty {
  581. err = errors.New("Interface cannot be empty")
  582. }
  583. return err
  584. }
  585. func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
  586. w.Header().Set("Content-Type", "application/json")
  587. var params = mux.Vars(r)
  588. node, err := DeleteEgressGateway(params["network"], params["macaddress"])
  589. if err != nil {
  590. returnErrorResponse(w, r, formatError(err, "internal"))
  591. return
  592. }
  593. w.WriteHeader(http.StatusOK)
  594. json.NewEncoder(w).Encode(node)
  595. }
  596. func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
  597. var nodeupdate models.Node
  598. var nodechange models.Node
  599. node, err := functions.GetNodeByMacAddress(network, macaddress)
  600. if err != nil {
  601. return models.Node{}, err
  602. }
  603. nodechange.IsEgressGateway = false
  604. nodechange.EgressGatewayRange = ""
  605. nodechange.PostUp = ""
  606. nodechange.PostDown = ""
  607. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  608. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  609. // Create filter
  610. filter := bson.M{"macaddress": macaddress, "network": network}
  611. nodechange.SetLastModified()
  612. // prepare update model.
  613. update := bson.D{
  614. {"$set", bson.D{
  615. {"postup", nodechange.PostUp},
  616. {"postdown", nodechange.PostDown},
  617. {"isegressgateway", nodechange.IsEgressGateway},
  618. {"egressgatewayrange", nodechange.EgressGatewayRange},
  619. {"lastmodified", nodechange.LastModified},
  620. }},
  621. }
  622. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  623. defer cancel()
  624. if err != nil {
  625. return models.Node{}, err
  626. }
  627. err = SetNetworkNodesLastModified(network)
  628. if err != nil {
  629. return models.Node{}, err
  630. }
  631. //Get updated values to return
  632. node, err = functions.GetNodeByMacAddress(network, macaddress)
  633. if err != nil {
  634. return models.Node{}, err
  635. }
  636. return node, nil
  637. }
  638. // == INGRESS ==
  639. func createIngressGateway(w http.ResponseWriter, r *http.Request) {
  640. var params = mux.Vars(r)
  641. w.Header().Set("Content-Type", "application/json")
  642. node, err := CreateIngressGateway(params["network"], params["macaddress"])
  643. if err != nil {
  644. returnErrorResponse(w, r, formatError(err, "internal"))
  645. return
  646. }
  647. w.WriteHeader(http.StatusOK)
  648. json.NewEncoder(w).Encode(node)
  649. }
  650. func CreateIngressGateway(netid string, macaddress string) (models.Node, error) {
  651. node, err := functions.GetNodeByMacAddress(netid, macaddress)
  652. if err != nil {
  653. return models.Node{}, err
  654. }
  655. network, err := functions.GetParentNetwork(netid)
  656. if err != nil {
  657. log.Println("Could not find network.")
  658. return models.Node{}, err
  659. }
  660. var nodechange models.Node
  661. nodechange.IngressGatewayRange = network.AddressRange
  662. nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE"
  663. nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
  664. if node.PostUp != "" {
  665. if !strings.Contains(node.PostUp, nodechange.PostUp) {
  666. nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp
  667. } else {
  668. nodechange.PostUp = node.PostUp
  669. }
  670. }
  671. if node.PostDown != "" {
  672. if !strings.Contains(node.PostDown, nodechange.PostDown) {
  673. nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
  674. } else {
  675. nodechange.PostDown = node.PostDown
  676. }
  677. }
  678. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  679. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  680. // Create filter
  681. filter := bson.M{"macaddress": macaddress, "network": netid}
  682. node.SetLastModified()
  683. // prepare update model.
  684. update := bson.D{
  685. {"$set", bson.D{
  686. {"postup", nodechange.PostUp},
  687. {"postdown", nodechange.PostDown},
  688. {"isingressgateway", true},
  689. {"ingressgatewayrange", nodechange.IngressGatewayRange},
  690. {"lastmodified", node.LastModified},
  691. }},
  692. }
  693. var nodeupdate models.Node
  694. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  695. defer cancel()
  696. if err != nil {
  697. log.Println("error updating node to gateway")
  698. return models.Node{}, err
  699. }
  700. err = SetNetworkNodesLastModified(netid)
  701. if err != nil {
  702. return node, err
  703. }
  704. //Get updated values to return
  705. node, err = functions.GetNodeByMacAddress(netid, macaddress)
  706. if err != nil {
  707. log.Println("error finding node after update")
  708. return node, err
  709. }
  710. return node, nil
  711. }
  712. func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
  713. w.Header().Set("Content-Type", "application/json")
  714. var params = mux.Vars(r)
  715. node, err := DeleteIngressGateway(params["network"], params["macaddress"])
  716. if err != nil {
  717. returnErrorResponse(w, r, formatError(err, "internal"))
  718. return
  719. }
  720. w.WriteHeader(http.StatusOK)
  721. json.NewEncoder(w).Encode(node)
  722. }
  723. func DeleteIngressGateway(network, macaddress string) (models.Node, error) {
  724. var nodeupdate models.Node
  725. node, err := functions.GetNodeByMacAddress(network, macaddress)
  726. if err != nil {
  727. return models.Node{}, err
  728. }
  729. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  730. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  731. // Create filter
  732. filter := bson.M{"macaddress": macaddress, "network": network}
  733. // prepare update model.
  734. update := bson.D{
  735. {"$set", bson.D{
  736. {"lastmodified", time.Now().Unix()},
  737. {"isingressgateway", false},
  738. }},
  739. }
  740. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  741. defer cancel()
  742. if err != nil {
  743. return models.Node{}, err
  744. }
  745. err = SetNetworkNodesLastModified(network)
  746. if err != nil {
  747. return models.Node{}, err
  748. }
  749. //Get updated values to return
  750. node, err = functions.GetNodeByMacAddress(network, macaddress)
  751. if err != nil {
  752. return models.Node{}, err
  753. }
  754. return node, nil
  755. }
  756. func updateNode(w http.ResponseWriter, r *http.Request) {
  757. w.Header().Set("Content-Type", "application/json")
  758. var params = mux.Vars(r)
  759. //Get id from parameters
  760. //id, _ := primitive.ObjectIDFromHex(params["id"])
  761. var node models.Node
  762. //start here
  763. node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
  764. if err != nil {
  765. returnErrorResponse(w, r, formatError(err, "internal"))
  766. return
  767. }
  768. var nodechange models.NodeUpdate
  769. // we decode our body request params
  770. _ = json.NewDecoder(r.Body).Decode(&nodechange)
  771. if nodechange.Network == "" {
  772. nodechange.Network = node.Network
  773. }
  774. if nodechange.MacAddress == "" {
  775. nodechange.MacAddress = node.MacAddress
  776. }
  777. err = ValidateNodeUpdate(params["network"], nodechange)
  778. if err != nil {
  779. returnErrorResponse(w, r, formatError(err, "badrequest"))
  780. return
  781. }
  782. node, err = UpdateNode(nodechange, node)
  783. if err != nil {
  784. returnErrorResponse(w, r, formatError(err, "internal"))
  785. return
  786. }
  787. w.WriteHeader(http.StatusOK)
  788. json.NewEncoder(w).Encode(node)
  789. }
  790. //Delete a node
  791. //Pretty straightforward
  792. func deleteNode(w http.ResponseWriter, r *http.Request) {
  793. // Set header
  794. w.Header().Set("Content-Type", "application/json")
  795. // get params
  796. var params = mux.Vars(r)
  797. success, err := DeleteNode(params["macaddress"], params["network"])
  798. if err != nil {
  799. returnErrorResponse(w, r, formatError(err, "internal"))
  800. return
  801. } else if !success {
  802. err = errors.New("Could not delete node " + params["macaddress"])
  803. returnErrorResponse(w, r, formatError(err, "internal"))
  804. return
  805. }
  806. returnSuccessResponse(w, r, params["macaddress"]+" deleted.")
  807. }