ext_client.go 24 KB

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