mirror of
https://github.com/gravitl/netmaker.git
synced 2025-09-06 13:14:24 +08:00
Merge pull request #3353 from gravitl/NET-1978
NET-1978: Add support for additional nameservers, add loadbalance connection api for client config
This commit is contained in:
commit
7764682886
7 changed files with 287 additions and 18 deletions
|
@ -17,6 +17,17 @@ var extClientConfigCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var extClientHAConfigCmd = &cobra.Command{
|
||||
Use: "auto_config [NETWORK NAME]",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Short: "Get an External Client Configuration",
|
||||
Long: `Get an External Client Configuration`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Println(functions.GetExtClientHAConfig(args[0]))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(extClientConfigCmd)
|
||||
rootCmd.AddCommand(extClientHAConfigCmd)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ func GetExtClientConfig(networkName, clientID string) string {
|
|||
return get(fmt.Sprintf("/api/extclients/%s/%s/file", networkName, clientID))
|
||||
}
|
||||
|
||||
// GetExtClientConfig - auto fetch a client config
|
||||
func GetExtClientHAConfig(networkName string) string {
|
||||
return get(fmt.Sprintf("/api/v1/client_conf/%s", networkName))
|
||||
}
|
||||
|
||||
// CreateExtClient - create an external client
|
||||
func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) {
|
||||
request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -640,6 +640,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
netNew := netOld
|
||||
netNew.NameServers = payload.NameServers
|
||||
netNew.DefaultACL = payload.DefaultACL
|
||||
_, _, _, err = logic.UpdateNetwork(&netOld, &netNew)
|
||||
if err != nil {
|
||||
|
@ -647,7 +648,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
|
|||
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
||||
return
|
||||
}
|
||||
|
||||
go mq.PublishPeerUpdate(false)
|
||||
slog.Info("updated network", "network", payload.NetID, "user", r.Header.Get("user"))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(payload)
|
||||
|
|
|
@ -270,7 +270,11 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
networkSettings, err := GetNetwork(node.Network)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
hostPeerUpdate.NameServers = append(hostPeerUpdate.NameServers, networkSettings.NameServers...)
|
||||
currentPeers := GetNetworkNodesMemory(allNodes, node.Network)
|
||||
for _, peer := range currentPeers {
|
||||
peer := peer
|
||||
|
|
|
@ -27,6 +27,7 @@ type HostPeerUpdate struct {
|
|||
EgressRoutes []EgressNetworkRoutes `json:"egress_network_routes"`
|
||||
FwUpdate FwUpdate `json:"fw_update"`
|
||||
ReplacePeers bool `json:"replace_peers"`
|
||||
NameServers []string `json:"name_servers"`
|
||||
ServerConfig
|
||||
OldPeerUpdateFields
|
||||
}
|
||||
|
|
|
@ -8,22 +8,23 @@ import (
|
|||
// Network Struct - contains info for a given unique network
|
||||
// At some point, need to replace all instances of Name with something else like Identifier
|
||||
type Network struct {
|
||||
AddressRange string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"`
|
||||
AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"`
|
||||
NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"`
|
||||
NodesLastModified int64 `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"`
|
||||
NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"`
|
||||
DefaultInterface string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"`
|
||||
DefaultListenPort int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
|
||||
NodeLimit int32 `json:"nodelimit" bson:"nodelimit"`
|
||||
DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"`
|
||||
DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
|
||||
AllowManualSignUp string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
|
||||
IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
|
||||
IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
|
||||
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
|
||||
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
|
||||
DefaultACL string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
|
||||
AddressRange string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"`
|
||||
AddressRange6 string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"`
|
||||
NetID string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"`
|
||||
NodesLastModified int64 `json:"nodeslastmodified" bson:"nodeslastmodified" swaggertype:"primitive,integer" format:"int64"`
|
||||
NetworkLastModified int64 `json:"networklastmodified" bson:"networklastmodified" swaggertype:"primitive,integer" format:"int64"`
|
||||
DefaultInterface string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"`
|
||||
DefaultListenPort int32 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
|
||||
NodeLimit int32 `json:"nodelimit" bson:"nodelimit"`
|
||||
DefaultPostDown string `json:"defaultpostdown" bson:"defaultpostdown"`
|
||||
DefaultKeepalive int32 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
|
||||
AllowManualSignUp string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
|
||||
IsIPv4 string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
|
||||
IsIPv6 string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
|
||||
DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
|
||||
DefaultMTU int32 `json:"defaultmtu" bson:"defaultmtu"`
|
||||
DefaultACL string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
|
||||
NameServers []string `json:"dns_nameservers"`
|
||||
}
|
||||
|
||||
// SaveData - sensitive fields of a network that should be kept the same
|
||||
|
|
Loading…
Add table
Reference in a new issue