ext_client.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  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. var gateway models.EgressGatewayRequest
  350. gateway.NetID = params["network"]
  351. gateway.Ranges = customExtClient.ExtraAllowedIPs
  352. err := logic.ValidateEgressRange(gateway)
  353. if err != nil {
  354. logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error())
  355. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  356. return
  357. }
  358. node, err := logic.GetNodeByID(nodeid)
  359. if err != nil {
  360. logger.Log(0, r.Header.Get("user"),
  361. fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", nodeid, err))
  362. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  363. return
  364. }
  365. var userName string
  366. if r.Header.Get("ismaster") == "yes" {
  367. userName = logic.MasterUser
  368. } else {
  369. caller, err := logic.GetUser(r.Header.Get("user"))
  370. if err != nil {
  371. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  372. return
  373. }
  374. userName = caller.UserName
  375. if _, ok := caller.RemoteGwIDs[nodeid]; (!caller.IsAdmin && !caller.IsSuperAdmin) && !ok {
  376. err = errors.New("permission denied")
  377. slog.Error("failed to create extclient", "error", err)
  378. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
  379. return
  380. }
  381. // check if user has a config already for remote access client
  382. extclients, err := logic.GetNetworkExtClients(node.Network)
  383. if err != nil {
  384. slog.Error("failed to get extclients", "error", err)
  385. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  386. return
  387. }
  388. for _, extclient := range extclients {
  389. if extclient.RemoteAccessClientID != "" &&
  390. extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && extclient.OwnerID == caller.UserName && nodeid == extclient.IngressGatewayID {
  391. // extclient on the gw already exists for the remote access client
  392. err = errors.New("remote client config already exists on the gateway")
  393. slog.Error("failed to create extclient", "user", userName, "error", err)
  394. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  395. return
  396. }
  397. }
  398. }
  399. extclient := logic.UpdateExtClient(&models.ExtClient{}, &customExtClient)
  400. extclient.OwnerID = userName
  401. extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
  402. extclient.IngressGatewayID = nodeid
  403. // set extclient dns to ingressdns if extclient dns is not explicitly set
  404. if (extclient.DNS == "") && (node.IngressDNS != "") {
  405. extclient.DNS = node.IngressDNS
  406. }
  407. extclient.Network = node.Network
  408. host, err := logic.GetHost(node.HostID.String())
  409. if err != nil {
  410. logger.Log(0, r.Header.Get("user"),
  411. fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", nodeid, err))
  412. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  413. return
  414. }
  415. listenPort := logic.GetPeerListenPort(host)
  416. extclient.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
  417. extclient.Enabled = true
  418. parentNetwork, err := logic.GetNetwork(node.Network)
  419. if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
  420. extclient.Enabled = parentNetwork.DefaultACL == "yes"
  421. }
  422. if err = logic.CreateExtClient(&extclient); err != nil {
  423. slog.Error("failed to create extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
  424. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  425. return
  426. }
  427. slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
  428. w.WriteHeader(http.StatusOK)
  429. go func() {
  430. if err := logic.SetClientDefaultACLs(&extclient); err != nil {
  431. slog.Error("failed to set default acls for extclient", "user", r.Header.Get("user"), "network", node.Network, "error", err)
  432. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  433. return
  434. }
  435. if err := mq.PublishPeerUpdate(false); err != nil {
  436. logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
  437. }
  438. if servercfg.IsDNSMode() {
  439. logic.SetDNS()
  440. }
  441. }()
  442. }
  443. // swagger:route PUT /api/extclients/{network}/{clientid} ext_client updateExtClient
  444. //
  445. // Update an individual extclient.
  446. //
  447. // Schemes: https
  448. //
  449. // Security:
  450. // oauth
  451. //
  452. // Responses:
  453. // 200: extClientResponse
  454. func updateExtClient(w http.ResponseWriter, r *http.Request) {
  455. w.Header().Set("Content-Type", "application/json")
  456. var params = mux.Vars(r)
  457. var update models.CustomExtClient
  458. //var oldExtClient models.ExtClient
  459. var sendPeerUpdate bool
  460. err := json.NewDecoder(r.Body).Decode(&update)
  461. if err != nil {
  462. logger.Log(0, r.Header.Get("user"), "error decoding request body: ",
  463. err.Error())
  464. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  465. return
  466. }
  467. clientid := params["clientid"]
  468. network := params["network"]
  469. oldExtClient, err := logic.GetExtClientByName(clientid)
  470. if err != nil {
  471. slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
  472. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  473. return
  474. }
  475. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), oldExtClient) {
  476. // check if user has access to extclient
  477. slog.Error("failed to get extclient", "network", network, "clientID",
  478. clientid, "error", errors.New("access is denied"))
  479. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  480. return
  481. }
  482. if oldExtClient.ClientID == update.ClientID {
  483. if err := validateCustomExtClient(&update, false); err != nil {
  484. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  485. return
  486. }
  487. } else {
  488. if err := validateCustomExtClient(&update, true); err != nil {
  489. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  490. return
  491. }
  492. }
  493. var gateway models.EgressGatewayRequest
  494. gateway.NetID = params["network"]
  495. gateway.Ranges = update.ExtraAllowedIPs
  496. err = logic.ValidateEgressRange(gateway)
  497. if err != nil {
  498. logger.Log(0, r.Header.Get("user"), "error validating egress range: ", err.Error())
  499. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
  500. return
  501. }
  502. var changedID = update.ClientID != oldExtClient.ClientID
  503. if !reflect.DeepEqual(update.DeniedACLs, oldExtClient.DeniedACLs) {
  504. sendPeerUpdate = true
  505. logic.SetClientACLs(&oldExtClient, update.DeniedACLs)
  506. }
  507. if !logic.IsSlicesEqual(update.ExtraAllowedIPs, oldExtClient.ExtraAllowedIPs) {
  508. sendPeerUpdate = true
  509. }
  510. if update.Enabled != oldExtClient.Enabled {
  511. sendPeerUpdate = true
  512. }
  513. newclient := logic.UpdateExtClient(&oldExtClient, &update)
  514. if err := logic.DeleteExtClient(oldExtClient.Network, oldExtClient.ClientID); err != nil {
  515. slog.Error("failed to delete ext client", "user", r.Header.Get("user"), "id", oldExtClient.ClientID, "network", oldExtClient.Network, "error", err)
  516. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  517. return
  518. }
  519. if err := logic.SaveExtClient(&newclient); err != nil {
  520. slog.Error("failed to save ext client", "user", r.Header.Get("user"), "id", newclient.ClientID, "network", newclient.Network, "error", err)
  521. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  522. return
  523. }
  524. logger.Log(0, r.Header.Get("user"), "updated ext client", update.ClientID)
  525. w.WriteHeader(http.StatusOK)
  526. json.NewEncoder(w).Encode(newclient)
  527. go func() {
  528. if changedID && servercfg.IsDNSMode() {
  529. logic.SetDNS()
  530. }
  531. if sendPeerUpdate { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
  532. ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID)
  533. if err == nil {
  534. if err = mq.PublishPeerUpdate(false); err != nil {
  535. logger.Log(1, "error setting ext peers on", ingressNode.ID.String(), ":", err.Error())
  536. }
  537. }
  538. if !update.Enabled {
  539. ingressHost, err := logic.GetHost(ingressNode.HostID.String())
  540. if err != nil {
  541. slog.Error("Failed to get ingress host", "node", ingressNode.ID.String(), "error", err)
  542. return
  543. }
  544. nodes, err := logic.GetAllNodes()
  545. if err != nil {
  546. slog.Error("Failed to get nodes", "error", err)
  547. return
  548. }
  549. go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{oldExtClient}, false, nil)
  550. }
  551. }
  552. }()
  553. }
  554. // swagger:route DELETE /api/extclients/{network}/{clientid} ext_client deleteExtClient
  555. //
  556. // Delete an individual extclient.
  557. //
  558. // Schemes: https
  559. //
  560. // Security:
  561. // oauth
  562. //
  563. // Responses:
  564. // 200: successResponse
  565. func deleteExtClient(w http.ResponseWriter, r *http.Request) {
  566. // Set header
  567. w.Header().Set("Content-Type", "application/json")
  568. // get params
  569. var params = mux.Vars(r)
  570. clientid := params["clientid"]
  571. network := params["network"]
  572. extclient, err := logic.GetExtClient(clientid, network)
  573. if err != nil {
  574. err = errors.New("Could not delete extclient " + params["clientid"])
  575. logger.Log(0, r.Header.Get("user"),
  576. fmt.Sprintf("failed to get extclient [%s],network [%s]: %v", clientid, network, err))
  577. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  578. return
  579. }
  580. if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), extclient) {
  581. slog.Error("user not allowed to delete", "network", network, "clientID",
  582. clientid, "error", errors.New("access is denied"))
  583. logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
  584. return
  585. }
  586. ingressnode, err := logic.GetNodeByID(extclient.IngressGatewayID)
  587. if err != nil {
  588. logger.Log(0, r.Header.Get("user"),
  589. fmt.Sprintf("failed to get ingress gateway node [%s] info: %v", extclient.IngressGatewayID, err))
  590. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  591. return
  592. }
  593. err = logic.DeleteExtClientAndCleanup(extclient)
  594. if err != nil {
  595. slog.Error("deleteExtClient: ", "Error", err.Error())
  596. err = errors.New("Could not delete extclient " + params["clientid"])
  597. logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
  598. return
  599. }
  600. go func() {
  601. if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
  602. slog.Error("error setting ext peers on " + ingressnode.ID.String() + ": " + err.Error())
  603. }
  604. if servercfg.IsDNSMode() {
  605. logic.SetDNS()
  606. }
  607. }()
  608. logger.Log(0, r.Header.Get("user"),
  609. "Deleted extclient client", params["clientid"], "from network", params["network"])
  610. logic.ReturnSuccessResponse(w, r, params["clientid"]+" deleted.")
  611. }
  612. // validateCustomExtClient Validates the extclient object
  613. func validateCustomExtClient(customExtClient *models.CustomExtClient, checkID bool) error {
  614. v := validator.New()
  615. err := v.Struct(customExtClient)
  616. if err != nil {
  617. return err
  618. }
  619. //validate clientid
  620. if customExtClient.ClientID != "" {
  621. if err := isValid(customExtClient.ClientID, checkID); err != nil {
  622. return fmt.Errorf("client validatation: %v", err)
  623. }
  624. }
  625. //extclient.ClientID = customExtClient.ClientID
  626. if len(customExtClient.PublicKey) > 0 {
  627. if _, err := wgtypes.ParseKey(customExtClient.PublicKey); err != nil {
  628. return errInvalidExtClientPubKey
  629. }
  630. //extclient.PublicKey = customExtClient.PublicKey
  631. }
  632. //validate extra ips
  633. if len(customExtClient.ExtraAllowedIPs) > 0 {
  634. for _, ip := range customExtClient.ExtraAllowedIPs {
  635. if _, _, err := net.ParseCIDR(ip); err != nil {
  636. return errInvalidExtClientExtraIP
  637. }
  638. }
  639. //extclient.ExtraAllowedIPs = customExtClient.ExtraAllowedIPs
  640. }
  641. //validate DNS
  642. if customExtClient.DNS != "" {
  643. if ip := net.ParseIP(customExtClient.DNS); ip == nil {
  644. return errInvalidExtClientDNS
  645. }
  646. //extclient.DNS = customExtClient.DNS
  647. }
  648. return nil
  649. }
  650. // isValid Checks if the clientid is valid
  651. func isValid(clientid string, checkID bool) error {
  652. if !validName(clientid) {
  653. return errInvalidExtClientID
  654. }
  655. if checkID {
  656. extclients, err := logic.GetAllExtClients()
  657. if err != nil {
  658. return fmt.Errorf("extclients isValid: %v", err)
  659. }
  660. for _, extclient := range extclients {
  661. if clientid == extclient.ClientID {
  662. return errDuplicateExtClientName
  663. }
  664. }
  665. }
  666. return nil
  667. }