ext_client.go 23 KB

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