ソースを参照

add loadbalance endpoint for extclients

abhishek9686 6 ヶ月 前
コミット
48b805bbc2
1 ファイル変更246 行追加0 行削除
  1. 246 0
      controllers/ext_client.go

+ 246 - 0
controllers/ext_client.go

@@ -42,6 +42,7 @@ func extClientHandlers(r *mux.Router) {
 		Methods(http.MethodDelete)
 	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).
 		Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/client_conf/{network}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientHAConf))).Methods(http.MethodGet)
 }
 
 func checkIngressExists(nodeID string) bool {
@@ -387,6 +388,251 @@ Endpoint = %s
 	json.NewEncoder(w).Encode(client)
 }
 
+// @Summary     Get an individual remote access client
+// @Router      /api/extclients/{network}/{clientid}/{type} [get]
+// @Tags        Remote Access Client
+// @Security    oauth2
+// @Success     200 {object} models.ExtClient
+// @Failure     500 {object} models.ErrorResponse
+// @Failure     403 {object} models.ErrorResponse
+func getExtClientHAConf(w http.ResponseWriter, r *http.Request) {
+
+	var params = mux.Vars(r)
+	networkid := params["network"]
+	network, err := logic.GetParentNetwork(networkid)
+	if err != nil {
+		logger.Log(
+			1,
+			r.Header.Get("user"),
+			"Could not retrieve Ingress Gateway Network",
+			networkid,
+		)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	// fetch client based on availability
+	nodes, _ := logic.GetNetworkNodes(networkid)
+	defaultPolicy, _ := logic.GetDefaultPolicy(models.NetworkID(networkid), models.DevicePolicy)
+	var targetGwID string
+	var connectionCnt int = -1
+	for _, nodeI := range nodes {
+		if nodeI.IsGw {
+			// check health status
+			logic.GetNodeStatus(&nodeI, defaultPolicy.Enabled)
+			if nodeI.Status != models.OnlineSt {
+				continue
+			}
+			// Get Total connections on the gw
+			clients := logic.GetGwExtclients(nodeI.ID.String(), networkid)
+
+			if connectionCnt == -1 || len(clients) < connectionCnt {
+				connectionCnt = len(clients)
+				targetGwID = nodeI.ID.String()
+			}
+
+		}
+	}
+	gwnode, err := logic.GetNodeByID(targetGwID)
+	if err != nil {
+		logger.Log(
+			0,
+			r.Header.Get("user"),
+			fmt.Sprintf(
+				"failed to get ingress gateway node [%s] info: %v",
+				gwnode.ID,
+				err,
+			),
+		)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	host, err := logic.GetHost(gwnode.HostID.String())
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"),
+			fmt.Sprintf("failed to get ingress gateway host for node [%s] info: %v", gwnode.ID, err))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	var userName string
+	if r.Header.Get("ismaster") == "yes" {
+		userName = logic.MasterUser
+	} else {
+		caller, err := logic.GetUser(r.Header.Get("user"))
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+		userName = caller.UserName
+	}
+	// create client
+	var extclient models.ExtClient
+	extclient.OwnerID = userName
+	extclient.IngressGatewayID = targetGwID
+	extclient.Network = networkid
+	extclient.Tags = make(map[models.TagID]struct{})
+	// extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network,
+	// 	models.RemoteAccessTagName))] = struct{}{}
+	// set extclient dns to ingressdns if extclient dns is not explicitly set
+	if (extclient.DNS == "") && (gwnode.IngressDNS != "") {
+		extclient.DNS = gwnode.IngressDNS
+	}
+
+	listenPort := logic.GetPeerListenPort(host)
+	extclient.IngressGatewayEndpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), listenPort)
+	extclient.Enabled = true
+
+	if err = logic.CreateExtClient(&extclient); err != nil {
+		slog.Error(
+			"failed to create extclient",
+			"user",
+			r.Header.Get("user"),
+			"network",
+			networkid,
+			"error",
+			err,
+		)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	client, err := logic.GetExtClient(extclient.ClientID, networkid)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	addrString := client.Address
+	if addrString != "" {
+		addrString += "/32"
+	}
+	if client.Address6 != "" {
+		if addrString != "" {
+			addrString += ","
+		}
+		addrString += client.Address6 + "/128"
+	}
+
+	keepalive := ""
+	if network.DefaultKeepalive != 0 {
+		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(network.DefaultKeepalive))
+	}
+	if gwnode.IngressPersistentKeepalive != 0 {
+		keepalive = "PersistentKeepalive = " + strconv.Itoa(int(gwnode.IngressPersistentKeepalive))
+	}
+	var newAllowedIPs string
+	if logic.IsInternetGw(gwnode) || gwnode.InternetGwID != "" {
+		egressrange := "0.0.0.0/0"
+		if gwnode.Address6.IP != nil && client.Address6 != "" {
+			egressrange += "," + "::/0"
+		}
+		newAllowedIPs = egressrange
+	} else {
+		newAllowedIPs = network.AddressRange
+		if newAllowedIPs != "" && network.AddressRange6 != "" {
+			newAllowedIPs += ","
+		}
+		if network.AddressRange6 != "" {
+			newAllowedIPs += network.AddressRange6
+		}
+		if egressGatewayRanges, err := logic.GetEgressRangesOnNetwork(&client); err == nil {
+			for _, egressGatewayRange := range egressGatewayRanges {
+				newAllowedIPs += "," + egressGatewayRange
+			}
+		}
+	}
+	gwendpoint := ""
+	if host.EndpointIP.To4() == nil {
+		gwendpoint = fmt.Sprintf("[%s]:%d", host.EndpointIPv6.String(), host.ListenPort)
+	} else {
+		gwendpoint = fmt.Sprintf("%s:%d", host.EndpointIP.String(), host.ListenPort)
+	}
+	defaultDNS := ""
+	if client.DNS != "" {
+		defaultDNS = "DNS = " + client.DNS
+	} else if gwnode.IngressDNS != "" {
+		defaultDNS = "DNS = " + gwnode.IngressDNS
+	}
+
+	defaultMTU := 1420
+	if host.MTU != 0 {
+		defaultMTU = host.MTU
+	}
+	if gwnode.IngressMTU != 0 {
+		defaultMTU = int(gwnode.IngressMTU)
+	}
+
+	postUp := strings.Builder{}
+	if client.PostUp != "" && params["type"] != "qr" {
+		for _, loc := range strings.Split(client.PostUp, "\n") {
+			postUp.WriteString(fmt.Sprintf("PostUp = %s\n", loc))
+		}
+	}
+
+	postDown := strings.Builder{}
+	if client.PostDown != "" && params["type"] != "qr" {
+		for _, loc := range strings.Split(client.PostDown, "\n") {
+			postDown.WriteString(fmt.Sprintf("PostDown = %s\n", loc))
+		}
+	}
+
+	config := fmt.Sprintf(`[Interface]
+Address = %s
+PrivateKey = %s
+MTU = %d
+%s
+%s
+%s
+
+[Peer]
+PublicKey = %s
+AllowedIPs = %s
+Endpoint = %s
+%s
+
+`, addrString,
+		client.PrivateKey,
+		defaultMTU,
+		defaultDNS,
+		postUp.String(),
+		postDown.String(),
+		host.PublicKey,
+		newAllowedIPs,
+		gwendpoint,
+		keepalive,
+	)
+
+	go func() {
+		if err := logic.SetClientDefaultACLs(&extclient); err != nil {
+			slog.Error(
+				"failed to set default acls for extclient",
+				"user",
+				r.Header.Get("user"),
+				"network",
+				networkid,
+				"error",
+				err,
+			)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+		if err := mq.PublishPeerUpdate(false); err != nil {
+			logger.Log(1, "error publishing peer update ", err.Error())
+		}
+		if servercfg.IsDNSMode() {
+			logic.SetDNS()
+		}
+	}()
+
+	name := client.ClientID + ".conf"
+	w.Header().Set("Content-Type", "application/config")
+	w.Header().Set("Content-Disposition", "attachment; filename=\""+name+"\"")
+	w.WriteHeader(http.StatusOK)
+	_, err = fmt.Fprint(w, config)
+	if err != nil {
+		logger.Log(1, r.Header.Get("user"), "response writer error (file) ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	}
+}
+
 // @Summary     Create an individual remote access client
 // @Router      /api/extclients/{network}/{nodeid} [post]
 // @Tags        Remote Access Client