ext_client.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. package controller
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "net/http"
  8. "reflect"
  9. "strconv"
  10. "strings"
  11. "github.com/go-playground/validator/v10"
  12. "github.com/gorilla/mux"
  13. "github.com/gravitl/netmaker/database"
  14. "github.com/gravitl/netmaker/logger"
  15. "github.com/gravitl/netmaker/logic"
  16. "github.com/gravitl/netmaker/servercfg"
  17. "github.com/gravitl/netmaker/models"
  18. "github.com/gravitl/netmaker/mq"
  19. "github.com/skip2/go-qrcode"
  20. "golang.org/x/exp/slices"
  21. "golang.org/x/exp/slog"
  22. "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
  23. )
  24. func extClientHandlers(r *mux.Router) {
  25. r.HandleFunc("/api/extclients", logic.SecurityCheck(true, http.HandlerFunc(getAllExtClients))).Methods(http.MethodGet)
  26. r.HandleFunc("/api/extclients/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkExtClients))).Methods(http.MethodGet)
  27. r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(getExtClient))).Methods(http.MethodGet)
  28. r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientConf))).Methods(http.MethodGet)
  29. r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(updateExtClient))).Methods(http.MethodPut)
  30. r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(deleteExtClient))).Methods(http.MethodDelete)
  31. r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
  32. }
  33. func checkIngressExists(nodeID string) bool {
  34. node, err := logic.GetNodeByID(nodeID)
  35. if err != nil {
  36. return false
  37. }
  38. return node.IsIngressGateway
  39. }
  40. // swagger:route GET /api/extclients/{network} ext_client getNetworkExtClients
  41. //
  42. // Get all extclients associated with network.
  43. // Gets all extclients associated with network, including pending extclients.
  44. //
  45. // Schemes: https
  46. //
  47. // Security:
  48. // oauth
  49. //
  50. // Responses:
  51. // 200: extClientSliceResponse
  52. func getNetworkExtClients(w http.ResponseWriter, r *http.Request) {
  53. w.Header().Set("Content-Type", "application/json")
  54. var extclients []models.ExtClient
  55. var params = mux.Vars(r)
  56. network := params["network"]
  57. extclients, err := logic.GetNetworkExtClients(network)
  58. if err != nil {
  59. logger.Log(0, r.Header.Get("user"),
  60. fmt.Sprintf("failed to get ext clients for network [%s]: %v", network, err))
  61. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  62. return
  63. }
  64. //Returns all the extclients in JSON format
  65. w.WriteHeader(http.StatusOK)
  66. json.NewEncoder(w).Encode(extclients)
  67. }
  68. // swagger:route GET /api/extclients ext_client getAllExtClients
  69. //
  70. // A separate function to get all extclients, not just extclients for a particular network.
  71. //
  72. // Schemes: https
  73. //
  74. // Security:
  75. // oauth
  76. //
  77. // Responses:
  78. // 200: extClientSliceResponse
  79. //
  80. // Not quite sure if this is necessary. Probably necessary based on front end but may
  81. // want to review after iteration 1 if it's being used or not
  82. func getAllExtClients(w http.ResponseWriter, r *http.Request) {
  83. w.Header().Set("Content-Type", "application/json")
  84. clients, err := logic.GetAllExtClients()
  85. if err != nil && !database.IsEmptyRecord(err) {
  86. logger.Log(0, "failed to get all extclients: ", err.Error())
  87. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  88. return
  89. }
  90. //Return all the extclients in JSON format
  91. logic.SortExtClient(clients[:])
  92. w.WriteHeader(http.StatusOK)
  93. json.NewEncoder(w).Encode(clients)
  94. }
  95. // swagger:route GET /api/extclients/{network}/{clientid} ext_client getExtClient
  96. //
  97. // Get an individual extclient.
  98. //
  99. // Schemes: https
  100. //
  101. // Security:
  102. // oauth
  103. //
  104. // Responses:
  105. // 200: extClientResponse
  106. func getExtClient(w http.ResponseWriter, r *http.Request) {
  107. // set header.
  108. w.Header().Set("Content-Type", "application/json")
  109. var params = mux.Vars(r)
  110. clientid := params["clientid"]
  111. network := params["network"]
  112. client, err := logic.GetExtClient(clientid, network)
  113. if err != nil {
  114. logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
  115. clientid, network, err))
  116. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  117. return
  118. }
  119. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), client) {
  120. // check if user has access to extclient
  121. slog.Error("failed to get extclient", "network", network, "clientID",
  122. clientid, "error", errors.New("access is denied"))
  123. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  124. return
  125. }
  126. w.WriteHeader(http.StatusOK)
  127. json.NewEncoder(w).Encode(client)
  128. }
  129. // swagger:route GET /api/extclients/{network}/{clientid}/{type} ext_client getExtClientConf
  130. //
  131. // Get an individual extclient.
  132. //
  133. // Schemes: https
  134. //
  135. // Security:
  136. // oauth
  137. //
  138. // Responses:
  139. // 200: extClientResponse
  140. func getExtClientConf(w http.ResponseWriter, r *http.Request) {
  141. // set header.
  142. w.Header().Set("Content-Type", "application/json")
  143. var params = mux.Vars(r)
  144. clientid := params["clientid"]
  145. networkid := params["network"]
  146. client, err := logic.GetExtClient(clientid, networkid)
  147. if err != nil {
  148. logger.Log(0, r.Header.Get("user"), fmt.Sprintf("failed to get extclient for [%s] on network [%s]: %v",
  149. clientid, networkid, err))
  150. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  151. return
  152. }
  153. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), client) {
  154. slog.Error("failed to get extclient", "network", networkid, "clientID",
  155. clientid, "error", errors.New("access is denied"))
  156. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  157. return
  158. }
  159. gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
  160. if err != nil {
  161. logger.Log(0, r.Header.Get("user"),
  162. fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
  163. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  164. return
  165. }
  166. host, err := logic.GetHost(gwnode.HostID.String())
  167. if err != nil {
  168. logger.Log(0, r.Header.Get("user"),
  169. fmt.Sprintf("failed to get host for ingress gateway node [%s] info: %v", client.IngressGatewayID, err))
  170. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  171. return
  172. }
  173. network, err := logic.GetParentNetwork(client.Network)
  174. if err != nil {
  175. logger.Log(1, r.Header.Get("user"), "Could not retrieve Ingress Gateway Network", client.Network)
  176. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  177. return
  178. }
  179. preferredIp := strings.TrimSpace(r.URL.Query().Get("preferredip"))
  180. if preferredIp != "" {
  181. allowedPreferredIps := []string{}
  182. for i := range gwnode.AdditionalRagIps {
  183. allowedPreferredIps = append(allowedPreferredIps, gwnode.AdditionalRagIps[i].String())
  184. }
  185. allowedPreferredIps = append(allowedPreferredIps, host.EndpointIP.String())
  186. allowedPreferredIps = append(allowedPreferredIps, host.EndpointIPv6.String())
  187. if !slices.Contains(allowedPreferredIps, preferredIp) {
  188. slog.Warn("preferred endpoint ip is not associated with the RAG. proceeding with preferred ip", "preferred ip", preferredIp)
  189. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("preferred endpoint ip is not associated with the RAG"), "badrequest"))
  190. return
  191. }
  192. if net.ParseIP(preferredIp).To4() == nil {
  193. preferredIp = fmt.Sprintf("[%s]", preferredIp)
  194. }
  195. }
  196. addrString := client.Address
  197. if addrString != "" {
  198. addrString += "/32"
  199. }
  200. if client.Address6 != "" {
  201. if addrString != "" {
  202. addrString += ","
  203. }
  204. addrString += client.Address6 + "/128"
  205. }
  206. keepalive := ""
  207. if network.DefaultKeepalive != 0 {
  208. keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
  209. }
  210. gwendpoint := ""
  211. if preferredIp == "" {
  212. if host.EndpointIP.To4() == nil {
  213. gwendpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), host.ListenPort)
  214. } else {
  215. gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
  216. }
  217. } else {
  218. gwendpoint = fmt.Sprintf("%s:%d", preferredIp, host.ListenPort)
  219. }
  220. var newAllowedIPs string
  221. if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" {
  222. egressrange := "0.0.0.0/0"
  223. if gwnode.Address6.IP != nil && client.Address6 != "" {
  224. egressrange += "," + "::/0"
  225. }
  226. newAllowedIPs = egressrange
  227. } else {
  228. newAllowedIPs = network.AddressRange
  229. if newAllowedIPs != "" && network.AddressRange6 != "" {
  230. newAllowedIPs += ","
  231. }
  232. if network.AddressRange6 != "" {
  233. newAllowedIPs += network.AddressRange6
  234. }
  235. if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
  236. for _, egressGatewayRange := range egressGatewayRanges {
  237. newAllowedIPs += "," + egressGatewayRange
  238. }
  239. }
  240. }
  241. defaultDNS := ""
  242. if client.DNS != "" {
  243. defaultDNS = "DNS = " + client.DNS
  244. } else if gwnode.IngressDNS != "" {
  245. defaultDNS = "DNS = " + gwnode.IngressDNS
  246. }
  247. defaultMTU := 1420
  248. if host.MTU != 0 {
  249. defaultMTU = host.MTU
  250. }
  251. postUp := strings.Builder{}
  252. if client.PostUp != "" && params["type"] != "qr" {
  253. for _, loc := range strings.Split(client.PostUp, "\n") {
  254. postUp.WriteString(fmt.Sprintf("PostUp = %s\n", loc))
  255. }
  256. }
  257. postDown := strings.Builder{}
  258. if client.PostDown != "" && params["type"] != "qr" {
  259. for _, loc := range strings.Split(client.PostDown, "\n") {
  260. postDown.WriteString(fmt.Sprintf("PostDown = %s\n", loc))
  261. }
  262. }
  263. config := fmt.Sprintf(`[Interface]
  264. Address = %s
  265. PrivateKey = %s
  266. MTU = %d
  267. %s
  268. %s
  269. %s
  270. [Peer]
  271. PublicKey = %s
  272. AllowedIPs = %s
  273. Endpoint = %s
  274. %s
  275. `, addrString,
  276. client.PrivateKey,
  277. defaultMTU,
  278. defaultDNS,
  279. postUp.String(),
  280. postDown.String(),
  281. host.PublicKey,
  282. newAllowedIPs,
  283. gwendpoint,
  284. keepalive,
  285. )
  286. if params["type"] == "qr" {
  287. bytes, err := qrcode.Encode(config, qrcode.Medium, 220)
  288. if err != nil {
  289. logger.Log(1, r.Header.Get("user"), "failed to encode qr code: ", err.Error())
  290. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  291. return
  292. }
  293. w.Header().Set("Content-Type", "image/png")
  294. w.WriteHeader(http.StatusOK)
  295. _, err = w.Write(bytes)
  296. if err != nil {
  297. logger.Log(1, r.Header.Get("user"), "response writer error (qr) ", err.Error())
  298. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  299. return
  300. }
  301. return
  302. }
  303. if params["type"] == "file" {
  304. name := client.ClientID + ".conf"
  305. w.Header().Set("Content-Type", "application/config")
  306. w.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
  307. w.WriteHeader(http.StatusOK)
  308. _, err := fmt.Fprint(w, config)
  309. if err != nil {
  310. logger.Log(1, r.Header.Get("user"), "response writer error (file) ", err.Error())
  311. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  312. }
  313. return
  314. }
  315. logger.Log(2, r.Header.Get("user"), "retrieved ext client config")
  316. w.WriteHeader(http.StatusOK)
  317. json.NewEncoder(w).Encode(client)
  318. }
  319. // swagger:route POST /api/extclients/{network}/{nodeid} ext_client createExtClient
  320. //
  321. // Create an individual extclient. Must have valid key and be unique.
  322. //
  323. // Schemes: https
  324. //
  325. // Security:
  326. // oauth
  327. // Responses:
  328. // 200: okResponse
  329. func createExtClient(w http.ResponseWriter, r *http.Request) {
  330. w.Header().Set("Content-Type", "application/json")
  331. var params = mux.Vars(r)
  332. nodeid := params["nodeid"]
  333. ingressExists := checkIngressExists(nodeid)
  334. if !ingressExists {
  335. err := errors.New("ingress does not exist")
  336. slog.Error("failed to create extclient", "user", r.Header.Get("user"), "error", err)
  337. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  338. return
  339. }
  340. var customExtClient models.CustomExtClient
  341. if err := json.NewDecoder(r.Body).Decode(&customExtClient); err != nil {
  342. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  343. return
  344. }
  345. if err := validateCustomExtClient(&customExtClient, true); err != nil {
  346. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  347. return
  348. }
  349. node, err := logic.GetNodeByID(nodeid)
  350. if err != nil {
  351. logger.Log(0, r.Header.Get("user"),
  352. fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", nodeid, err))
  353. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  354. return
  355. }
  356. var userName string
  357. if r.Header.Get("ismaster") == "yes" {
  358. userName = logic.MasterUser
  359. } else {
  360. caller, err := logic.GetUser(r.Header.Get("user"))
  361. if err != nil {
  362. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  363. return
  364. }
  365. userName = caller.UserName
  366. if _, ok := caller.RemoteGwIDs[nodeid]; (!caller.IsAdmin && !caller.IsSuperAdmin) && !ok {
  367. err = errors.New("permission denied")
  368. slog.Error("failed to create extclient", "error", err)
  369. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
  370. return
  371. }
  372. // check if user has a config already for remote access client
  373. extclients, err := logic.GetNetworkExtClients(node.Network)
  374. if err != nil {
  375. slog.Error("failed to get extclients", "error", err)
  376. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  377. return
  378. }
  379. for _, extclient := range extclients {
  380. if extclient.RemoteAccessClientID != "" &&
  381. extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && extclient.OwnerID == caller.UserName && nodeid == extclient.IngressGatewayID {
  382. // extclient on the gw already exists for the remote access client
  383. err = errors.New("remote client config already exists on the gateway")
  384. slog.Error("failed to create extclient", "user", userName, "error", err)
  385. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  386. return
  387. }
  388. }
  389. }
  390. extclient := logic.UpdateExtClient(&models.ExtClient{}, &customExtClient)
  391. extclient.OwnerID = userName
  392. extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
  393. extclient.IngressGatewayID = nodeid
  394. // set extclient dns to ingressdns if extclient dns is not explicitly set
  395. if (extclient.DNS == "") && (node.IngressDNS != "") {
  396. extclient.DNS = node.IngressDNS
  397. }
  398. extclient.Network = node.Network
  399. host, err := logic.GetHost(node.HostID.String())
  400. if err != nil {
  401. logger.Log(0, r.Header.Get("user"),
  402. fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", nodeid, err))
  403. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  404. return
  405. }
  406. listenPort := logic.GetPeerListenPort(host)
  407. extclient.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
  408. extclient.Enabled = true
  409. parentNetwork, err := logic.GetNetwork(node.Network)
  410. if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
  411. extclient.Enabled = parentNetwork.DefaultACL == "yes"
  412. }
  413. if err = logic.CreateExtClient(&extclient); err != nil {
  414. slog.Error("failed to create extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
  415. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  416. return
  417. }
  418. slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
  419. w.WriteHeader(http.StatusOK)
  420. go func() {
  421. if err := logic.SetClientDefaultACLs(&extclient); err != nil {
  422. slog.Error("failed to set default acls for extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
  423. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  424. return
  425. }
  426. if err := mq.PublishPeerUpdate(false); err != nil {
  427. logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
  428. }
  429. if servercfg.IsDNSMode() {
  430. logic.SetDNS()
  431. }
  432. }()
  433. }
  434. // swagger:route PUT /api/extclients/{network}/{clientid} ext_client updateExtClient
  435. //
  436. // Update an individual extclient.
  437. //
  438. // Schemes: https
  439. //
  440. // Security:
  441. // oauth
  442. //
  443. // Responses:
  444. // 200: extClientResponse
  445. func updateExtClient(w http.ResponseWriter, r *http.Request) {
  446. w.Header().Set("Content-Type", "application/json")
  447. var params = mux.Vars(r)
  448. var update models.CustomExtClient
  449. //var oldExtClient models.ExtClient
  450. var sendPeerUpdate bool
  451. err := json.NewDecoder(r.Body).Decode(&update)
  452. if err != nil {
  453. logger.Log(0, r.Header.Get("user"), "error decoding request body: ",
  454. err.Error())
  455. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  456. return
  457. }
  458. clientid := params["clientid"]
  459. network := params["network"]
  460. oldExtClient, err := logic.GetExtClientByName(clientid)
  461. if err != nil {
  462. slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
  463. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  464. return
  465. }
  466. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), oldExtClient) {
  467. // check if user has access to extclient
  468. slog.Error("failed to get extclient", "network", network, "clientID",
  469. clientid, "error", errors.New("access is denied"))
  470. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  471. return
  472. }
  473. if oldExtClient.ClientID == update.ClientID {
  474. if err := validateCustomExtClient(&update, false); err != nil {
  475. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  476. return
  477. }
  478. } else {
  479. if err := validateCustomExtClient(&update, true); err != nil {
  480. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  481. return
  482. }
  483. }
  484. var changedID = update.ClientID != oldExtClient.ClientID
  485. if !reflect.DeepEqual(update.DeniedACLs, oldExtClient.DeniedACLs) {
  486. sendPeerUpdate = true
  487. logic.SetClientACLs(&oldExtClient, update.DeniedACLs)
  488. }
  489. if !logic.IsSlicesEqual(update.ExtraAllowedIPs, oldExtClient.ExtraAllowedIPs) {
  490. sendPeerUpdate = true
  491. }
  492. if update.Enabled != oldExtClient.Enabled {
  493. sendPeerUpdate = true
  494. }
  495. newclient := logic.UpdateExtClient(&oldExtClient, &update)
  496. if err := logic.DeleteExtClient(oldExtClient.Network, oldExtClient.ClientID); err != nil {
  497. slog.Error("failed to delete ext client", "user", r.Header.Get("user"), "id", oldExtClient.ClientID, "network", oldExtClient.Network, "error", err)
  498. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  499. return
  500. }
  501. if err := logic.SaveExtClient(&newclient); err != nil {
  502. slog.Error("failed to save ext client", "user", r.Header.Get("user"), "id", newclient.ClientID, "network", newclient.Network, "error", err)
  503. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  504. return
  505. }
  506. logger.Log(0, r.Header.Get("user"), "updated ext client", update.ClientID)
  507. w.WriteHeader(http.StatusOK)
  508. json.NewEncoder(w).Encode(newclient)
  509. go func() {
  510. if changedID && servercfg.IsDNSMode() {
  511. logic.SetDNS()
  512. }
  513. if sendPeerUpdate { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
  514. ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID)
  515. if err == nil {
  516. if err = mq.PublishPeerUpdate(false); err != nil {
  517. logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
  518. }
  519. }
  520. if !update.Enabled {
  521. ingressHost, err := logic.GetHost(ingressNode.HostID.String())
  522. if err != nil {
  523. slog.Error("Failed to get ingress host", "node", ingressNode.ID.String(), "error", err)
  524. return
  525. }
  526. nodes, err := logic.GetAllNodes()
  527. if err != nil {
  528. slog.Error("Failed to get nodes", "error", err)
  529. return
  530. }
  531. go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{oldExtClient}, false)
  532. }
  533. }
  534. }()
  535. }
  536. // swagger:route DELETE /api/extclients/{network}/{clientid} ext_client deleteExtClient
  537. //
  538. // Delete an individual extclient.
  539. //
  540. // Schemes: https
  541. //
  542. // Security:
  543. // oauth
  544. //
  545. // Responses:
  546. // 200: successResponse
  547. func deleteExtClient(w http.ResponseWriter, r *http.Request) {
  548. // Set header
  549. w.Header().Set("Content-Type", "application/json")
  550. // get params
  551. var params = mux.Vars(r)
  552. clientid := params["clientid"]
  553. network := params["network"]
  554. extclient, err := logic.GetExtClient(clientid, network)
  555. if err != nil {
  556. err = errors.New("Could not delete extclient " + params["clientid"])
  557. logger.Log(0, r.Header.Get("user"),
  558. fmt.Sprintf("failed to get extclient [%s],network [%s]: %v", clientid, network, err))
  559. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  560. return
  561. }
  562. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), extclient) {
  563. slog.Error("user not allowed to delete", "network", network, "clientID",
  564. clientid, "error", errors.New("access is denied"))
  565. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  566. return
  567. }
  568. ingressnode, err := logic.GetNodeByID(extclient.IngressGatewayID)
  569. if err != nil {
  570. logger.Log(0, r.Header.Get("user"),
  571. fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", extclient.IngressGatewayID, err))
  572. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  573. return
  574. }
  575. err = logic.DeleteExtClientAndCleanup(extclient)
  576. if err != nil {
  577. slog.Error("deleteExtClient: ", "Error", err.Error())
  578. err = errors.New("Could not delete extclient " + params["clientid"])
  579. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  580. return
  581. }
  582. go func() {
  583. if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
  584. slog.Error("error setting ext peers on " + ingressnode.ID.String() + ": " + err.Error())
  585. }
  586. if servercfg.IsDNSMode() {
  587. logic.SetDNS()
  588. }
  589. }()
  590. logger.Log(0, r.Header.Get("user"),
  591. "Deleted extclient client", params["clientid"], "from network", params["network"])
  592. logic.ReturnSuccessResponse(w, r, params["clientid"]+" deleted.")
  593. }
  594. // validateCustomExtClient Validates the extclient object
  595. func validateCustomExtClient(customExtClient *models.CustomExtClient, checkID bool) error {
  596. v := validator.New()
  597. err := v.Struct(customExtClient)
  598. if err != nil {
  599. return err
  600. }
  601. //validate clientid
  602. if customExtClient.ClientID != "" {
  603. if err := isValid(customExtClient.ClientID, checkID); err != nil {
  604. return fmt.Errorf("client validatation: %v", err)
  605. }
  606. }
  607. //extclient.ClientID = customExtClient.ClientID
  608. if len(customExtClient.PublicKey) > 0 {
  609. if _, err := wgtypes.ParseKey(customExtClient.PublicKey); err != nil {
  610. return errInvalidExtClientPubKey
  611. }
  612. //extclient.PublicKey = customExtClient.PublicKey
  613. }
  614. //validate extra ips
  615. if len(customExtClient.ExtraAllowedIPs) > 0 {
  616. for _, ip := range customExtClient.ExtraAllowedIPs {
  617. if _, _, err := net.ParseCIDR(ip); err != nil {
  618. return errInvalidExtClientExtraIP
  619. }
  620. }
  621. //extclient.ExtraAllowedIPs = customExtClient.ExtraAllowedIPs
  622. }
  623. //validate DNS
  624. if customExtClient.DNS != "" {
  625. if ip := net.ParseIP(customExtClient.DNS); ip == nil {
  626. return errInvalidExtClientDNS
  627. }
  628. //extclient.DNS = customExtClient.DNS
  629. }
  630. return nil
  631. }
  632. // isValid Checks if the clientid is valid
  633. func isValid(clientid string, checkID bool) error {
  634. if !validName(clientid) {
  635. return errInvalidExtClientID
  636. }
  637. if checkID {
  638. extclients, err := logic.GetAllExtClients()
  639. if err != nil {
  640. return fmt.Errorf("extclients isValid: %v", err)
  641. }
  642. for _, extclient := range extclients {
  643. if clientid == extclient.ClientID {
  644. return errDuplicateExtClientName
  645. }
  646. }
  647. }
  648. return nil
  649. }