Browse Source

Extclient NET-63x (#2286)

* model changes

* additional fields for extclient create

* add DNS to extclient config

* extclient name checks

* update extclient

* nmctl extclient

* final tweaks

* review comments

* add extclientdns to node on ingress creation

* fix to add ingress dns to api (#2296)

---------

Co-authored-by: Aceix <[email protected]>
Matthew R Kasun 2 years ago
parent
commit
78640f1342

+ 18 - 2
cli/cmd/ext_client/create.go

@@ -4,10 +4,16 @@ import (
 	"fmt"
 
 	"github.com/gravitl/netmaker/cli/functions"
+	"github.com/gravitl/netmaker/models"
 	"github.com/spf13/cobra"
 )
 
-var extClientID string
+var (
+	extClientID string
+	publicKey   string
+	dns         string
+	allowedips  []string
+)
 
 var extClientCreateCmd = &cobra.Command{
 	Use:   "create [NETWORK NAME] [NODE ID]",
@@ -15,12 +21,22 @@ var extClientCreateCmd = &cobra.Command{
 	Short: "Create an External Client",
 	Long:  `Create an External Client`,
 	Run: func(cmd *cobra.Command, args []string) {
-		functions.CreateExtClient(args[0], args[1], extClientID)
+		extClient := models.CustomExtClient{
+			ClientID:        extClientID,
+			PublicKey:       publicKey,
+			DNS:             dns,
+			ExtraAllowedIPs: allowedips,
+		}
+
+		functions.CreateExtClient(args[0], args[1], extClient)
 		fmt.Println("Success")
 	},
 }
 
 func init() {
 	extClientCreateCmd.Flags().StringVar(&extClientID, "id", "", "ID of the external client")
+	extClientCreateCmd.Flags().StringVar(&publicKey, "public_key", "", "updated public key of the external client")
+	extClientCreateCmd.Flags().StringVar(&dns, "dns", "", "updated DNS of the external client")
+	extClientCreateCmd.Flags().StringSliceVar(&allowedips, "allowedips", []string{}, "updated extra allowed IPs of the external client")
 	rootCmd.AddCommand(extClientCreateCmd)
 }

+ 8 - 27
cli/cmd/ext_client/update.go

@@ -11,15 +11,7 @@ import (
 )
 
 var (
-	extClientUpdateFile    string
-	description            string
-	privateKey             string
-	publicKey              string
-	address                string
-	address6               string
-	ingressGatewayID       string
-	ingressGatewayEndpoint string
-	ownerID                string
+	extClientUpdateFile string
 )
 
 var extClientUpdateCmd = &cobra.Command{
@@ -31,7 +23,7 @@ var extClientUpdateCmd = &cobra.Command{
 		var (
 			network   = args[0]
 			clientID  = args[1]
-			extClient = &models.ExtClient{}
+			extClient = &models.CustomExtClient{}
 		)
 		if extClientUpdateFile != "" {
 			content, err := os.ReadFile(extClientUpdateFile)
@@ -42,30 +34,19 @@ var extClientUpdateCmd = &cobra.Command{
 				log.Fatal(err)
 			}
 		} else {
-			extClient.ClientID = clientID
-			extClient.Description = description
-			extClient.PrivateKey = privateKey
+			extClient.ClientID = extClientID
 			extClient.PublicKey = publicKey
-			extClient.Network = network
-			extClient.Address = address
-			extClient.Address6 = address6
-			extClient.IngressGatewayID = ingressGatewayID
-			extClient.IngressGatewayEndpoint = ingressGatewayEndpoint
-			extClient.OwnerID = ownerID
+			extClient.DNS = dns
 		}
 		functions.PrettyPrint(functions.UpdateExtClient(network, clientID, extClient))
 	},
 }
 
 func init() {
+	extClientUpdateCmd.Flags().StringVar(&extClientID, "id", "", "updated ID of the external client")
 	extClientUpdateCmd.Flags().StringVar(&extClientUpdateFile, "file", "", "Filepath of updated external client definition in JSON")
-	extClientUpdateCmd.Flags().StringVar(&description, "desc", "", "Description of the external client")
-	extClientUpdateCmd.Flags().StringVar(&privateKey, "private_key", "", "Filepath of updated external client definition in JSON")
-	extClientUpdateCmd.Flags().StringVar(&publicKey, "public_key", "", "Filepath of updated external client definition in JSON")
-	extClientUpdateCmd.Flags().StringVar(&address, "ipv4_addr", "", "IPv4 address of the external client")
-	extClientUpdateCmd.Flags().StringVar(&address6, "ipv6_addr", "", "IPv6 address of the external client")
-	extClientUpdateCmd.Flags().StringVar(&ingressGatewayID, "ingress_gateway_id", "", "ID of the ingress gateway")
-	extClientUpdateCmd.Flags().StringVar(&ingressGatewayEndpoint, "ingress_gateway_endpoint", "", "Endpoint of the ingress gateway")
-	extClientUpdateCmd.Flags().StringVar(&ownerID, "owner_id", "", "External Client owner's ID")
+	extClientUpdateCmd.Flags().StringVar(&publicKey, "public_key", "", "updated public key of the external client")
+	extClientUpdateCmd.Flags().StringVar(&dns, "dns", "", "updated DNS of the external client")
+	extClientUpdateCmd.Flags().StringSliceVar(&allowedips, "allowedips", []string{}, "updated extra allowed IPs of the external client")
 	rootCmd.AddCommand(extClientUpdateCmd)
 }

+ 0 - 2
cli/cmd/network/create.go

@@ -45,7 +45,6 @@ var networkCreateCmd = &cobra.Command{
 			if allowManualSignUp {
 				network.AllowManualSignUp = "yes"
 			}
-			network.DefaultExtClientDNS = defaultExtClientDNS
 			network.DefaultMTU = int32(defaultMTU)
 		}
 		functions.PrettyPrint(functions.CreateNetwork(network))
@@ -61,7 +60,6 @@ func init() {
 	networkCreateCmd.Flags().BoolVar(&udpHolePunch, "udp_hole_punch", false, "Enable UDP Hole Punching ?")
 	networkCreateCmd.Flags().BoolVar(&defaultACL, "default_acl", false, "Enable default Access Control List ?")
 	networkCreateCmd.Flags().StringVar(&defaultInterface, "interface", "", "Name of the network interface")
-	networkCreateCmd.Flags().StringVar(&defaultExtClientDNS, "ext_client_dns", "", "IPv4 address of DNS server to be used by external clients")
 	networkCreateCmd.Flags().IntVar(&defaultListenPort, "listen_port", 51821, "Default wireguard port each node will attempt to use")
 	networkCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be associated with this network")
 	networkCreateCmd.Flags().IntVar(&defaultKeepalive, "keep_alive", 20, "Keep Alive in seconds")

+ 0 - 1
cli/cmd/network/flags.go

@@ -12,6 +12,5 @@ var (
 	nodeLimit                 int
 	defaultKeepalive          int
 	allowManualSignUp         bool
-	defaultExtClientDNS       string
 	defaultMTU                int
 )

+ 3 - 9
cli/functions/ext_client.go

@@ -28,14 +28,8 @@ func GetExtClientConfig(networkName, clientID string) string {
 }
 
 // CreateExtClient - create an external client
-func CreateExtClient(networkName, nodeID, extClientID string) {
-	if extClientID != "" {
-		request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), &models.CustomExtClient{
-			ClientID: extClientID,
-		})
-	} else {
-		request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), nil)
-	}
+func CreateExtClient(networkName, nodeID string, extClient models.CustomExtClient) {
+	request[any](http.MethodPost, fmt.Sprintf("/api/extclients/%s/%s", networkName, nodeID), extClient)
 }
 
 // DeleteExtClient - delete an external client
@@ -44,6 +38,6 @@ func DeleteExtClient(networkName, clientID string) *models.SuccessResponse {
 }
 
 // UpdateExtClient - update an external client
-func UpdateExtClient(networkName, clientID string, payload *models.ExtClient) *models.ExtClient {
+func UpdateExtClient(networkName, clientID string, payload *models.CustomExtClient) *models.ExtClient {
 	return request[models.ExtClient](http.MethodPut, fmt.Sprintf("/api/extclients/%s/%s", networkName, clientID), payload)
 }

+ 56 - 29
controllers/ext_client.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"net"
 	"net/http"
 	"strconv"
 
@@ -230,8 +231,10 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 	defaultDNS := ""
-	if network.DefaultExtClientDNS != "" {
-		defaultDNS = "DNS = " + network.DefaultExtClientDNS
+	if client.DNS != "" {
+		defaultDNS = "DNS = " + client.DNS
+	} else if gwnode.IngressDNS != "" {
+		defaultDNS = "DNS = " + gwnode.IngressDNS
 	}
 
 	defaultMTU := 1420
@@ -321,20 +324,13 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	var extclient models.ExtClient
 	var customExtClient models.CustomExtClient
 
-	err := json.NewDecoder(r.Body).Decode(&customExtClient)
-	if err == nil {
-		if customExtClient.ClientID != "" && !validName(customExtClient.ClientID) {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(errInvalidExtClientID, "badrequest"))
-			return
-		}
-		extclient.ClientID = customExtClient.ClientID
-		if len(customExtClient.PublicKey) > 0 {
-			if _, err := wgtypes.ParseKey(customExtClient.PublicKey); err != nil {
-				logic.ReturnErrorResponse(w, r, logic.FormatError(errInvalidExtClientPubKey, "badrequest"))
-				return
-			}
-			extclient.PublicKey = customExtClient.PublicKey
-		}
+	if err := json.NewDecoder(r.Body).Decode(&customExtClient); err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	if err := validateExtClient(&extclient, &customExtClient); err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
 	}
 
 	extclient.Network = networkName
@@ -392,7 +388,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 				logger.Log(0, "failed to associate client", extclient.ClientID, "to user", userID)
 			}
 			extclient.OwnerID = userID
-			if _, err := logic.UpdateExtClient(extclient.ClientID, extclient.Network, extclient.Enabled, &extclient, extclient.ACLs); err != nil {
+			if err := logic.SaveExtClient(&extclient); err != nil {
 				logger.Log(0, "failed to add owner id", userID, "to client", extclient.ClientID)
 			}
 		}
@@ -426,9 +422,9 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 
 	var params = mux.Vars(r)
 
-	var newExtClient models.ExtClient
+	var update models.CustomExtClient
 	var oldExtClient models.ExtClient
-	err := json.NewDecoder(r.Body).Decode(&newExtClient)
+	err := json.NewDecoder(r.Body).Decode(&update)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ",
 			err.Error())
@@ -445,8 +441,8 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	if !validName(newExtClient.ClientID) {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errInvalidExtClientID, "badrequest"))
+	if err := validateExtClient(&oldExtClient, &update); err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
 	data, err := database.FetchRecord(database.EXT_CLIENT_TABLE_NAME, key)
@@ -466,7 +462,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 
 	// == PRO ==
 	networkName := params["network"]
-	var changedID = newExtClient.ClientID != oldExtClient.ClientID
+	var changedID = update.ClientID != oldExtClient.ClientID
 	if r.Header.Get("ismaster") != "yes" {
 		userID := r.Header.Get("user")
 		_, doesOwn := doesUserOwnClient(userID, params["clientid"], networkName)
@@ -479,17 +475,16 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		if err := pro.DissociateNetworkUserClient(oldExtClient.OwnerID, networkName, oldExtClient.ClientID); err != nil {
 			logger.Log(0, "failed to dissociate client", oldExtClient.ClientID, "from user", oldExtClient.OwnerID)
 		}
-		if err := pro.AssociateNetworkUserClient(oldExtClient.OwnerID, networkName, newExtClient.ClientID); err != nil {
-			logger.Log(0, "failed to associate client", newExtClient.ClientID, "to user", oldExtClient.OwnerID)
+		if err := pro.AssociateNetworkUserClient(oldExtClient.OwnerID, networkName, update.ClientID); err != nil {
+			logger.Log(0, "failed to associate client", update.ClientID, "to user", oldExtClient.OwnerID)
 		}
 	}
 	// == END PRO ==
 
-	var changedEnabled = (newExtClient.Enabled != oldExtClient.Enabled) || // indicates there was a change in enablement
-		len(newExtClient.ACLs) != len(oldExtClient.ACLs)
+	var changedEnabled = (update.Enabled != oldExtClient.Enabled) // indicates there was a change in enablement
 	// extra var need as logic.Update changes oldExtClient
 	currentClient := oldExtClient
-	newclient, err := logic.UpdateExtClient(newExtClient.ClientID, params["network"], newExtClient.Enabled, &oldExtClient, newExtClient.ACLs)
+	newclient, err := logic.UpdateExtClient(&oldExtClient, &update)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to update ext client [%s], network [%s]: %v",
@@ -497,7 +492,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	logger.Log(0, r.Header.Get("user"), "updated ext client", newExtClient.ClientID)
+	logger.Log(0, r.Header.Get("user"), "updated ext client", update.ClientID)
 	if changedEnabled { // need to send a peer update to the ingress node as enablement of one of it's clients has changed
 		if ingressNode, err := logic.GetNodeByID(newclient.IngressGatewayID); err == nil {
 			if err = mq.PublishPeerUpdate(); err != nil {
@@ -509,7 +504,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(newclient)
 	if changedID {
 		go func() {
-			if err := mq.PublishExtClientDNSUpdate(currentClient, newExtClient, networkName); err != nil {
+			if err := mq.PublishExtClientDNSUpdate(currentClient, *newclient, networkName); err != nil {
 				logger.Log(1, "error pubishing dns update for extcient update", err.Error())
 			}
 		}()
@@ -648,3 +643,35 @@ func doesUserOwnClient(username, clientID, network string) (bool, bool) {
 
 	return false, logic.StringSliceContains(netUser.Clients, clientID)
 }
+
+// validateExtClient	Validates the extclient object
+func validateExtClient(extclient *models.ExtClient, customExtClient *models.CustomExtClient) error {
+	//validate clientid
+	if customExtClient.ClientID != "" && !validName(customExtClient.ClientID) {
+		return errInvalidExtClientID
+	}
+	extclient.ClientID = customExtClient.ClientID
+	if len(customExtClient.PublicKey) > 0 {
+		if _, err := wgtypes.ParseKey(customExtClient.PublicKey); err != nil {
+			return errInvalidExtClientPubKey
+		}
+		extclient.PublicKey = customExtClient.PublicKey
+	}
+	//validate extra ips
+	if len(customExtClient.ExtraAllowedIPs) > 0 {
+		for _, ip := range customExtClient.ExtraAllowedIPs {
+			if _, _, err := net.ParseCIDR(ip); err != nil {
+				return errInvalidExtClientExtraIP
+			}
+		}
+		extclient.ExtraAllowedIPs = customExtClient.ExtraAllowedIPs
+	}
+	//validate DNS
+	if customExtClient.DNS != "" {
+		if ip := net.ParseIP(customExtClient.DNS); ip == nil {
+			return errInvalidExtClientDNS
+		}
+		extclient.DNS = customExtClient.DNS
+	}
+	return nil
+}

+ 4 - 7
controllers/node.go

@@ -520,13 +520,10 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	nodeid := params["nodeid"]
 	netid := params["network"]
-	type failoverData struct {
-		Failover bool `json:"failover"`
-	}
-	var failoverReqBody failoverData
-	json.NewDecoder(r.Body).Decode(&failoverReqBody)
+	var request models.IngressRequest
+	json.NewDecoder(r.Body).Decode(&request)
 
-	node, err := logic.CreateIngressGateway(netid, nodeid, failoverReqBody.Failover)
+	node, err := logic.CreateIngressGateway(netid, nodeid, request)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to create ingress gateway on node [%s] on network [%s]: %v",
@@ -535,7 +532,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if servercfg.Is_EE && failoverReqBody.Failover {
+	if servercfg.Is_EE && request.Failover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 		}

+ 15 - 3
controllers/regex.go

@@ -6,11 +6,23 @@ import (
 )
 
 var (
-	errInvalidExtClientPubKey = errors.New("incorrect ext client public key")
-	errInvalidExtClientID     = errors.New("ext client ID must be alphanumderic and/or dashes")
+	errInvalidExtClientPubKey  = errors.New("incorrect ext client public key")
+	errInvalidExtClientID      = errors.New("ext client ID must be alphanumderic and/or dashes and less that 15 chars")
+	errInvalidExtClientExtraIP = errors.New("ext client extra ip must be a valid cidr")
+	errInvalidExtClientDNS     = errors.New("ext client dns must be a valid ip address")
 )
 
 // allow only dashes and alphaneumeric for ext client and node names
 func validName(name string) bool {
-	return regexp.MustCompile("^[a-zA-Z0-9-]+$").MatchString(name)
+	reg, err := regexp.Compile("^[a-zA-Z0-9-]+$")
+	if err != nil {
+		return false
+	}
+	if !reg.MatchString(name) {
+		return false
+	}
+	if len(name) > 15 {
+		return false
+	}
+	return true
 }

+ 51 - 0
controllers/regex_test.go

@@ -0,0 +1,51 @@
+package controller
+
+import "testing"
+
+// TestValidName tests the validName function
+func TestValidName(t *testing.T) {
+	type args struct {
+		Name string
+	}
+	tests := []struct {
+		Name string
+		Args args
+		Want bool
+	}{
+		{
+			Name: "validName",
+			Args: args{
+				Name: "TestvalidName",
+			},
+			Want: true,
+		},
+		{
+			Name: "invalidName",
+			Args: args{
+				Name: "Test*Name",
+			},
+			Want: false,
+		},
+		{
+			Name: "nametoolong",
+			Args: args{
+				Name: "TestvalidNameTestvalidName",
+			},
+			Want: false,
+		},
+		{
+			Name: "maxlength",
+			Args: args{
+				Name: "123456789012345",
+			},
+			Want: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.Name, func(t *testing.T) {
+			if got := validName(tt.Args.Name); got != tt.Want {
+				t.Errorf("validName() = %v, want %v", got, tt.Want)
+			}
+		})
+	}
+}

+ 1 - 2
functions/helpers_test.go

@@ -18,8 +18,7 @@ var (
 		NetID: "not-a-network",
 	}
 	testExternalClient = &models.ExtClient{
-		ClientID:    "testExtClient",
-		Description: "ext client for testing",
+		ClientID: "testExtClient",
 	}
 )
 

+ 22 - 25
logic/extpeers.go

@@ -174,6 +174,11 @@ func CreateExtClient(extclient *models.ExtClient) error {
 	}
 
 	extclient.LastModified = time.Now().Unix()
+	return SaveExtClient(extclient)
+}
+
+// SaveExtClient - saves an ext client to database
+func SaveExtClient(extclient *models.ExtClient) error {
 	key, err := GetRecordKey(extclient.ClientID, extclient.Network)
 	if err != nil {
 		return err
@@ -188,35 +193,27 @@ func CreateExtClient(extclient *models.ExtClient) error {
 	return SetNetworkNodesLastModified(extclient.Network)
 }
 
-// UpdateExtClient - only supports name changes right now
-func UpdateExtClient(newclientid string, network string, enabled bool, client *models.ExtClient, newACLs map[string]struct{}) (*models.ExtClient, error) {
-	err := DeleteExtClient(network, client.ClientID)
+// UpdateExtClient - updates an ext client with new values
+func UpdateExtClient(old *models.ExtClient, update *models.CustomExtClient) (*models.ExtClient, error) {
+	new := old
+	err := DeleteExtClient(old.Network, old.ClientID)
 	if err != nil {
-		return client, err
+		return new, err
 	}
-	if newclientid != client.ClientID { // name change only
-		client.ClientID = newclientid
-		client.LastModified = time.Now().Unix()
-		data, err := json.Marshal(&client)
-		if err != nil {
-			return nil, err
-		}
-		key, err := GetRecordKey(client.ClientID, client.Network)
-		if err != nil {
-			return nil, err
-		}
-		if err = database.Insert(key, string(data), database.EXT_CLIENT_TABLE_NAME); err != nil {
-			return client, err
-		}
-		return client, nil
+	new.ClientID = update.ClientID
+	if update.PublicKey != "" && old.PublicKey != update.PublicKey {
+		new.PublicKey = update.PublicKey
+	}
+	if update.DNS != "" && update.DNS != old.DNS {
+		new.DNS = update.DNS
+	}
+	if update.Enabled != old.Enabled {
+		new.Enabled = update.Enabled
 	}
-	client.ClientID = newclientid
-	client.Enabled = enabled
-	SetClientACLs(client, newACLs)
-	if err = CreateExtClient(client); err != nil {
-		return client, err
+	if update.ExtraAllowedIPs != nil && StringDifference(old.ExtraAllowedIPs, update.ExtraAllowedIPs) != nil {
+		new.ExtraAllowedIPs = update.ExtraAllowedIPs
 	}
-	return client, err
+	return new, CreateExtClient(new)
 }
 
 // GetExtClientsByID - gets the clients of attached gateway

+ 3 - 2
logic/gateway.go

@@ -96,7 +96,7 @@ func DeleteEgressGateway(network, nodeid string) (models.Node, error) {
 }
 
 // CreateIngressGateway - creates an ingress gateway
-func CreateIngressGateway(netid string, nodeid string, failover bool) (models.Node, error) {
+func CreateIngressGateway(netid string, nodeid string, ingress models.IngressRequest) (models.Node, error) {
 
 	node, err := GetNodeByID(nodeid)
 	if err != nil {
@@ -120,8 +120,9 @@ func CreateIngressGateway(netid string, nodeid string, failover bool) (models.No
 	node.IsIngressGateway = true
 	node.IngressGatewayRange = network.AddressRange
 	node.IngressGatewayRange6 = network.AddressRange6
+	node.IngressDNS = ingress.ExtclientDNS
 	node.SetLastModified()
-	if failover && servercfg.Is_EE {
+	if ingress.Failover && servercfg.Is_EE {
 		node.Failover = true
 	}
 	data, err := json.Marshal(&node)

+ 3 - 0
models/api_node.go

@@ -32,6 +32,7 @@ type ApiNode struct {
 	RelayAddrs              []string `json:"relayaddrs"`
 	FailoverNode            string   `json:"failovernode"`
 	DNSOn                   bool     `json:"dnson"`
+	IngressDns              string   `json:"ingressdns"`
 	Server                  string   `json:"server"`
 	InternetGateway         string   `json:"internetgateway"`
 	Connected               bool     `json:"connected"`
@@ -61,6 +62,7 @@ func (a *ApiNode) ConvertToServerNode(currentNode *Node) *Node {
 	convertedNode.IngressGatewayRange = currentNode.IngressGatewayRange
 	convertedNode.IngressGatewayRange6 = currentNode.IngressGatewayRange6
 	convertedNode.DNSOn = a.DNSOn
+	convertedNode.IngressDNS = a.IngressDns
 	convertedNode.EgressGatewayRequest = currentNode.EgressGatewayRequest
 	convertedNode.EgressGatewayNatEnabled = currentNode.EgressGatewayNatEnabled
 	convertedNode.PersistentKeepalive = time.Second * time.Duration(a.PersistentKeepalive)
@@ -148,6 +150,7 @@ func (nm *Node) ConvertToAPINode() *ApiNode {
 		apiNode.FailoverNode = ""
 	}
 	apiNode.DNSOn = nm.DNSOn
+	apiNode.IngressDns = nm.IngressDNS
 	apiNode.Server = nm.Server
 	apiNode.InternetGateway = nm.InternetGateway.String()
 	if isEmptyAddr(apiNode.InternetGateway) {

+ 11 - 1
models/extclient.go

@@ -3,12 +3,13 @@ package models
 // ExtClient - struct for external clients
 type ExtClient struct {
 	ClientID               string              `json:"clientid" bson:"clientid"`
-	Description            string              `json:"description" bson:"description"`
 	PrivateKey             string              `json:"privatekey" bson:"privatekey"`
 	PublicKey              string              `json:"publickey" bson:"publickey"`
 	Network                string              `json:"network" bson:"network"`
+	DNS                    string              `json:"dns" bson:"dns"`
 	Address                string              `json:"address" bson:"address"`
 	Address6               string              `json:"address6" bson:"address6"`
+	ExtraAllowedIPs        []string            `json:"extraallowedips" bson:"extraallowedips"`
 	IngressGatewayID       string              `json:"ingressgatewayid" bson:"ingressgatewayid"`
 	IngressGatewayEndpoint string              `json:"ingressgatewayendpoint" bson:"ingressgatewayendpoint"`
 	LastModified           int64               `json:"lastmodified" bson:"lastmodified"`
@@ -16,3 +17,12 @@ type ExtClient struct {
 	OwnerID                string              `json:"ownerid" bson:"ownerid"`
 	ACLs                   map[string]struct{} `json:"acls,omitempty" bson:"acls,omitempty"`
 }
+
+// CustomExtClient - struct for CustomExtClient params
+type CustomExtClient struct {
+	ClientID        string   `json:"clientid,omitempty"`
+	PublicKey       string   `json:"publickey,omitempty"`
+	DNS             string   `json:"dns,omitempty"`
+	ExtraAllowedIPs []string `json:"extraallowedips,omitempty"`
+	Enabled         bool     `json:"enabled,omitempty"`
+}

+ 0 - 1
models/network.go

@@ -23,7 +23,6 @@ type Network struct {
 	IsIPv4              string                `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
 	IsIPv6              string                `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
 	DefaultUDPHolePunch string                `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
-	DefaultExtClientDNS string                `json:"defaultextclientdns" bson:"defaultextclientdns"`
 	DefaultMTU          int32                 `json:"defaultmtu" bson:"defaultmtu"`
 	DefaultACL          string                `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
 	ProSettings         *promodels.ProNetwork `json:"prosettings,omitempty" bson:"prosettings,omitempty" yaml:"prosettings,omitempty"`

+ 1 - 0
models/node.go

@@ -69,6 +69,7 @@ type CommonNode struct {
 	IsEgressGateway     bool          `json:"isegressgateway" yaml:"isegressgateway"`
 	EgressGatewayRanges []string      `json:"egressgatewayranges" bson:"egressgatewayranges" yaml:"egressgatewayranges"`
 	IsIngressGateway    bool          `json:"isingressgateway" yaml:"isingressgateway"`
+	IngressDNS          string        `json:"ingressdns" yaml:"ingressdns"`
 	DNSOn               bool          `json:"dnson" yaml:"dnson"`
 	PersistentKeepalive time.Duration `json:"persistentkeepalive" yaml:"persistentkeepalive"`
 }

+ 6 - 6
models/structs.go

@@ -14,12 +14,6 @@ const (
 	PLACEHOLDER_TOKEN_TEXT = "ACCESS_TOKEN"
 )
 
-// CustomExtClient - struct for CustomExtClient params
-type CustomExtClient struct {
-	ClientID  string `json:"clientid"`
-	PublicKey string `json:"publickey,omitempty"`
-}
-
 // AuthParams - struct for auth params
 type AuthParams struct {
 	MacAddress string `json:"macaddress"`
@@ -170,6 +164,12 @@ type HostRelayRequest struct {
 	RelayedHosts []string `json:"relayed_hosts"`
 }
 
+// IngressRequest - ingress request struct
+type IngressRequest struct {
+	ExtclientDNS string `json:"extclientdns"`
+	Failover     bool   `json:"failover"`
+}
+
 // ServerUpdateData - contains data to configure server
 // and if it should set peers
 type ServerUpdateData struct {