nodeHttpController.go 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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(http.HandlerFunc(createIngressGateway))).Methods("POST")
  29. r.HandleFunc("/api/nodes/{network}/{macaddress}/deleteingress", securityCheck(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. _, isadmin, errN := functions.VerifyUserToken(authToken)
  162. if errN == nil && isadmin {
  163. macaddress = "mastermac"
  164. isAuthorized = true
  165. } else {
  166. mac, _, err := functions.VerifyToken(authToken)
  167. if err != nil {
  168. errorResponse = models.ErrorResponse{
  169. Code: http.StatusUnauthorized, Message: "W1R3: Error Verifying Auth Token.",
  170. }
  171. returnErrorResponse(w, r, errorResponse)
  172. return
  173. }
  174. macaddress = mac
  175. }
  176. //The mastermac (login with masterkey from config) can do everything!! May be dangerous.
  177. if macaddress == "mastermac" {
  178. isAuthorized = true
  179. //for everyone else, there's poor man's RBAC. The "cases" are defined in the routes in the handlers
  180. //So each route defines which access network should be allowed to access it
  181. } else {
  182. switch authNetwork {
  183. case "all":
  184. isAuthorized = true
  185. case "nodes":
  186. isAuthorized = (macaddress != "")
  187. case "network":
  188. node, err := functions.GetNodeByMacAddress(params["network"], macaddress)
  189. if err != nil {
  190. errorResponse = models.ErrorResponse{
  191. Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
  192. }
  193. returnErrorResponse(w, r, errorResponse)
  194. return
  195. }
  196. isAuthorized = (node.Network == params["network"])
  197. case "node":
  198. isAuthorized = (macaddress == params["macaddress"])
  199. case "master":
  200. isAuthorized = (macaddress == "mastermac")
  201. default:
  202. isAuthorized = false
  203. }
  204. }
  205. if !isAuthorized {
  206. errorResponse = models.ErrorResponse{
  207. Code: http.StatusUnauthorized, Message: "W1R3: You are unauthorized to access this endpoint.",
  208. }
  209. returnErrorResponse(w, r, errorResponse)
  210. return
  211. } else {
  212. //If authorized, this function passes along it's request and output to the appropriate route function.
  213. next.ServeHTTP(w, r)
  214. }
  215. }
  216. }
  217. }
  218. //Gets all nodes associated with network, including pending nodes
  219. func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
  220. w.Header().Set("Content-Type", "application/json")
  221. var nodes []models.Node
  222. var params = mux.Vars(r)
  223. nodes, err := GetNetworkNodes(params["network"])
  224. if err != nil {
  225. returnErrorResponse(w, r, formatError(err, "internal"))
  226. return
  227. }
  228. //Returns all the nodes in JSON format
  229. w.WriteHeader(http.StatusOK)
  230. json.NewEncoder(w).Encode(nodes)
  231. }
  232. func GetNetworkNodes(network string) ([]models.Node, error) {
  233. var nodes []models.Node
  234. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  235. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  236. filter := bson.M{"network": network}
  237. //Filtering out the ID field cuz Dillon doesn't like it. May want to filter out other fields in the future
  238. cur, err := collection.Find(ctx, filter, options.Find().SetProjection(bson.M{"_id": 0}))
  239. if err != nil {
  240. return []models.Node{}, err
  241. }
  242. defer cancel()
  243. for cur.Next(context.TODO()) {
  244. //Using a different model for the Node (other than regular node).
  245. //Either we should do this for ALL structs (so Networks and Keys)
  246. //OR we should just use the original struct
  247. //My preference is to make some new return structs
  248. //TODO: Think about this. Not an immediate concern. Just need to get some consistency eventually
  249. var node models.Node
  250. err := cur.Decode(&node)
  251. if err != nil {
  252. return []models.Node{}, err
  253. }
  254. // add item our array of nodes
  255. nodes = append(nodes, node)
  256. }
  257. //TODO: Another fatal error we should take care of.
  258. if err := cur.Err(); err != nil {
  259. return []models.Node{}, err
  260. }
  261. return nodes, nil
  262. }
  263. //A separate function to get all nodes, not just nodes for a particular network.
  264. //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
  265. func getAllNodes(w http.ResponseWriter, r *http.Request) {
  266. w.Header().Set("Content-Type", "application/json")
  267. nodes, err := functions.GetAllNodes()
  268. if err != nil {
  269. returnErrorResponse(w, r, formatError(err, "internal"))
  270. return
  271. }
  272. //Return all the nodes in JSON format
  273. w.WriteHeader(http.StatusOK)
  274. json.NewEncoder(w).Encode(nodes)
  275. }
  276. //This function get's called when a node "checks in" at check in interval
  277. //Honestly I'm not sure what all it should be doing
  278. //TODO: Implement the necessary stuff, including the below
  279. //Check the last modified of the network
  280. //Check the last modified of the nodes
  281. //Write functions for responding to these two thingies
  282. func checkIn(w http.ResponseWriter, r *http.Request) {
  283. //TODO: Current thoughts:
  284. //Dont bother with a networklastmodified
  285. //Instead, implement a "configupdate" boolean on nodes
  286. //when there is a network update that requrires a config update, then the node will pull its new config
  287. // set header.
  288. w.Header().Set("Content-Type", "application/json")
  289. var params = mux.Vars(r)
  290. node, err := CheckIn(params["network"], params["macaddress"])
  291. if err != nil {
  292. returnErrorResponse(w, r, formatError(err, "internal"))
  293. return
  294. }
  295. w.WriteHeader(http.StatusOK)
  296. json.NewEncoder(w).Encode(node)
  297. }
  298. func CheckIn(network, macaddress string) (models.Node, error) {
  299. var node models.Node
  300. //Retrieves node with DB Call which is inefficient. Let's just get the time and set it.
  301. //node = functions.GetNodeByMacAddress(params["network"], params["macaddress"])
  302. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  303. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  304. filter := bson.M{"macaddress": macaddress, "network": network}
  305. //old code was inefficient, this is all we need.
  306. time := time.Now().Unix()
  307. //node.SetLastCheckIn()
  308. // prepare update model with new time
  309. update := bson.D{
  310. {"$set", bson.D{
  311. {"lastcheckin", time},
  312. }},
  313. }
  314. err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
  315. defer cancel()
  316. if err != nil {
  317. return models.Node{}, err
  318. }
  319. //TODO: check node last modified vs network last modified
  320. //Get Updated node to return
  321. node, err = GetNode(macaddress, network)
  322. if err != nil {
  323. return models.Node{}, err
  324. }
  325. return node, nil
  326. }
  327. //Get an individual node. Nothin fancy here folks.
  328. func getNode(w http.ResponseWriter, r *http.Request) {
  329. // set header.
  330. w.Header().Set("Content-Type", "application/json")
  331. var params = mux.Vars(r)
  332. node, err := GetNode(params["macaddress"], params["network"])
  333. if err != nil {
  334. returnErrorResponse(w, r, formatError(err, "internal"))
  335. return
  336. }
  337. w.WriteHeader(http.StatusOK)
  338. json.NewEncoder(w).Encode(node)
  339. }
  340. //Get the time that a network of nodes was last modified.
  341. //TODO: This needs to be refactored
  342. //Potential way to do this: On UpdateNode, set a new field for "LastModified"
  343. //If we go with the existing way, we need to at least set network.NodesLastModified on UpdateNode
  344. func getLastModified(w http.ResponseWriter, r *http.Request) {
  345. // set header.
  346. w.Header().Set("Content-Type", "application/json")
  347. var params = mux.Vars(r)
  348. network, err := GetLastModified(params["network"])
  349. if err != nil {
  350. returnErrorResponse(w, r, formatError(err, "internal"))
  351. return
  352. }
  353. w.WriteHeader(http.StatusOK)
  354. json.NewEncoder(w).Encode(network.NodesLastModified)
  355. }
  356. func GetLastModified(network string) (models.Network, error) {
  357. var net models.Network
  358. collection := mongoconn.Client.Database("netmaker").Collection("networks")
  359. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  360. filter := bson.M{"netid": network}
  361. err := collection.FindOne(ctx, filter).Decode(&net)
  362. defer cancel()
  363. if err != nil {
  364. fmt.Println(err)
  365. return models.Network{}, err
  366. }
  367. return net, nil
  368. }
  369. //This one's a doozy
  370. //To create a node
  371. //Must have valid key and be unique
  372. func createNode(w http.ResponseWriter, r *http.Request) {
  373. w.Header().Set("Content-Type", "application/json")
  374. var params = mux.Vars(r)
  375. var errorResponse = models.ErrorResponse{
  376. Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
  377. }
  378. networkName := params["network"]
  379. //Check if network exists first
  380. //TODO: This is inefficient. Let's find a better way.
  381. //Just a few rows down we grab the network anyway
  382. networkexists, err := functions.NetworkExists(networkName)
  383. if err != nil {
  384. returnErrorResponse(w, r, formatError(err, "internal"))
  385. return
  386. } else if !networkexists {
  387. errorResponse = models.ErrorResponse{
  388. Code: http.StatusNotFound, Message: "W1R3: Network does not exist! ",
  389. }
  390. returnErrorResponse(w, r, errorResponse)
  391. return
  392. }
  393. var node models.Node
  394. //get node from body of request
  395. err = json.NewDecoder(r.Body).Decode(&node)
  396. if err != nil {
  397. returnErrorResponse(w, r, formatError(err, "internal"))
  398. return
  399. }
  400. node.Network = networkName
  401. network, err := node.GetNetwork()
  402. if err != nil {
  403. returnErrorResponse(w, r, formatError(err, "internal"))
  404. return
  405. }
  406. //Check to see if key is valid
  407. //TODO: Triple inefficient!!! This is the third call to the DB we make for networks
  408. validKey := functions.IsKeyValid(networkName, node.AccessKey)
  409. if !validKey {
  410. //Check to see if network will allow manual sign up
  411. //may want to switch this up with the valid key check and avoid a DB call that way.
  412. if *network.AllowManualSignUp {
  413. node.IsPending = true
  414. } else {
  415. errorResponse = models.ErrorResponse{
  416. Code: http.StatusUnauthorized, Message: "W1R3: Key invalid, or none provided.",
  417. }
  418. returnErrorResponse(w, r, errorResponse)
  419. return
  420. }
  421. }
  422. err = ValidateNodeCreate(networkName, node)
  423. if err != nil {
  424. returnErrorResponse(w, r, formatError(err, "badrequest"))
  425. return
  426. }
  427. node, err = CreateNode(node, networkName)
  428. if err != nil {
  429. returnErrorResponse(w, r, formatError(err, "internal"))
  430. return
  431. }
  432. w.WriteHeader(http.StatusOK)
  433. json.NewEncoder(w).Encode(node)
  434. }
  435. //Takes node out of pending state
  436. //TODO: May want to use cordon/uncordon terminology instead of "ispending".
  437. func uncordonNode(w http.ResponseWriter, r *http.Request) {
  438. var params = mux.Vars(r)
  439. w.Header().Set("Content-Type", "application/json")
  440. node, err := UncordonNode(params["network"], params["macaddress"])
  441. if err != nil {
  442. returnErrorResponse(w, r, formatError(err, "internal"))
  443. return
  444. }
  445. fmt.Println("Node " + node.Name + " uncordoned.")
  446. w.WriteHeader(http.StatusOK)
  447. json.NewEncoder(w).Encode("SUCCESS")
  448. }
  449. func UncordonNode(network, macaddress string) (models.Node, error) {
  450. node, err := functions.GetNodeByMacAddress(network, macaddress)
  451. if err != nil {
  452. return models.Node{}, err
  453. }
  454. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  455. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  456. // Create filter
  457. filter := bson.M{"macaddress": macaddress, "network": network}
  458. node.SetLastModified()
  459. fmt.Println("Uncordoning node " + node.Name)
  460. // prepare update model.
  461. update := bson.D{
  462. {"$set", bson.D{
  463. {"ispending", false},
  464. }},
  465. }
  466. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&node)
  467. defer cancel()
  468. if err != nil {
  469. return models.Node{}, err
  470. }
  471. return node, nil
  472. }
  473. func createEgressGateway(w http.ResponseWriter, r *http.Request) {
  474. var gateway models.EgressGatewayRequest
  475. var params = mux.Vars(r)
  476. w.Header().Set("Content-Type", "application/json")
  477. err := json.NewDecoder(r.Body).Decode(&gateway)
  478. if err != nil {
  479. returnErrorResponse(w, r, formatError(err, "internal"))
  480. return
  481. }
  482. gateway.NetID = params["network"]
  483. gateway.NodeID = params["macaddress"]
  484. node, err := CreateEgressGateway(gateway)
  485. if err != nil {
  486. returnErrorResponse(w, r, formatError(err, "internal"))
  487. return
  488. }
  489. w.WriteHeader(http.StatusOK)
  490. json.NewEncoder(w).Encode(node)
  491. }
  492. func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, error) {
  493. node, err := functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
  494. if err != nil {
  495. return models.Node{}, err
  496. }
  497. err = ValidateEgressGateway(gateway)
  498. if err != nil {
  499. return models.Node{}, err
  500. }
  501. var nodechange models.Node
  502. nodechange.IsEgressGateway = true
  503. nodechange.EgressGatewayRange = gateway.RangeString
  504. nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
  505. nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + gateway.Interface + " -j MASQUERADE"
  506. if gateway.PostUp != "" {
  507. nodechange.PostUp = gateway.PostUp
  508. }
  509. if gateway.PostDown != "" {
  510. nodechange.PostDown = gateway.PostDown
  511. }
  512. if node.PostUp != "" {
  513. if !strings.Contains(node.PostUp, nodechange.PostUp) {
  514. nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp
  515. } else {
  516. nodechange.PostUp = node.PostUp
  517. }
  518. }
  519. if node.PostDown != "" {
  520. if !strings.Contains(node.PostDown, nodechange.PostDown) {
  521. nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
  522. } else {
  523. nodechange.PostDown = node.PostDown
  524. }
  525. }
  526. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  527. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  528. // Create filter
  529. filter := bson.M{"macaddress": gateway.NodeID, "network": gateway.NetID}
  530. nodechange.SetLastModified()
  531. // prepare update model.
  532. update := bson.D{
  533. {"$set", bson.D{
  534. {"postup", nodechange.PostUp},
  535. {"postdown", nodechange.PostDown},
  536. {"isegressgateway", nodechange.IsEgressGateway},
  537. {"egressgatewayrange", nodechange.EgressGatewayRange},
  538. {"lastmodified", nodechange.LastModified},
  539. }},
  540. }
  541. var nodeupdate models.Node
  542. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  543. defer cancel()
  544. if err != nil {
  545. return models.Node{}, err
  546. }
  547. err = SetNetworkNodesLastModified(gateway.NetID)
  548. if err != nil {
  549. return models.Node{}, err
  550. }
  551. //Get updated values to return
  552. node, err = functions.GetNodeByMacAddress(gateway.NetID, gateway.NodeID)
  553. if err != nil {
  554. return models.Node{}, err
  555. }
  556. return node, nil
  557. }
  558. func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
  559. var err error
  560. isIp := functions.IsIpCIDR(gateway.RangeString)
  561. empty := gateway.RangeString == ""
  562. if empty || !isIp {
  563. err = errors.New("IP Range Not Valid")
  564. }
  565. empty = gateway.Interface == ""
  566. if empty {
  567. err = errors.New("Interface cannot be empty")
  568. }
  569. return err
  570. }
  571. func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
  572. w.Header().Set("Content-Type", "application/json")
  573. var params = mux.Vars(r)
  574. node, err := DeleteEgressGateway(params["network"], params["macaddress"])
  575. if err != nil {
  576. returnErrorResponse(w, r, formatError(err, "internal"))
  577. return
  578. }
  579. w.WriteHeader(http.StatusOK)
  580. json.NewEncoder(w).Encode(node)
  581. }
  582. func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
  583. var nodeupdate models.Node
  584. var nodechange models.Node
  585. node, err := functions.GetNodeByMacAddress(network, macaddress)
  586. if err != nil {
  587. return models.Node{}, err
  588. }
  589. nodechange.IsEgressGateway = false
  590. nodechange.EgressGatewayRange = ""
  591. nodechange.PostUp = ""
  592. nodechange.PostDown = ""
  593. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  594. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  595. // Create filter
  596. filter := bson.M{"macaddress": macaddress, "network": network}
  597. nodechange.SetLastModified()
  598. // prepare update model.
  599. update := bson.D{
  600. {"$set", bson.D{
  601. {"postup", nodechange.PostUp},
  602. {"postdown", nodechange.PostDown},
  603. {"isegressgateway", nodechange.IsEgressGateway},
  604. {"egressgatewayrange", nodechange.EgressGatewayRange},
  605. {"lastmodified", nodechange.LastModified},
  606. }},
  607. }
  608. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  609. defer cancel()
  610. if err != nil {
  611. return models.Node{}, err
  612. }
  613. err = SetNetworkNodesLastModified(network)
  614. if err != nil {
  615. return models.Node{}, err
  616. }
  617. //Get updated values to return
  618. node, err = functions.GetNodeByMacAddress(network, macaddress)
  619. if err != nil {
  620. return models.Node{}, err
  621. }
  622. return node, nil
  623. }
  624. // == INGRESS ==
  625. func createIngressGateway(w http.ResponseWriter, r *http.Request) {
  626. var params = mux.Vars(r)
  627. w.Header().Set("Content-Type", "application/json")
  628. node, err := CreateIngressGateway(params["network"], params["macaddress"])
  629. if err != nil {
  630. returnErrorResponse(w, r, formatError(err, "internal"))
  631. return
  632. }
  633. w.WriteHeader(http.StatusOK)
  634. json.NewEncoder(w).Encode(node)
  635. }
  636. func CreateIngressGateway(netid string, macaddress string) (models.Node, error) {
  637. node, err := functions.GetNodeByMacAddress(netid, macaddress)
  638. if err != nil {
  639. return models.Node{}, err
  640. }
  641. network, err := functions.GetParentNetwork(netid)
  642. if err != nil {
  643. log.Println("Could not find network.")
  644. return models.Node{}, err
  645. }
  646. var nodechange models.Node
  647. nodechange.IngressGatewayRange = network.AddressRange
  648. nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE"
  649. nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
  650. if node.PostUp != "" {
  651. if !strings.Contains(node.PostUp, nodechange.PostUp) {
  652. nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp
  653. } else {
  654. nodechange.PostUp = node.PostUp
  655. }
  656. }
  657. if node.PostDown != "" {
  658. if !strings.Contains(node.PostDown, nodechange.PostDown) {
  659. nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
  660. } else {
  661. nodechange.PostDown = node.PostDown
  662. }
  663. }
  664. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  665. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  666. // Create filter
  667. filter := bson.M{"macaddress": macaddress, "network": netid}
  668. node.SetLastModified()
  669. // prepare update model.
  670. update := bson.D{
  671. {"$set", bson.D{
  672. {"postup", nodechange.PostUp},
  673. {"postdown", nodechange.PostDown},
  674. {"isingressgateway", true},
  675. {"ingressgatewayrange", nodechange.IngressGatewayRange},
  676. {"lastmodified", node.LastModified},
  677. }},
  678. }
  679. var nodeupdate models.Node
  680. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  681. defer cancel()
  682. if err != nil {
  683. log.Println("error updating node to gateway")
  684. return models.Node{}, err
  685. }
  686. err = SetNetworkNodesLastModified(netid)
  687. if err != nil {
  688. return node, err
  689. }
  690. //Get updated values to return
  691. node, err = functions.GetNodeByMacAddress(netid, macaddress)
  692. if err != nil {
  693. log.Println("error finding node after update")
  694. return node, err
  695. }
  696. return node, nil
  697. }
  698. func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
  699. w.Header().Set("Content-Type", "application/json")
  700. var params = mux.Vars(r)
  701. node, err := DeleteIngressGateway(params["network"], params["macaddress"])
  702. if err != nil {
  703. returnErrorResponse(w, r, formatError(err, "internal"))
  704. return
  705. }
  706. w.WriteHeader(http.StatusOK)
  707. json.NewEncoder(w).Encode(node)
  708. }
  709. func DeleteIngressGateway(network, macaddress string) (models.Node, error) {
  710. var nodeupdate models.Node
  711. node, err := functions.GetNodeByMacAddress(network, macaddress)
  712. if err != nil {
  713. return models.Node{}, err
  714. }
  715. collection := mongoconn.Client.Database("netmaker").Collection("nodes")
  716. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
  717. // Create filter
  718. filter := bson.M{"macaddress": macaddress, "network": network}
  719. // prepare update model.
  720. update := bson.D{
  721. {"$set", bson.D{
  722. {"lastmodified", time.Now().Unix()},
  723. {"isingressgateway", false},
  724. }},
  725. }
  726. err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
  727. defer cancel()
  728. if err != nil {
  729. return models.Node{}, err
  730. }
  731. err = SetNetworkNodesLastModified(network)
  732. if err != nil {
  733. return models.Node{}, err
  734. }
  735. //Get updated values to return
  736. node, err = functions.GetNodeByMacAddress(network, macaddress)
  737. if err != nil {
  738. return models.Node{}, err
  739. }
  740. return node, nil
  741. }
  742. func updateNode(w http.ResponseWriter, r *http.Request) {
  743. w.Header().Set("Content-Type", "application/json")
  744. var params = mux.Vars(r)
  745. //Get id from parameters
  746. //id, _ := primitive.ObjectIDFromHex(params["id"])
  747. var node models.Node
  748. //start here
  749. node, err := functions.GetNodeByMacAddress(params["network"], params["macaddress"])
  750. if err != nil {
  751. returnErrorResponse(w, r, formatError(err, "internal"))
  752. return
  753. }
  754. var nodechange models.NodeUpdate
  755. // we decode our body request params
  756. _ = json.NewDecoder(r.Body).Decode(&nodechange)
  757. if nodechange.Network == "" {
  758. nodechange.Network = node.Network
  759. }
  760. if nodechange.MacAddress == "" {
  761. nodechange.MacAddress = node.MacAddress
  762. }
  763. err = ValidateNodeUpdate(params["network"], nodechange)
  764. if err != nil {
  765. returnErrorResponse(w, r, formatError(err, "badrequest"))
  766. return
  767. }
  768. node, err = UpdateNode(nodechange, node)
  769. if err != nil {
  770. returnErrorResponse(w, r, formatError(err, "internal"))
  771. return
  772. }
  773. w.WriteHeader(http.StatusOK)
  774. json.NewEncoder(w).Encode(node)
  775. }
  776. //Delete a node
  777. //Pretty straightforward
  778. func deleteNode(w http.ResponseWriter, r *http.Request) {
  779. // Set header
  780. w.Header().Set("Content-Type", "application/json")
  781. // get params
  782. var params = mux.Vars(r)
  783. success, err := DeleteNode(params["macaddress"], params["network"])
  784. if err != nil {
  785. returnErrorResponse(w, r, formatError(err, "internal"))
  786. return
  787. } else if !success {
  788. err = errors.New("Could not delete node " + params["macaddress"])
  789. returnErrorResponse(w, r, formatError(err, "internal"))
  790. return
  791. }
  792. returnSuccessResponse(w, r, params["macaddress"]+" deleted.")
  793. }